From 027ae3f3f6e1f7d023a30c70e9eae95ce06d0a4e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Mar 2023 15:32:47 +0300 Subject: [PATCH 01/34] SubGHz: Add manually extend, add support for new protocols Schellenberg / Stilmatic and Came Space keeloq protocols support --- CHANGELOG.md | 3 + ReadMe.md | 3 + .../main/subghz/helpers/subghz_custom_event.h | 18 +- .../subghz/scenes/subghz_scene_set_type.c | 379 ++++++++++++++++-- documentation/SubGHzSettings.md | 3 +- lib/subghz/protocols/keeloq.c | 14 +- lib/subghz/subghz_setting.c | 1 - 7 files changed, 388 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60d0f905d..5cd2d5f13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ### New changes +* SubGHz: New protocols support: CAME Space, Stilmatic / Schellenberg +* SubGHz: Add Manually - new protocols -> Beninca, Sommer(FSK), IronLogic, DTM Neo, Gibidi, Elmes Electronic (Elmes Poland), CAME Space +* SubGHz: Removed 330MHz from default freq hopper to make it faster, to change freq hopper settings and remove/add your freqs see -> [Instruction](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md) * Plugins: Updated **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (fixed issue #379) * Plugins: Update for SubGHz Bruteforcer, added - Holtek HT12X 12bit AM 433.920MHz/315MHz/868MHz/915MHz (TE: 433us) * OFW: Fbt: fix broken resource deployment diff --git a/ReadMe.md b/ReadMe.md index 24659d6b6..ed31961fa 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -46,6 +46,7 @@ Our Discord Community: * Customizable Flipper name - BadUSB -> Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout) - Sub-GHz -> External CC1101 module support - [(by quen0n)](https://github.com/DarkFlippers/unleashed-firmware/pull/307) +- Sub-GHz -> `Add manually` menu extended with new protocols - Sub-GHz -> New frequency analyzer - [(by ClusterM)](https://github.com/DarkFlippers/unleashed-firmware/pull/43) - Sub-GHz -> Save last used frequency [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77) - Sub-GHz -> Press OK in frequency analyzer to use detected frequency in Read modes [(by derskythe)](https://github.com/DarkFlippers/unleashed-firmware/pull/77) @@ -81,6 +82,8 @@ Encoders/sending made by Eng1n33r & @xMasterX: - Keeloq: Mutancode - Keeloq: Normstahl - Keeloq: Beninca +- Keeloq: Stilmatic / Schellenberg +- Keeloq: CAME Space - CAME Atomo - Nice Flor S - FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)] diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 4475045ee..69f946a9a 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -11,7 +11,14 @@ typedef enum { SubmenuIndexBFTClone, SubmenuIndexBFTMitto, SubmenuIndexSomfyTelis, - SubmenuIndexPricenton, + SubmenuIndexBeninca433, + SubmenuIndexBeninca868, + SubmenuIndexIronLogic, + SubmenuIndexElmesElectronic, + SubmenuIndexSommer_FM_434, + SubmenuIndexSommer_FM_868, + SubmenuIndexDTMNeo433, + SubmenuIndexGibidi433, SubmenuIndexNiceFlo12bit, SubmenuIndexNiceFlo24bit, SubmenuIndexNiceFlorS_433_92, @@ -19,14 +26,19 @@ typedef enum { SubmenuIndexNiceSmilo_433_92, SubmenuIndexCAME12bit, SubmenuIndexCAME24bit, - SubmenuIndexBETT_433, + SubmenuIndexCAME12bit868, + SubmenuIndexCAME24bit868, SubmenuIndexCAMETwee, + SubmenuIndexCAMESpace, + SubmenuIndexPricenton, + SubmenuIndexPricenton315, + SubmenuIndexBETT_433, + SubmenuIndexLinear_300_00, SubmenuIndexNeroSketch, SubmenuIndexNeroRadio, SubmenuIndexGateTX, SubmenuIndexDoorHan_315_00, SubmenuIndexDoorHan_433_92, - SubmenuIndexLinear_300_00, SubmenuIndexLiftMaster_315_00, SubmenuIndexLiftMaster_390_00, SubmenuIndexLiftMaster_433_00, diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index bc76ee146..7a16fc8d9 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -100,25 +100,67 @@ void subghz_scene_set_type_on_enter(void* context) { subghz); submenu_add_item( subghz->submenu, - "Princeton 433MHz", - SubmenuIndexPricenton, + "KL: DoorHan 315MHz", + SubmenuIndexDoorHan_315_00, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Nice Flo 12bit 433MHz", - SubmenuIndexNiceFlo12bit, + "KL: DoorHan 433MHz", + SubmenuIndexDoorHan_433_92, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Nice Flo 24bit 433MHz", - SubmenuIndexNiceFlo24bit, + "KL: Beninca 433MHz", + SubmenuIndexBeninca433, subghz_scene_set_type_submenu_callback, subghz); submenu_add_item( subghz->submenu, - "Nice Smilo 433MHz", + "KL: Beninca 868MHz", + SubmenuIndexBeninca868, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "KL: Sommer 434MHz", + SubmenuIndexSommer_FM_434, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "KL: Sommer 868MHz", + SubmenuIndexSommer_FM_868, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "KL: IronLogic 433MHz", + SubmenuIndexIronLogic, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "KL: DTM Neo 433MHz", + SubmenuIndexDTMNeo433, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "KL: Gibidi 433MHz", + SubmenuIndexGibidi433, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "KL: Elmes (PL) 433MHz", + SubmenuIndexElmesElectronic, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "KL: Nice Smilo 433MHz", SubmenuIndexNiceSmilo_433_92, subghz_scene_set_type_submenu_callback, subghz); @@ -134,6 +176,18 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexNiceOne_433_92, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "Nice Flo 12bit 433MHz", + SubmenuIndexNiceFlo12bit, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Nice Flo 24bit 433MHz", + SubmenuIndexNiceFlo24bit, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "CAME 12bit 433MHz", @@ -146,6 +200,42 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexCAME24bit, subghz_scene_set_type_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "CAME 12bit 868MHz", + SubmenuIndexCAME12bit868, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "CAME 24bit 868MHz", + SubmenuIndexCAME24bit868, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "CAME TWEE 433MHz", + SubmenuIndexCAMETwee, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "CAME Space 433MHz", + SubmenuIndexCAMESpace, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Princeton 315MHz", + SubmenuIndexPricenton315, + subghz_scene_set_type_submenu_callback, + subghz); + submenu_add_item( + subghz->submenu, + "Princeton 433MHz", + SubmenuIndexPricenton, + subghz_scene_set_type_submenu_callback, + subghz); submenu_add_item( subghz->submenu, "BETT 433MHz", @@ -158,12 +248,6 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexLinear_300_00, subghz_scene_set_type_submenu_callback, subghz); - submenu_add_item( - subghz->submenu, - "CAME TWEE 433MHz", - SubmenuIndexCAMETwee, - subghz_scene_set_type_submenu_callback, - subghz); // submenu_add_item( // subghz->submenu, "Nero Sketch", SubmenuIndexNeroSketch, subghz_scene_set_type_submenu_callback, subghz); // submenu_add_item( @@ -174,18 +258,6 @@ void subghz_scene_set_type_on_enter(void* context) { SubmenuIndexGateTX, subghz_scene_set_type_submenu_callback, subghz); - submenu_add_item( - subghz->submenu, - "DoorHan 315MHz", - SubmenuIndexDoorHan_315_00, - subghz_scene_set_type_submenu_callback, - subghz); - submenu_add_item( - subghz->submenu, - "DoorHan 433MHz", - SubmenuIndexDoorHan_433_92, - subghz_scene_set_type_submenu_callback, - subghz); submenu_add_item( subghz->submenu, "Security+1.0 315MHz", @@ -261,6 +333,15 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { generated_protocol = true; } break; + case SubmenuIndexPricenton315: + key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, SUBGHZ_PROTOCOL_PRINCETON_NAME, key, 24, 315000000, "AM650")) { + uint32_t te = 400; + flipper_format_update_uint32(subghz->txrx->fff_data, "TE", (uint32_t*)&te, 1); + generated_protocol = true; + } + break; case SubmenuIndexNiceFlo12bit: key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 if(subghz_scene_set_type_submenu_gen_data_protocol( @@ -289,6 +370,20 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { generated_protocol = true; } break; + case SubmenuIndexCAME12bit868: + key = (key & 0x0000FFF0) | 0x1; //btn 0x1, 0x2, 0x4 + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 12, 868350000, "AM650")) { + generated_protocol = true; + } + break; + case SubmenuIndexCAME24bit868: + key = (key & 0x00FFFFF0) | 0x4; //btn 0x1, 0x2, 0x4, 0x8 + if(subghz_scene_set_type_submenu_gen_data_protocol( + subghz, SUBGHZ_PROTOCOL_CAME_NAME, key, 24, 868350000, "AM650")) { + generated_protocol = true; + } + break; case SubmenuIndexLinear_300_00: key = (key & 0x3FF); if(subghz_scene_set_type_submenu_gen_data_protocol( @@ -325,6 +420,236 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { generated_protocol = true; } break; + case SubmenuIndexBeninca433: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + (key & 0x000FFF00) | 0x00800080, + 0x1, + 0x0005, + "Beninca", + subghz->txrx->preset); + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "Beninca"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexBeninca868: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 868350000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + (key & 0x000FFF00) | 0x00800080, + 0x1, + 0x0005, + "Beninca", + subghz->txrx->preset); + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "Beninca"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexElmesElectronic: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + (key & 0x00FFFFFF) | 0x02000000, + 0x2, + 0x0003, + "Elmes_Poland", + subghz->txrx->preset); + flipper_format_write_string_cstr( + subghz->txrx->fff_data, "Manufacture", "Elmes_Poland"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexGibidi433: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x00FFFFFF, + 0x2, + 0x0003, + "Gibidi", + subghz->txrx->preset); + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "Gibidi"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexIronLogic: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x0FFFFFFF, + 0x2, + 0x0003, + "IronLogic", + subghz->txrx->preset); + flipper_format_write_string_cstr( + subghz->txrx->fff_data, "Manufacture", "IronLogic"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexSommer_FM_434: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "FM476", 434420000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x0FFFFFFF, + 0x4, + 0x0003, + "Sommer(fsk476)", + subghz->txrx->preset); + flipper_format_write_string_cstr( + subghz->txrx->fff_data, "Manufacture", "Sommer(fsk476)"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexSommer_FM_868: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "FM476", 868800000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x0FFFFFFF, + 0x4, + 0x0003, + "Sommer(fsk476)", + subghz->txrx->preset); + flipper_format_write_string_cstr( + subghz->txrx->fff_data, "Manufacture", "Sommer(fsk476)"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexDTMNeo433: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x000FFFFF, + 0x2, + 0x0003, + "DTM_Neo", + subghz->txrx->preset); + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "DTM_Neo"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; + case SubmenuIndexCAMESpace: + subghz->txrx->transmitter = subghz_transmitter_alloc_init( + subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); + subghz_preset_init(subghz, "AM650", 433920000, NULL, 0); + if(subghz->txrx->transmitter) { + subghz_protocol_keeloq_create_data( + subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), + subghz->txrx->fff_data, + key & 0x00FFFFFF, + 0x2, + 0x0003, + "Came_Space", + subghz->txrx->preset); + flipper_format_write_string_cstr( + subghz->txrx->fff_data, "Manufacture", "Came_Space"); + generated_protocol = true; + } else { + generated_protocol = false; + } + subghz_transmitter_free(subghz->txrx->transmitter); + if(!generated_protocol) { + furi_string_set( + subghz->error_str, "Function requires\nan SD card with\nfresh databases."); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError); + } + break; case SubmenuIndexBFTMitto: subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); @@ -397,6 +722,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { 0x0003, "DoorHan", subghz->txrx->preset); + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "DoorHan"); generated_protocol = true; } else { generated_protocol = false; @@ -421,6 +747,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { 0x0003, "DoorHan", subghz->txrx->preset); + flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "DoorHan"); generated_protocol = true; } else { generated_protocol = false; @@ -493,6 +820,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { 0x0003, "NICE_Smilo", subghz->txrx->preset); + flipper_format_write_string_cstr( + subghz->txrx->fff_data, "Manufacture", "NICE_Smilo"); generated_protocol = true; } else { generated_protocol = false; diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md index e30a7c244..4b85f25e3 100644 --- a/documentation/SubGHzSettings.md +++ b/documentation/SubGHzSettings.md @@ -2,7 +2,7 @@ #### CC1101 Frequency range specs: 300-348 MHz, 386-464 MHz, and 778-928 MHz (+ 350MHz and 467MHz was added to default range) -Edit user settings file located on your microSD card - `subghz/assets/setting_user` +Edit user settings file located on your microSD card - `subghz/assets/setting_user` (remove .example from name to use config) in this file you will find we already have extra frequencies added if you need your custom one, make sure it doesn't listed here @@ -90,7 +90,6 @@ Your frequencies will be added after default ones ### Default hopper list ``` 315000000, - 330000000, 390000000, 433420000, 433920000, diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 4bd978c8f..d08352e78 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -198,10 +198,12 @@ static bool subghz_protocol_keeloq_gen_data( (instance->generic.serial & 0x3FF) << 16 | //ToDo in some protocols the discriminator is 0 instance->generic.cnt; - // DTM Neo uses 12bit -> simple learning -- FAAC_RC,XT , Mutanco_Mutancode -> 12bit normal learning + // DTM Neo, Came_Space uses 12bit -> simple learning -- FAAC_RC,XT , Mutanco_Mutancode, Stilmatic(Schellenberg) -> 12bit normal learning if((strcmp(instance->manufacture_name, "DTM_Neo") == 0) || (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || - (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0)) { + (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0) || + (strcmp(instance->manufacture_name, "Stilmatic") == 0) || + (strcmp(instance->manufacture_name, "Came_Space") == 0)) { decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; } @@ -567,6 +569,10 @@ SubGhzProtocolStatus instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | seed_data[3]; + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } // Read manufacturer from file if(flipper_format_read_string( flipper_format, "Manufacture", instance->manufacture_from_file)) { @@ -1247,6 +1253,10 @@ SubGhzProtocolStatus instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | seed_data[3]; + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } // Read manufacturer from file if(flipper_format_read_string( flipper_format, "Manufacture", instance->manufacture_from_file)) { diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 05b6a74ad..ca094e556 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -78,7 +78,6 @@ static const uint32_t subghz_frequency_list[] = { static const uint32_t subghz_hopper_frequency_list[] = { 315000000, - 330000000, 390000000, 433420000, 433920000, From eb57f4c7e48c82dac01256d243c5dd67d48db094 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Mar 2023 15:53:06 +0300 Subject: [PATCH 02/34] Fix ironlogic add manually --- applications/main/subghz/scenes/subghz_scene_set_type.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 7a16fc8d9..26065b107 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -529,9 +529,9 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { subghz_protocol_keeloq_create_data( subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), subghz->txrx->fff_data, - key & 0x0FFFFFFF, - 0x2, - 0x0003, + key & 0x00FFFFF0, + 0x4, + 0x0005, "IronLogic", subghz->txrx->preset); flipper_format_write_string_cstr( @@ -609,7 +609,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { subghz->txrx->fff_data, key & 0x000FFFFF, 0x2, - 0x0003, + 0x0005, "DTM_Neo", subghz->txrx->preset); flipper_format_write_string_cstr(subghz->txrx->fff_data, "Manufacture", "DTM_Neo"); From 5ddb9bd44458097f3c32d04dd4f1e4a6a0d4f387 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Mar 2023 18:07:54 +0300 Subject: [PATCH 03/34] Correct hopping freqs, now it actually works good --- documentation/SubGHzSettings.md | 2 +- lib/subghz/subghz_setting.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md index 4b85f25e3..d01cd6250 100644 --- a/documentation/SubGHzSettings.md +++ b/documentation/SubGHzSettings.md @@ -89,8 +89,8 @@ Your frequencies will be added after default ones ### Default hopper list ``` + 310000000, 315000000, - 390000000, 433420000, 433920000, 868350000, diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index ca094e556..1216a9571 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -77,8 +77,8 @@ static const uint32_t subghz_frequency_list[] = { }; static const uint32_t subghz_hopper_frequency_list[] = { + 310000000, 315000000, - 390000000, 433420000, 433920000, 868350000, From c560394358f0e15bc78f745c23403fe7a3c3bc5d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 13 Mar 2023 18:43:50 +0300 Subject: [PATCH 04/34] External module power bug fixes --- .../main/subghz/scenes/subghz_scene_ext_module_settings.c | 2 +- applications/main/subghz/scenes/subghz_scene_start.c | 1 + applications/main/subghz/subghz.c | 3 +++ firmware/targets/f7/furi_hal/furi_hal_subghz.c | 4 ++-- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c index c58661332..163099e69 100644 --- a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c @@ -181,7 +181,7 @@ bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent // Check if module is present, if no -> show error if(!furi_hal_subghz_check_radio()) { value_index_exm = 0; - furi_hal_subghz_set_radio_type(value_index_exm); + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); furi_string_set(subghz->error_str, "Please connect\nexternal radio"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); } diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index fd31353e0..f23e2d0cb 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -84,6 +84,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { furi_hal_subghz_enable_ext_power(); if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); furi_string_set(subghz->error_str, "Please connect\nexternal radio"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); return true; diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index e2b484120..f7adc4021 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -73,6 +73,9 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->in_decoder_scene = false; subghz->in_decoder_scene_skip = false; + // Call enable power for external module + furi_hal_subghz_enable_ext_power(); + // View Dispatcher subghz->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(subghz->view_dispatcher); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 181445372..989f97698 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -93,10 +93,10 @@ bool furi_hal_subghz_enable_ext_power(void) { } if(furi_hal_subghz.radio_type != SubGhzRadioInternal) { uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 2) { + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { furi_hal_power_enable_otg(); //CC1101 power-up time - furi_delay_ms(5); + furi_delay_ms(10); } } return furi_hal_power_is_otg_enabled(); From d5ea5168a063b81fc3c7adbd03bb7df13af31c8c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 13 Mar 2023 19:25:17 +0300 Subject: [PATCH 05/34] Prepare apps for next OFW changes PR 2420 will change PLUGIN type meaning --- applications/debug/uart_echo/application.fam | 2 +- applications/plugins/dap_link/application.fam | 2 +- applications/plugins/hid_app/application.fam | 4 ++-- applications/plugins/music_player/application.fam | 2 +- applications/plugins/pocsag_pager/application.fam | 2 +- applications/plugins/signal_generator/application.fam | 2 +- applications/plugins/snake_game/application.fam | 2 +- applications/plugins/swd_probe/application.fam | 2 +- applications/plugins/weather_station/application.fam | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam index 9bc61edc8..4ad14e6c3 100644 --- a/applications/debug/uart_echo/application.fam +++ b/applications/debug/uart_echo/application.fam @@ -1,7 +1,7 @@ App( appid="UART_Echo", name="UART Echo", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", cdefines=["APP_UART_ECHO"], requires=["gui"], diff --git a/applications/plugins/dap_link/application.fam b/applications/plugins/dap_link/application.fam index 711e4833d..017143803 100644 --- a/applications/plugins/dap_link/application.fam +++ b/applications/plugins/dap_link/application.fam @@ -1,7 +1,7 @@ App( appid="dap_link", name="DAP Link", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="dap_link_app", requires=[ "gui", diff --git a/applications/plugins/hid_app/application.fam b/applications/plugins/hid_app/application.fam index fa67fed6b..e96e956d8 100644 --- a/applications/plugins/hid_app/application.fam +++ b/applications/plugins/hid_app/application.fam @@ -1,7 +1,7 @@ App( appid="hid_usb", name="USB Keyboard & Mouse", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="hid_usb_app", stack_size=1 * 1024, fap_category="Misc", @@ -14,7 +14,7 @@ App( App( appid="hid_ble", name="Bluetooth Remote", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="hid_ble_app", stack_size=1 * 1024, fap_category="Misc", diff --git a/applications/plugins/music_player/application.fam b/applications/plugins/music_player/application.fam index b8bd75198..9fdd96166 100644 --- a/applications/plugins/music_player/application.fam +++ b/applications/plugins/music_player/application.fam @@ -1,7 +1,7 @@ App( appid="Music_Player", name="Music Player", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="music_player_app", cdefines=["APP_MUSIC_PLAYER"], requires=[ diff --git a/applications/plugins/pocsag_pager/application.fam b/applications/plugins/pocsag_pager/application.fam index 86f8d528b..b0d1bedd1 100644 --- a/applications/plugins/pocsag_pager/application.fam +++ b/applications/plugins/pocsag_pager/application.fam @@ -1,7 +1,7 @@ App( appid="pocsag_pager", name="POCSAG Pager", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="pocsag_pager_app", cdefines=["APP_POCSAG_PAGER"], requires=["gui"], diff --git a/applications/plugins/signal_generator/application.fam b/applications/plugins/signal_generator/application.fam index 7536a4c11..7d9d181ec 100644 --- a/applications/plugins/signal_generator/application.fam +++ b/applications/plugins/signal_generator/application.fam @@ -1,7 +1,7 @@ App( appid="Signal_Generator", name="Signal Generator", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="signal_gen_app", cdefines=["APP_SIGNAL_GEN"], requires=["gui"], diff --git a/applications/plugins/snake_game/application.fam b/applications/plugins/snake_game/application.fam index abf4f72c5..faa5ce145 100644 --- a/applications/plugins/snake_game/application.fam +++ b/applications/plugins/snake_game/application.fam @@ -1,7 +1,7 @@ App( appid="Snake", name="Snake Game", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="snake_game_app", cdefines=["APP_SNAKE_GAME"], requires=["gui"], diff --git a/applications/plugins/swd_probe/application.fam b/applications/plugins/swd_probe/application.fam index c24813ed9..c1aec00ea 100644 --- a/applications/plugins/swd_probe/application.fam +++ b/applications/plugins/swd_probe/application.fam @@ -1,7 +1,7 @@ App( appid="swd_probe", name="SWD Probe", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="swd_probe_app_main", cdefines=["APP_SWD_PROBE"], requires=["notification", "gui", "storage", "dialogs", "cli"], diff --git a/applications/plugins/weather_station/application.fam b/applications/plugins/weather_station/application.fam index 935f92573..28a425bdc 100644 --- a/applications/plugins/weather_station/application.fam +++ b/applications/plugins/weather_station/application.fam @@ -1,7 +1,7 @@ App( appid="weather_station", name="Weather Station", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, targets=["f7"], entry_point="weather_station_app", cdefines=["APP_WEATHER_STATION"], From 84b22426366e9c006eb82632793832998607415b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 13 Mar 2023 23:19:33 +0300 Subject: [PATCH 06/34] Check for external module when launching from favourites --- CHANGELOG.md | 1 + applications/main/subghz/subghz.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cd2d5f13..a2700b486 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### New changes * SubGHz: New protocols support: CAME Space, Stilmatic / Schellenberg * SubGHz: Add Manually - new protocols -> Beninca, Sommer(FSK), IronLogic, DTM Neo, Gibidi, Elmes Electronic (Elmes Poland), CAME Space +* SubGHz: Fixed external module power issues, added more checks, fixed issues when launching subghz favourites * SubGHz: Removed 330MHz from default freq hopper to make it faster, to change freq hopper settings and remove/add your freqs see -> [Instruction](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md) * Plugins: Updated **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (fixed issue #379) * Plugins: Update for SubGHz Bruteforcer, added - Holtek HT12X 12bit AM 433.920MHz/315MHz/868MHz/915MHz (TE: 433us) diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index f7adc4021..3ca66374b 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -453,6 +453,11 @@ int32_t subghz_app(void* p) { // Check argument and run corresponding scene if(p && strlen(p)) { uint32_t rpc_ctx = 0; + + if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + } + if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { subghz->rpc_ctx = (void*)rpc_ctx; rpc_system_app_set_callback(subghz->rpc_ctx, subghz_rpc_command_callback, subghz); From 53435579b3c357a1f915dd5e3f0822da65ba59cd Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 14 Mar 2023 18:29:28 +0400 Subject: [PATCH 07/34] [FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes --- .github/CODEOWNERS | 4 +- .gitmodules | 4 +- .pvsoptions | 2 +- SConstruct | 27 +- applications/examples/application.fam | 1 + .../examples/example_plugins/application.fam | 31 ++ .../example_plugins/example_plugins.c | 70 +++ .../example_plugins/example_plugins_multi.c | 43 ++ .../examples/example_plugins/plugin1.c | 32 ++ .../examples/example_plugins/plugin2.c | 32 ++ .../example_plugins/plugin_interface.h | 12 + .../example_plugins_advanced/app_api.c | 25 ++ .../example_plugins_advanced/app_api.h | 25 ++ .../app_api_interface.h | 9 + .../app_api_table.cpp | 27 ++ .../app_api_table_i.h | 13 + .../example_plugins_advanced/application.fam | 24 + .../example_advanced_plugins.c | 48 ++ .../example_plugins_advanced/plugin1.c | 40 ++ .../example_plugins_advanced/plugin2.c | 40 ++ .../plugin_interface.h | 12 + applications/external/application.fam | 6 + .../clock/application.fam | 2 +- .../{plugins => external}/clock/clock.png | Bin .../{plugins => external}/clock/clock_app.c | 0 .../{plugins => external}/dap_link/README.md | 0 .../dap_link/application.fam | 2 +- .../dap_link/dap_config.h | 0 .../{plugins => external}/dap_link/dap_link.c | 0 .../{plugins => external}/dap_link/dap_link.h | 0 .../dap_link/dap_link.png | Bin .../dap_link/gui/dap_gui.c | 0 .../dap_link/gui/dap_gui.h | 0 .../dap_link/gui/dap_gui_custom_event.h | 0 .../dap_link/gui/dap_gui_i.h | 0 .../dap_link/gui/scenes/config/dap_scene.c | 0 .../dap_link/gui/scenes/config/dap_scene.h | 0 .../gui/scenes/config/dap_scene_config.h | 0 .../dap_link/gui/scenes/dap_scene_about.c | 0 .../dap_link/gui/scenes/dap_scene_config.c | 0 .../dap_link/gui/scenes/dap_scene_help.c | 0 .../dap_link/gui/scenes/dap_scene_main.c | 0 .../dap_link/gui/views/dap_main_view.c | 0 .../dap_link/gui/views/dap_main_view.h | 0 .../dap_link/icons/ActiveConnection_50x64.png | Bin .../dap_link/icons/ArrowDownEmpty_12x18.png | Bin .../dap_link/icons/ArrowDownFilled_12x18.png | Bin .../dap_link/icons/ArrowUpEmpty_12x18.png | Bin .../dap_link/icons/ArrowUpFilled_12x18.png | Bin .../dap_link/lib/free-dap | 0 .../dap_link/usb/dap_v2_usb.c | 0 .../dap_link/usb/dap_v2_usb.h | 0 .../dap_link/usb/usb_winusb.h | 0 .../hid_app/application.fam | 4 +- .../hid_app/assets/Arr_dwn_7x9.png | Bin .../hid_app/assets/Arr_up_7x9.png | Bin .../hid_app/assets/Ble_connected_15x15.png | Bin .../hid_app/assets/Ble_disconnected_15x15.png | Bin .../hid_app/assets/ButtonDown_7x4.png | Bin .../hid_app/assets/ButtonF10_5x8.png | Bin .../hid_app/assets/ButtonF11_5x8.png | Bin .../hid_app/assets/ButtonF12_5x8.png | Bin .../hid_app/assets/ButtonF1_5x8.png | Bin .../hid_app/assets/ButtonF2_5x8.png | Bin .../hid_app/assets/ButtonF3_5x8.png | Bin .../hid_app/assets/ButtonF4_5x8.png | Bin .../hid_app/assets/ButtonF5_5x8.png | Bin .../hid_app/assets/ButtonF6_5x8.png | Bin .../hid_app/assets/ButtonF7_5x8.png | Bin .../hid_app/assets/ButtonF8_5x8.png | Bin .../hid_app/assets/ButtonF9_5x8.png | Bin .../hid_app/assets/ButtonLeft_4x7.png | Bin .../hid_app/assets/ButtonRight_4x7.png | Bin .../hid_app/assets/ButtonUp_7x4.png | Bin .../hid_app/assets/Button_18x18.png | Bin .../hid_app/assets/Circles_47x47.png | Bin .../hid_app/assets/Left_mouse_icon_9x9.png | Bin .../hid_app/assets/Like_def_11x9.png | Bin .../hid_app/assets/Like_pressed_17x17.png | Bin .../hid_app/assets/Ok_btn_9x9.png | Bin .../hid_app/assets/Ok_btn_pressed_13x13.png | Bin .../hid_app/assets/Pin_arrow_down_7x9.png | Bin .../hid_app/assets/Pin_arrow_left_9x7.png | Bin .../hid_app/assets/Pin_arrow_right_9x7.png | Bin .../hid_app/assets/Pin_arrow_up_7x9.png | Bin .../hid_app/assets/Pin_back_arrow_10x8.png | Bin .../hid_app/assets/Pressed_Button_13x13.png | Bin .../hid_app/assets/Right_mouse_icon_9x9.png | Bin .../hid_app/assets/Space_65x18.png | Bin .../hid_app/assets/Voldwn_6x6.png | Bin .../hid_app/assets/Volup_8x6.png | Bin .../{plugins => external}/hid_app/hid.c | 0 .../{plugins => external}/hid_app/hid.h | 0 .../hid_app/hid_ble_10px.png | Bin .../hid_app/hid_usb_10px.png | Bin .../{plugins => external}/hid_app/views.h | 0 .../hid_app/views/hid_keyboard.c | 0 .../hid_app/views/hid_keyboard.h | 0 .../hid_app/views/hid_keynote.c | 0 .../hid_app/views/hid_keynote.h | 0 .../hid_app/views/hid_media.c | 0 .../hid_app/views/hid_media.h | 0 .../hid_app/views/hid_mouse.c | 0 .../hid_app/views/hid_mouse.h | 0 .../hid_app/views/hid_mouse_jiggler.c | 0 .../hid_app/views/hid_mouse_jiggler.h | 0 .../hid_app/views/hid_tiktok.c | 0 .../hid_app/views/hid_tiktok.h | 0 .../music_player/application.fam | 3 +- .../music_player/icons/music_10px.png | Bin .../music_player/music_player.c | 0 .../music_player/music_player_cli.c | 0 .../music_player/music_player_worker.c | 0 .../music_player/music_player_worker.h | 0 .../nfc_magic/application.fam | 0 .../nfc_magic/assets/DolphinCommon_56x48.png | Bin .../nfc_magic/assets/DolphinNice_96x59.png | Bin .../nfc_magic/assets/Loading_24.png | Bin .../nfc_magic/assets/NFC_manual_60x50.png | Bin .../nfc_magic/lib/magic/magic.c | 0 .../nfc_magic/lib/magic/magic.h | 0 .../nfc_magic/nfc_magic.c | 0 .../nfc_magic/nfc_magic.h | 0 .../nfc_magic/nfc_magic_i.h | 0 .../nfc_magic/nfc_magic_worker.c | 0 .../nfc_magic/nfc_magic_worker.h | 0 .../nfc_magic/nfc_magic_worker_i.h | 0 .../nfc_magic/scenes/nfc_magic_scene.c | 0 .../nfc_magic/scenes/nfc_magic_scene.h | 0 .../nfc_magic/scenes/nfc_magic_scene_check.c | 0 .../nfc_magic/scenes/nfc_magic_scene_config.h | 0 .../scenes/nfc_magic_scene_file_select.c | 0 .../scenes/nfc_magic_scene_magic_info.c | 0 .../scenes/nfc_magic_scene_not_magic.c | 0 .../nfc_magic/scenes/nfc_magic_scene_start.c | 0 .../scenes/nfc_magic_scene_success.c | 0 .../nfc_magic/scenes/nfc_magic_scene_wipe.c | 0 .../scenes/nfc_magic_scene_wipe_fail.c | 0 .../nfc_magic/scenes/nfc_magic_scene_write.c | 0 .../scenes/nfc_magic_scene_write_confirm.c | 0 .../scenes/nfc_magic_scene_write_fail.c | 0 .../scenes/nfc_magic_scene_wrong_card.c | 0 .../picopass/125_10px.png | Bin .../picopass/application.fam | 0 .../picopass/helpers/iclass_elite_dict.c | 0 .../picopass/helpers/iclass_elite_dict.h | 0 .../picopass/icons/DolphinMafia_115x62.png | Bin .../picopass/icons/DolphinNice_96x59.png | Bin .../picopass/icons/Nfc_10px.png | Bin .../icons/RFIDDolphinReceive_97x61.png | Bin .../picopass/icons/RFIDDolphinSend_97x61.png | Bin .../picopass/lib/loclass/optimized_cipher.c | 0 .../picopass/lib/loclass/optimized_cipher.h | 0 .../lib/loclass/optimized_cipherutils.c | 0 .../lib/loclass/optimized_cipherutils.h | 0 .../picopass/lib/loclass/optimized_elite.c | 0 .../picopass/lib/loclass/optimized_elite.h | 0 .../picopass/lib/loclass/optimized_ikeys.c | 0 .../picopass/lib/loclass/optimized_ikeys.h | 0 .../{plugins => external}/picopass/picopass.c | 0 .../{plugins => external}/picopass/picopass.h | 0 .../picopass/picopass_device.c | 0 .../picopass/picopass_device.h | 0 .../picopass/picopass_i.h | 0 .../picopass/picopass_keys.c | 0 .../picopass/picopass_keys.h | 0 .../picopass/picopass_worker.c | 0 .../picopass/picopass_worker.h | 0 .../picopass/picopass_worker_i.h | 0 .../picopass/rfal_picopass.c | 0 .../picopass/rfal_picopass.h | 0 .../picopass/scenes/picopass_scene.c | 0 .../picopass/scenes/picopass_scene.h | 0 .../scenes/picopass_scene_card_menu.c | 0 .../picopass/scenes/picopass_scene_config.h | 0 .../picopass/scenes/picopass_scene_delete.c | 0 .../scenes/picopass_scene_delete_success.c | 0 .../scenes/picopass_scene_device_info.c | 0 .../scenes/picopass_scene_file_select.c | 0 .../picopass/scenes/picopass_scene_key_menu.c | 0 .../scenes/picopass_scene_read_card.c | 0 .../scenes/picopass_scene_read_card_success.c | 0 .../picopass_scene_read_factory_success.c | 0 .../scenes/picopass_scene_save_name.c | 0 .../scenes/picopass_scene_save_success.c | 0 .../scenes/picopass_scene_saved_menu.c | 0 .../picopass/scenes/picopass_scene_start.c | 0 .../scenes/picopass_scene_write_card.c | 0 .../picopass_scene_write_card_success.c | 0 .../scenes/picopass_scene_write_key.c | 0 .../signal_generator/application.fam | 3 +- .../icons/SmallArrowDown_3x5.png | Bin .../icons/SmallArrowUp_3x5.png | Bin .../scenes/signal_gen_scene.c | 0 .../scenes/signal_gen_scene.h | 0 .../scenes/signal_gen_scene_config.h | 0 .../scenes/signal_gen_scene_mco.c | 0 .../scenes/signal_gen_scene_pwm.c | 0 .../scenes/signal_gen_scene_start.c | 0 .../signal_generator/signal_gen_10px.png | Bin .../signal_generator/signal_gen_app.c | 0 .../signal_generator/signal_gen_app_i.h | 0 .../signal_generator/views/signal_gen_pwm.c | 0 .../signal_generator/views/signal_gen_pwm.h | 0 .../snake_game/application.fam | 3 +- .../snake_game/snake_10px.png | Bin .../snake_game/snake_game.c | 0 .../spi_mem_manager/application.fam | 0 .../images/ChipLooking_64x64/frame_01.png | Bin .../images/ChipLooking_64x64/frame_02.png | Bin .../images/ChipLooking_64x64/frame_03.png | Bin .../images/ChipLooking_64x64/frame_rate | 0 .../spi_mem_manager/images/Dip8_10px.png | Bin .../spi_mem_manager/images/Dip8_32x36.png | Bin .../images/DolphinMafia_115x62.png | Bin .../images/DolphinNice_96x59.png | Bin .../images/SDQuestion_35x43.png | Bin .../images/Wiring_SPI_128x64.png | Bin .../spi_mem_manager/lib/spi/spi_mem_chip.c | 0 .../spi_mem_manager/lib/spi/spi_mem_chip.h | 0 .../lib/spi/spi_mem_chip_arr.c | 0 .../spi_mem_manager/lib/spi/spi_mem_chip_i.h | 0 .../spi_mem_manager/lib/spi/spi_mem_tools.c | 0 .../spi_mem_manager/lib/spi/spi_mem_tools.h | 0 .../spi_mem_manager/lib/spi/spi_mem_worker.c | 0 .../spi_mem_manager/lib/spi/spi_mem_worker.h | 0 .../lib/spi/spi_mem_worker_i.h | 0 .../lib/spi/spi_mem_worker_modes.c | 0 .../spi_mem_manager/scenes/spi_mem_scene.c | 0 .../spi_mem_manager/scenes/spi_mem_scene.h | 0 .../scenes/spi_mem_scene_about.c | 0 .../scenes/spi_mem_scene_chip_detect.c | 0 .../scenes/spi_mem_scene_chip_detect_fail.c | 0 .../scenes/spi_mem_scene_chip_detected.c | 0 .../scenes/spi_mem_scene_chip_error.c | 0 .../scenes/spi_mem_scene_config.h | 0 .../scenes/spi_mem_scene_delete_confirm.c | 0 .../scenes/spi_mem_scene_erase.c | 0 .../scenes/spi_mem_scene_file_info.c | 0 .../scenes/spi_mem_scene_read.c | 0 .../scenes/spi_mem_scene_read_filename.c | 0 .../scenes/spi_mem_scene_saved_file_menu.c | 0 .../scenes/spi_mem_scene_select_file.c | 0 .../scenes/spi_mem_scene_select_model.c | 0 .../scenes/spi_mem_scene_select_vendor.c | 0 .../scenes/spi_mem_scene_start.c | 0 .../scenes/spi_mem_scene_storage_error.c | 0 .../scenes/spi_mem_scene_success.c | 0 .../scenes/spi_mem_scene_verify.c | 0 .../scenes/spi_mem_scene_verify_error.c | 0 .../scenes/spi_mem_scene_wiring.c | 0 .../scenes/spi_mem_scene_write.c | 0 .../spi_mem_manager/spi_mem_app.c | 0 .../spi_mem_manager/spi_mem_app.h | 0 .../spi_mem_manager/spi_mem_app_i.h | 0 .../spi_mem_manager/spi_mem_files.c | 0 .../spi_mem_manager/spi_mem_files.h | 0 .../spi_mem_manager/tools/README.md | 0 .../spi_mem_manager/tools/chiplist/LICENSE | 0 .../tools/chiplist/chiplist.xml | 0 .../spi_mem_manager/tools/chiplist_convert.py | 0 .../views/spi_mem_view_detect.c | 0 .../views/spi_mem_view_detect.h | 0 .../views/spi_mem_view_progress.c | 0 .../views/spi_mem_view_progress.h | 0 .../weather_station/application.fam | 3 +- .../helpers/weather_station_event.h | 0 .../helpers/weather_station_types.h | 0 .../weather_station/images/Humid_10x15.png | Bin .../weather_station/images/Humid_8x13.png | Bin .../weather_station/images/Lock_7x8.png | Bin .../images/Pin_back_arrow_10x8.png | Bin .../weather_station/images/Quest_7x8.png | Bin .../images/Scanning_123x52.png | Bin .../weather_station/images/Therm_7x16.png | Bin .../weather_station/images/Timer_11x11.png | Bin .../weather_station/images/Unlock_7x8.png | Bin .../images/WarningDolphin_45x42.png | Bin .../weather_station/images/station_icon.png | Bin .../protocols/acurite_592txr.c | 0 .../protocols/acurite_592txr.h | 0 .../weather_station/protocols/acurite_606tx.c | 0 .../weather_station/protocols/acurite_606tx.h | 0 .../protocols/acurite_609txc.c | 0 .../protocols/acurite_609txc.h | 0 .../protocols/ambient_weather.c | 0 .../protocols/ambient_weather.h | 0 .../protocols/auriol_hg0601a.c | 0 .../protocols/auriol_hg0601a.h | 0 .../weather_station/protocols/gt_wt_02.c | 0 .../weather_station/protocols/gt_wt_02.h | 0 .../weather_station/protocols/gt_wt_03.c | 0 .../weather_station/protocols/gt_wt_03.h | 0 .../weather_station/protocols/infactory.c | 0 .../weather_station/protocols/infactory.h | 0 .../weather_station/protocols/lacrosse_tx.c | 0 .../weather_station/protocols/lacrosse_tx.h | 0 .../protocols/lacrosse_tx141thbv2.c | 0 .../protocols/lacrosse_tx141thbv2.h | 0 .../weather_station/protocols/nexus_th.c | 0 .../weather_station/protocols/nexus_th.h | 0 .../weather_station/protocols/oregon2.c | 0 .../weather_station/protocols/oregon2.h | 0 .../weather_station/protocols/oregon_v1.c | 0 .../weather_station/protocols/oregon_v1.h | 0 .../protocols/protocol_items.c | 0 .../protocols/protocol_items.h | 0 .../weather_station/protocols/thermopro_tx4.c | 0 .../weather_station/protocols/thermopro_tx4.h | 0 .../weather_station/protocols/tx_8300.c | 0 .../weather_station/protocols/tx_8300.h | 0 .../weather_station/protocols/ws_generic.c | 0 .../weather_station/protocols/ws_generic.h | 0 .../scenes/weather_station_receiver.c | 0 .../scenes/weather_station_scene.c | 0 .../scenes/weather_station_scene.h | 0 .../scenes/weather_station_scene_about.c | 0 .../scenes/weather_station_scene_config.h | 0 .../weather_station_scene_receiver_config.c | 0 .../weather_station_scene_receiver_info.c | 0 .../scenes/weather_station_scene_start.c | 0 .../views/weather_station_receiver.c | 0 .../views/weather_station_receiver.h | 0 .../views/weather_station_receiver_info.c | 0 .../views/weather_station_receiver_info.h | 0 .../weather_station/weather_station_10px.png | Bin .../weather_station/weather_station_app.c | 0 .../weather_station/weather_station_app_i.c | 0 .../weather_station/weather_station_app_i.h | 0 .../weather_station/weather_station_history.c | 0 .../weather_station/weather_station_history.h | 0 applications/main/fap_loader/application.fam | 1 + .../main/fap_loader/elf_cpp/elf_hashtable.cpp | 48 -- .../main/fap_loader/elf_cpp/elf_hashtable.h | 14 - .../elf_cpp/elf_hashtable_checks.hpp | 18 - .../fap_loader/elf_cpp/elf_hashtable_entry.h | 41 -- applications/main/fap_loader/fap_loader_app.c | 6 +- applications/plugins/application.fam | 9 - applications/services/applications.h | 12 - applications/services/loader/application.fam | 5 +- .../loader/firmware_api/firmware_api.cpp | 21 + .../loader/firmware_api/firmware_api.h | 5 + applications/services/loader/loader.c | 81 +--- applications/services/loader/loader_i.h | 4 - assets/.gitignore | 1 + documentation/Doxyfile | 2 +- fbt_options.py | 5 - firmware/targets/f18/api_symbols.csv | 24 +- firmware/targets/f7/api_symbols.csv | 24 +- furi/flipper.c | 2 +- lib/flipper_application/SConscript | 5 + .../api_hashtable/api_hashtable.cpp | 38 ++ .../api_hashtable/api_hashtable.h | 85 ++++ .../api_hashtable}/compilesort.hpp | 4 + .../elf/elf_api_interface.h | 12 +- lib/flipper_application/elf/elf_file.c | 33 +- lib/flipper_application/elf/elf_file.h | 22 +- lib/flipper_application/elf/elf_file_i.h | 2 + lib/flipper_application/flipper_application.c | 64 ++- lib/flipper_application/flipper_application.h | 36 +- .../plugins/composite_resolver.c | 52 +++ .../plugins/composite_resolver.h | 46 ++ .../plugins/plugin_manager.c | 153 +++++++ .../plugins/plugin_manager.h | 82 ++++ scripts/distfap.py | 71 +++ scripts/fbt/appmanifest.py | 46 +- scripts/fbt/fapassets.py | 108 +++++ scripts/fbt_tools/fbt_extapps.py | 425 ++++++++---------- scripts/fbt_tools/fbt_sdk.py | 2 +- scripts/flipper/storage.py | 348 +++++++++----- scripts/requirements.txt | 9 - scripts/runfap.py | 132 +++--- scripts/selfupdate.py | 94 ++-- scripts/storage.py | 267 +++-------- site_scons/commandline.scons | 6 +- site_scons/extapps.scons | 65 ++- 376 files changed, 2041 insertions(+), 1036 deletions(-) create mode 100644 applications/examples/example_plugins/application.fam create mode 100644 applications/examples/example_plugins/example_plugins.c create mode 100644 applications/examples/example_plugins/example_plugins_multi.c create mode 100644 applications/examples/example_plugins/plugin1.c create mode 100644 applications/examples/example_plugins/plugin2.c create mode 100644 applications/examples/example_plugins/plugin_interface.h create mode 100644 applications/examples/example_plugins_advanced/app_api.c create mode 100644 applications/examples/example_plugins_advanced/app_api.h create mode 100644 applications/examples/example_plugins_advanced/app_api_interface.h create mode 100644 applications/examples/example_plugins_advanced/app_api_table.cpp create mode 100644 applications/examples/example_plugins_advanced/app_api_table_i.h create mode 100644 applications/examples/example_plugins_advanced/application.fam create mode 100644 applications/examples/example_plugins_advanced/example_advanced_plugins.c create mode 100644 applications/examples/example_plugins_advanced/plugin1.c create mode 100644 applications/examples/example_plugins_advanced/plugin2.c create mode 100644 applications/examples/example_plugins_advanced/plugin_interface.h create mode 100644 applications/external/application.fam rename applications/{plugins => external}/clock/application.fam (82%) rename applications/{plugins => external}/clock/clock.png (100%) rename applications/{plugins => external}/clock/clock_app.c (100%) rename applications/{plugins => external}/dap_link/README.md (100%) rename applications/{plugins => external}/dap_link/application.fam (92%) rename applications/{plugins => external}/dap_link/dap_config.h (100%) rename applications/{plugins => external}/dap_link/dap_link.c (100%) rename applications/{plugins => external}/dap_link/dap_link.h (100%) rename applications/{plugins => external}/dap_link/dap_link.png (100%) rename applications/{plugins => external}/dap_link/gui/dap_gui.c (100%) rename applications/{plugins => external}/dap_link/gui/dap_gui.h (100%) rename applications/{plugins => external}/dap_link/gui/dap_gui_custom_event.h (100%) rename applications/{plugins => external}/dap_link/gui/dap_gui_i.h (100%) rename applications/{plugins => external}/dap_link/gui/scenes/config/dap_scene.c (100%) rename applications/{plugins => external}/dap_link/gui/scenes/config/dap_scene.h (100%) rename applications/{plugins => external}/dap_link/gui/scenes/config/dap_scene_config.h (100%) rename applications/{plugins => external}/dap_link/gui/scenes/dap_scene_about.c (100%) rename applications/{plugins => external}/dap_link/gui/scenes/dap_scene_config.c (100%) rename applications/{plugins => external}/dap_link/gui/scenes/dap_scene_help.c (100%) rename applications/{plugins => external}/dap_link/gui/scenes/dap_scene_main.c (100%) rename applications/{plugins => external}/dap_link/gui/views/dap_main_view.c (100%) rename applications/{plugins => external}/dap_link/gui/views/dap_main_view.h (100%) rename applications/{plugins => external}/dap_link/icons/ActiveConnection_50x64.png (100%) rename applications/{plugins => external}/dap_link/icons/ArrowDownEmpty_12x18.png (100%) rename applications/{plugins => external}/dap_link/icons/ArrowDownFilled_12x18.png (100%) rename applications/{plugins => external}/dap_link/icons/ArrowUpEmpty_12x18.png (100%) rename applications/{plugins => external}/dap_link/icons/ArrowUpFilled_12x18.png (100%) rename applications/{plugins => external}/dap_link/lib/free-dap (100%) rename applications/{plugins => external}/dap_link/usb/dap_v2_usb.c (100%) rename applications/{plugins => external}/dap_link/usb/dap_v2_usb.h (100%) rename applications/{plugins => external}/dap_link/usb/usb_winusb.h (100%) rename applications/{plugins => external}/hid_app/application.fam (86%) rename applications/{plugins => external}/hid_app/assets/Arr_dwn_7x9.png (100%) rename applications/{plugins => external}/hid_app/assets/Arr_up_7x9.png (100%) rename applications/{plugins => external}/hid_app/assets/Ble_connected_15x15.png (100%) rename applications/{plugins => external}/hid_app/assets/Ble_disconnected_15x15.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonDown_7x4.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF10_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF11_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF12_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF1_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF2_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF3_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF4_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF5_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF6_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF7_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF8_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonF9_5x8.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonLeft_4x7.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonRight_4x7.png (100%) rename applications/{plugins => external}/hid_app/assets/ButtonUp_7x4.png (100%) rename applications/{plugins => external}/hid_app/assets/Button_18x18.png (100%) rename applications/{plugins => external}/hid_app/assets/Circles_47x47.png (100%) rename applications/{plugins => external}/hid_app/assets/Left_mouse_icon_9x9.png (100%) rename applications/{plugins => external}/hid_app/assets/Like_def_11x9.png (100%) rename applications/{plugins => external}/hid_app/assets/Like_pressed_17x17.png (100%) rename applications/{plugins => external}/hid_app/assets/Ok_btn_9x9.png (100%) rename applications/{plugins => external}/hid_app/assets/Ok_btn_pressed_13x13.png (100%) rename applications/{plugins => external}/hid_app/assets/Pin_arrow_down_7x9.png (100%) rename applications/{plugins => external}/hid_app/assets/Pin_arrow_left_9x7.png (100%) rename applications/{plugins => external}/hid_app/assets/Pin_arrow_right_9x7.png (100%) rename applications/{plugins => external}/hid_app/assets/Pin_arrow_up_7x9.png (100%) rename applications/{plugins => external}/hid_app/assets/Pin_back_arrow_10x8.png (100%) rename applications/{plugins => external}/hid_app/assets/Pressed_Button_13x13.png (100%) rename applications/{plugins => external}/hid_app/assets/Right_mouse_icon_9x9.png (100%) rename applications/{plugins => external}/hid_app/assets/Space_65x18.png (100%) rename applications/{plugins => external}/hid_app/assets/Voldwn_6x6.png (100%) rename applications/{plugins => external}/hid_app/assets/Volup_8x6.png (100%) rename applications/{plugins => external}/hid_app/hid.c (100%) rename applications/{plugins => external}/hid_app/hid.h (100%) rename applications/{plugins => external}/hid_app/hid_ble_10px.png (100%) rename applications/{plugins => external}/hid_app/hid_usb_10px.png (100%) rename applications/{plugins => external}/hid_app/views.h (100%) rename applications/{plugins => external}/hid_app/views/hid_keyboard.c (100%) rename applications/{plugins => external}/hid_app/views/hid_keyboard.h (100%) rename applications/{plugins => external}/hid_app/views/hid_keynote.c (100%) rename applications/{plugins => external}/hid_app/views/hid_keynote.h (100%) rename applications/{plugins => external}/hid_app/views/hid_media.c (100%) rename applications/{plugins => external}/hid_app/views/hid_media.h (100%) rename applications/{plugins => external}/hid_app/views/hid_mouse.c (100%) rename applications/{plugins => external}/hid_app/views/hid_mouse.h (100%) rename applications/{plugins => external}/hid_app/views/hid_mouse_jiggler.c (100%) rename applications/{plugins => external}/hid_app/views/hid_mouse_jiggler.h (100%) rename applications/{plugins => external}/hid_app/views/hid_tiktok.c (100%) rename applications/{plugins => external}/hid_app/views/hid_tiktok.h (100%) rename applications/{plugins => external}/music_player/application.fam (87%) rename applications/{plugins => external}/music_player/icons/music_10px.png (100%) rename applications/{plugins => external}/music_player/music_player.c (100%) rename applications/{plugins => external}/music_player/music_player_cli.c (100%) rename applications/{plugins => external}/music_player/music_player_worker.c (100%) rename applications/{plugins => external}/music_player/music_player_worker.h (100%) rename applications/{plugins => external}/nfc_magic/application.fam (100%) rename applications/{plugins => external}/nfc_magic/assets/DolphinCommon_56x48.png (100%) rename applications/{plugins => external}/nfc_magic/assets/DolphinNice_96x59.png (100%) rename applications/{plugins => external}/nfc_magic/assets/Loading_24.png (100%) rename applications/{plugins => external}/nfc_magic/assets/NFC_manual_60x50.png (100%) rename applications/{plugins => external}/nfc_magic/lib/magic/magic.c (100%) rename applications/{plugins => external}/nfc_magic/lib/magic/magic.h (100%) rename applications/{plugins => external}/nfc_magic/nfc_magic.c (100%) rename applications/{plugins => external}/nfc_magic/nfc_magic.h (100%) rename applications/{plugins => external}/nfc_magic/nfc_magic_i.h (100%) rename applications/{plugins => external}/nfc_magic/nfc_magic_worker.c (100%) rename applications/{plugins => external}/nfc_magic/nfc_magic_worker.h (100%) rename applications/{plugins => external}/nfc_magic/nfc_magic_worker_i.h (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene.h (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_check.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_config.h (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_file_select.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_magic_info.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_not_magic.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_start.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_success.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_wipe.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_write.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_write_confirm.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_write_fail.c (100%) rename applications/{plugins => external}/nfc_magic/scenes/nfc_magic_scene_wrong_card.c (100%) rename applications/{plugins => external}/picopass/125_10px.png (100%) rename applications/{plugins => external}/picopass/application.fam (100%) rename applications/{plugins => external}/picopass/helpers/iclass_elite_dict.c (100%) rename applications/{plugins => external}/picopass/helpers/iclass_elite_dict.h (100%) rename applications/{plugins => external}/picopass/icons/DolphinMafia_115x62.png (100%) rename applications/{plugins => external}/picopass/icons/DolphinNice_96x59.png (100%) rename applications/{plugins => external}/picopass/icons/Nfc_10px.png (100%) rename applications/{plugins => external}/picopass/icons/RFIDDolphinReceive_97x61.png (100%) rename applications/{plugins => external}/picopass/icons/RFIDDolphinSend_97x61.png (100%) rename applications/{plugins => external}/picopass/lib/loclass/optimized_cipher.c (100%) rename applications/{plugins => external}/picopass/lib/loclass/optimized_cipher.h (100%) rename applications/{plugins => external}/picopass/lib/loclass/optimized_cipherutils.c (100%) rename applications/{plugins => external}/picopass/lib/loclass/optimized_cipherutils.h (100%) rename applications/{plugins => external}/picopass/lib/loclass/optimized_elite.c (100%) rename applications/{plugins => external}/picopass/lib/loclass/optimized_elite.h (100%) rename applications/{plugins => external}/picopass/lib/loclass/optimized_ikeys.c (100%) rename applications/{plugins => external}/picopass/lib/loclass/optimized_ikeys.h (100%) rename applications/{plugins => external}/picopass/picopass.c (100%) rename applications/{plugins => external}/picopass/picopass.h (100%) rename applications/{plugins => external}/picopass/picopass_device.c (100%) rename applications/{plugins => external}/picopass/picopass_device.h (100%) rename applications/{plugins => external}/picopass/picopass_i.h (100%) rename applications/{plugins => external}/picopass/picopass_keys.c (100%) rename applications/{plugins => external}/picopass/picopass_keys.h (100%) rename applications/{plugins => external}/picopass/picopass_worker.c (100%) rename applications/{plugins => external}/picopass/picopass_worker.h (100%) rename applications/{plugins => external}/picopass/picopass_worker_i.h (100%) rename applications/{plugins => external}/picopass/rfal_picopass.c (100%) rename applications/{plugins => external}/picopass/rfal_picopass.h (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene.h (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_card_menu.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_config.h (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_delete.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_delete_success.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_device_info.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_file_select.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_key_menu.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_read_card.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_read_card_success.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_read_factory_success.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_save_name.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_save_success.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_saved_menu.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_start.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_write_card.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_write_card_success.c (100%) rename applications/{plugins => external}/picopass/scenes/picopass_scene_write_key.c (100%) rename applications/{plugins => external}/signal_generator/application.fam (78%) rename applications/{plugins => external}/signal_generator/icons/SmallArrowDown_3x5.png (100%) rename applications/{plugins => external}/signal_generator/icons/SmallArrowUp_3x5.png (100%) rename applications/{plugins => external}/signal_generator/scenes/signal_gen_scene.c (100%) rename applications/{plugins => external}/signal_generator/scenes/signal_gen_scene.h (100%) rename applications/{plugins => external}/signal_generator/scenes/signal_gen_scene_config.h (100%) rename applications/{plugins => external}/signal_generator/scenes/signal_gen_scene_mco.c (100%) rename applications/{plugins => external}/signal_generator/scenes/signal_gen_scene_pwm.c (100%) rename applications/{plugins => external}/signal_generator/scenes/signal_gen_scene_start.c (100%) rename applications/{plugins => external}/signal_generator/signal_gen_10px.png (100%) rename applications/{plugins => external}/signal_generator/signal_gen_app.c (100%) rename applications/{plugins => external}/signal_generator/signal_gen_app_i.h (100%) rename applications/{plugins => external}/signal_generator/views/signal_gen_pwm.c (100%) rename applications/{plugins => external}/signal_generator/views/signal_gen_pwm.h (100%) rename applications/{plugins => external}/snake_game/application.fam (75%) rename applications/{plugins => external}/snake_game/snake_10px.png (100%) rename applications/{plugins => external}/snake_game/snake_game.c (100%) rename applications/{plugins => external}/spi_mem_manager/application.fam (100%) rename applications/{plugins => external}/spi_mem_manager/images/ChipLooking_64x64/frame_01.png (100%) rename applications/{plugins => external}/spi_mem_manager/images/ChipLooking_64x64/frame_02.png (100%) rename applications/{plugins => external}/spi_mem_manager/images/ChipLooking_64x64/frame_03.png (100%) rename applications/{plugins => external}/spi_mem_manager/images/ChipLooking_64x64/frame_rate (100%) rename applications/{plugins => external}/spi_mem_manager/images/Dip8_10px.png (100%) rename applications/{plugins => external}/spi_mem_manager/images/Dip8_32x36.png (100%) rename applications/{plugins => external}/spi_mem_manager/images/DolphinMafia_115x62.png (100%) rename applications/{plugins => external}/spi_mem_manager/images/DolphinNice_96x59.png (100%) rename applications/{plugins => external}/spi_mem_manager/images/SDQuestion_35x43.png (100%) rename applications/{plugins => external}/spi_mem_manager/images/Wiring_SPI_128x64.png (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_chip.c (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_chip.h (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_chip_arr.c (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_chip_i.h (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_tools.c (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_tools.h (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_worker.c (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_worker.h (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_worker_i.h (100%) rename applications/{plugins => external}/spi_mem_manager/lib/spi/spi_mem_worker_modes.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene.h (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_about.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_chip_error.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_config.h (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_erase.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_file_info.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_read.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_read_filename.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_select_file.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_select_model.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_start.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_storage_error.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_success.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_verify.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_verify_error.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_wiring.c (100%) rename applications/{plugins => external}/spi_mem_manager/scenes/spi_mem_scene_write.c (100%) rename applications/{plugins => external}/spi_mem_manager/spi_mem_app.c (100%) rename applications/{plugins => external}/spi_mem_manager/spi_mem_app.h (100%) rename applications/{plugins => external}/spi_mem_manager/spi_mem_app_i.h (100%) rename applications/{plugins => external}/spi_mem_manager/spi_mem_files.c (100%) rename applications/{plugins => external}/spi_mem_manager/spi_mem_files.h (100%) rename applications/{plugins => external}/spi_mem_manager/tools/README.md (100%) rename applications/{plugins => external}/spi_mem_manager/tools/chiplist/LICENSE (100%) rename applications/{plugins => external}/spi_mem_manager/tools/chiplist/chiplist.xml (100%) rename applications/{plugins => external}/spi_mem_manager/tools/chiplist_convert.py (100%) rename applications/{plugins => external}/spi_mem_manager/views/spi_mem_view_detect.c (100%) rename applications/{plugins => external}/spi_mem_manager/views/spi_mem_view_detect.h (100%) rename applications/{plugins => external}/spi_mem_manager/views/spi_mem_view_progress.c (100%) rename applications/{plugins => external}/spi_mem_manager/views/spi_mem_view_progress.h (100%) rename applications/{plugins => external}/weather_station/application.fam (79%) rename applications/{plugins => external}/weather_station/helpers/weather_station_event.h (100%) rename applications/{plugins => external}/weather_station/helpers/weather_station_types.h (100%) rename applications/{plugins => external}/weather_station/images/Humid_10x15.png (100%) rename applications/{plugins => external}/weather_station/images/Humid_8x13.png (100%) rename applications/{plugins => external}/weather_station/images/Lock_7x8.png (100%) rename applications/{plugins => external}/weather_station/images/Pin_back_arrow_10x8.png (100%) rename applications/{plugins => external}/weather_station/images/Quest_7x8.png (100%) rename applications/{plugins => external}/weather_station/images/Scanning_123x52.png (100%) rename applications/{plugins => external}/weather_station/images/Therm_7x16.png (100%) rename applications/{plugins => external}/weather_station/images/Timer_11x11.png (100%) rename applications/{plugins => external}/weather_station/images/Unlock_7x8.png (100%) rename applications/{plugins => external}/weather_station/images/WarningDolphin_45x42.png (100%) rename applications/{plugins => external}/weather_station/images/station_icon.png (100%) rename applications/{plugins => external}/weather_station/protocols/acurite_592txr.c (100%) rename applications/{plugins => external}/weather_station/protocols/acurite_592txr.h (100%) rename applications/{plugins => external}/weather_station/protocols/acurite_606tx.c (100%) rename applications/{plugins => external}/weather_station/protocols/acurite_606tx.h (100%) rename applications/{plugins => external}/weather_station/protocols/acurite_609txc.c (100%) rename applications/{plugins => external}/weather_station/protocols/acurite_609txc.h (100%) rename applications/{plugins => external}/weather_station/protocols/ambient_weather.c (100%) rename applications/{plugins => external}/weather_station/protocols/ambient_weather.h (100%) rename applications/{plugins => external}/weather_station/protocols/auriol_hg0601a.c (100%) rename applications/{plugins => external}/weather_station/protocols/auriol_hg0601a.h (100%) rename applications/{plugins => external}/weather_station/protocols/gt_wt_02.c (100%) rename applications/{plugins => external}/weather_station/protocols/gt_wt_02.h (100%) rename applications/{plugins => external}/weather_station/protocols/gt_wt_03.c (100%) rename applications/{plugins => external}/weather_station/protocols/gt_wt_03.h (100%) rename applications/{plugins => external}/weather_station/protocols/infactory.c (100%) rename applications/{plugins => external}/weather_station/protocols/infactory.h (100%) rename applications/{plugins => external}/weather_station/protocols/lacrosse_tx.c (100%) rename applications/{plugins => external}/weather_station/protocols/lacrosse_tx.h (100%) rename applications/{plugins => external}/weather_station/protocols/lacrosse_tx141thbv2.c (100%) rename applications/{plugins => external}/weather_station/protocols/lacrosse_tx141thbv2.h (100%) rename applications/{plugins => external}/weather_station/protocols/nexus_th.c (100%) rename applications/{plugins => external}/weather_station/protocols/nexus_th.h (100%) rename applications/{plugins => external}/weather_station/protocols/oregon2.c (100%) rename applications/{plugins => external}/weather_station/protocols/oregon2.h (100%) rename applications/{plugins => external}/weather_station/protocols/oregon_v1.c (100%) rename applications/{plugins => external}/weather_station/protocols/oregon_v1.h (100%) rename applications/{plugins => external}/weather_station/protocols/protocol_items.c (100%) rename applications/{plugins => external}/weather_station/protocols/protocol_items.h (100%) rename applications/{plugins => external}/weather_station/protocols/thermopro_tx4.c (100%) rename applications/{plugins => external}/weather_station/protocols/thermopro_tx4.h (100%) rename applications/{plugins => external}/weather_station/protocols/tx_8300.c (100%) rename applications/{plugins => external}/weather_station/protocols/tx_8300.h (100%) rename applications/{plugins => external}/weather_station/protocols/ws_generic.c (100%) rename applications/{plugins => external}/weather_station/protocols/ws_generic.h (100%) rename applications/{plugins => external}/weather_station/scenes/weather_station_receiver.c (100%) rename applications/{plugins => external}/weather_station/scenes/weather_station_scene.c (100%) rename applications/{plugins => external}/weather_station/scenes/weather_station_scene.h (100%) rename applications/{plugins => external}/weather_station/scenes/weather_station_scene_about.c (100%) rename applications/{plugins => external}/weather_station/scenes/weather_station_scene_config.h (100%) rename applications/{plugins => external}/weather_station/scenes/weather_station_scene_receiver_config.c (100%) rename applications/{plugins => external}/weather_station/scenes/weather_station_scene_receiver_info.c (100%) rename applications/{plugins => external}/weather_station/scenes/weather_station_scene_start.c (100%) rename applications/{plugins => external}/weather_station/views/weather_station_receiver.c (100%) rename applications/{plugins => external}/weather_station/views/weather_station_receiver.h (100%) rename applications/{plugins => external}/weather_station/views/weather_station_receiver_info.c (100%) rename applications/{plugins => external}/weather_station/views/weather_station_receiver_info.h (100%) rename applications/{plugins => external}/weather_station/weather_station_10px.png (100%) rename applications/{plugins => external}/weather_station/weather_station_app.c (100%) rename applications/{plugins => external}/weather_station/weather_station_app_i.c (100%) rename applications/{plugins => external}/weather_station/weather_station_app_i.h (100%) rename applications/{plugins => external}/weather_station/weather_station_history.c (100%) rename applications/{plugins => external}/weather_station/weather_station_history.h (100%) delete mode 100644 applications/main/fap_loader/elf_cpp/elf_hashtable.cpp delete mode 100644 applications/main/fap_loader/elf_cpp/elf_hashtable.h delete mode 100644 applications/main/fap_loader/elf_cpp/elf_hashtable_checks.hpp delete mode 100644 applications/main/fap_loader/elf_cpp/elf_hashtable_entry.h delete mode 100644 applications/plugins/application.fam create mode 100644 applications/services/loader/firmware_api/firmware_api.cpp create mode 100644 applications/services/loader/firmware_api/firmware_api.h create mode 100644 lib/flipper_application/api_hashtable/api_hashtable.cpp create mode 100644 lib/flipper_application/api_hashtable/api_hashtable.h rename {applications/main/fap_loader/elf_cpp => lib/flipper_application/api_hashtable}/compilesort.hpp (99%) create mode 100644 lib/flipper_application/plugins/composite_resolver.c create mode 100644 lib/flipper_application/plugins/composite_resolver.h create mode 100644 lib/flipper_application/plugins/plugin_manager.c create mode 100644 lib/flipper_application/plugins/plugin_manager.h create mode 100644 scripts/distfap.py create mode 100644 scripts/fbt/fapassets.py delete mode 100644 scripts/requirements.txt diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 69f8289f9..0bc130243 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -22,8 +22,8 @@ /applications/main/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm /applications/main/u2f/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/plugins/bt_hid_app/ @skotopes @DrZlo13 @hedger @gornekich -/applications/plugins/picopass/ @skotopes @DrZlo13 @hedger @gornekich +/applications/external/bt_hid_app/ @skotopes @DrZlo13 @hedger @gornekich +/applications/external/picopass/ @skotopes @DrZlo13 @hedger @gornekich /applications/services/bt/ @skotopes @DrZlo13 @hedger @gornekich /applications/services/cli/ @skotopes @DrZlo13 @hedger @nminaylov diff --git a/.gitmodules b/.gitmodules index a97e0933a..56368cd58 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,6 +28,6 @@ [submodule "lib/cxxheaderparser"] path = lib/cxxheaderparser url = https://github.com/robotpy/cxxheaderparser.git -[submodule "applications/plugins/dap_link/lib/free-dap"] - path = applications/plugins/dap_link/lib/free-dap +[submodule "applications/external/dap_link/lib/free-dap"] + path = applications/external/dap_link/lib/free-dap url = https://github.com/ataradov/free-dap.git diff --git a/.pvsoptions b/.pvsoptions index ca1b2b572..6b22aed76 100644 --- a/.pvsoptions +++ b/.pvsoptions @@ -1 +1 @@ ---ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/plugins/dap_link/lib/free-dap +--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/STM32CubeWB -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap diff --git a/SConstruct b/SConstruct index 62e37dfdc..090a92599 100644 --- a/SConstruct +++ b/SConstruct @@ -139,34 +139,33 @@ if GetOption("fullenv") or any( basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"]) distenv.Default(basic_dist) -dist_dir = distenv.GetProjetDirName() +dist_dir_name = distenv.GetProjetDirName() +dist_dir = distenv.Dir(f"#/dist/{dist_dir_name}") +external_apps_artifacts = firmware_env["FW_EXTAPPS"] +external_app_list = external_apps_artifacts.application_map.values() + fap_dist = [ distenv.Install( - distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"), - list( - app_artifact.debug - for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() - ), + dist_dir.Dir("debug_elf"), + list(app_artifact.debug for app_artifact in external_app_list), ), *( distenv.Install( - f"#/dist/{dist_dir}/apps/{app_artifact.app.fap_category}", - app_artifact.compact[0], + dist_dir.File(dist_entry[1]).dir, + app_artifact.compact, ) - for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() + for app_artifact in external_app_list + for dist_entry in app_artifact.dist_entries ), ] Depends( fap_dist, - list( - app_artifact.validator - for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() - ), + list(app_artifact.validator for app_artifact in external_app_list), ) Alias("fap_dist", fap_dist) # distenv.Default(fap_dist) -distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist) +distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_dist) # Copy all faps to device diff --git a/applications/examples/application.fam b/applications/examples/application.fam index 8556714c9..347411fac 100644 --- a/applications/examples/application.fam +++ b/applications/examples/application.fam @@ -1,3 +1,4 @@ +# Placeholder App( appid="example_apps", name="Example apps bundle", diff --git a/applications/examples/example_plugins/application.fam b/applications/examples/example_plugins/application.fam new file mode 100644 index 000000000..a6e3c2078 --- /dev/null +++ b/applications/examples/example_plugins/application.fam @@ -0,0 +1,31 @@ +App( + appid="example_plugins", + name="Example: App w/plugin", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_plugins_app", + stack_size=2 * 1024, + fap_category="Examples", +) + +App( + appid="example_plugins_multi", + name="Example: App w/plugins", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_plugins_multi_app", + stack_size=2 * 1024, + fap_category="Examples", +) + +App( + appid="example_plugin1", + apptype=FlipperAppType.PLUGIN, + entry_point="example_plugin1_ep", + requires=["example_plugins", "example_plugins_multi"], +) + +App( + appid="example_plugin2", + apptype=FlipperAppType.PLUGIN, + entry_point="example_plugin2_ep", + requires=["example_plugins_multi"], +) diff --git a/applications/examples/example_plugins/example_plugins.c b/applications/examples/example_plugins/example_plugins.c new file mode 100644 index 000000000..acc5903ad --- /dev/null +++ b/applications/examples/example_plugins/example_plugins.c @@ -0,0 +1,70 @@ +/* + * An example of a plugin host application. + * Loads a single plugin and calls its methods. + */ + +#include "plugin_interface.h" + +#include + +#include +#include +#include + +#define TAG "example_plugins" + +int32_t example_plugins_app(void* p) { + UNUSED(p); + + FURI_LOG_I(TAG, "Starting"); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); + + do { + FlipperApplicationPreloadStatus preload_res = + flipper_application_preload(app, APP_DATA_PATH("plugins/example_plugin1.fal")); + + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to preload plugin"); + break; + } + + if(!flipper_application_is_plugin(app)) { + FURI_LOG_E(TAG, "Plugin file is not a library"); + break; + } + + FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(app); + if(load_status != FlipperApplicationLoadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to load plugin file"); + break; + } + + const FlipperAppPluginDescriptor* app_descriptor = + flipper_application_plugin_get_descriptor(app); + + FURI_LOG_I( + TAG, + "Loaded plugin for appid '%s', API %lu", + app_descriptor->appid, + app_descriptor->ep_api_version); + + furi_check(app_descriptor->ep_api_version == PLUGIN_API_VERSION); + furi_check(strcmp(app_descriptor->appid, PLUGIN_APP_ID) == 0); + + const ExamplePlugin* plugin = app_descriptor->entry_point; + + FURI_LOG_I(TAG, "Plugin name: %s", plugin->name); + FURI_LOG_I(TAG, "Plugin method1: %d", plugin->method1()); + FURI_LOG_I(TAG, "Plugin method2(7,8): %d", plugin->method2(7, 8)); + FURI_LOG_I(TAG, "Plugin method2(1337,228): %d", plugin->method2(1337, 228)); + } while(false); + flipper_application_free(app); + + furi_record_close(RECORD_STORAGE); + FURI_LOG_I(TAG, "Goodbye!"); + + return 0; +} diff --git a/applications/examples/example_plugins/example_plugins_multi.c b/applications/examples/example_plugins/example_plugins_multi.c new file mode 100644 index 000000000..12eba01c1 --- /dev/null +++ b/applications/examples/example_plugins/example_plugins_multi.c @@ -0,0 +1,43 @@ +/* + * An example of an advanced plugin host application. + * It uses PluginManager to load all plugins from a directory + */ + +#include "plugin_interface.h" + +#include +#include +#include + +#include + +#define TAG "example_plugins" + +int32_t example_plugins_multi_app(void* p) { + UNUSED(p); + + FURI_LOG_I(TAG, "Starting"); + + PluginManager* manager = + plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface); + + if(plugin_manager_load_all(manager, APP_DATA_PATH("plugins")) != PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load all libs"); + return 0; + } + + uint32_t plugin_count = plugin_manager_get_count(manager); + FURI_LOG_I(TAG, "Loaded %lu plugin(s)", plugin_count); + + for(uint32_t i = 0; i < plugin_count; i++) { + const ExamplePlugin* plugin = plugin_manager_get_ep(manager, i); + FURI_LOG_I(TAG, "plugin name: %s", plugin->name); + FURI_LOG_I(TAG, "plugin method1: %d", plugin->method1()); + FURI_LOG_I(TAG, "plugin method2(7,8): %d", plugin->method2(7, 8)); + } + + plugin_manager_free(manager); + FURI_LOG_I(TAG, "Goodbye!"); + + return 0; +} diff --git a/applications/examples/example_plugins/plugin1.c b/applications/examples/example_plugins/plugin1.c new file mode 100644 index 000000000..156219353 --- /dev/null +++ b/applications/examples/example_plugins/plugin1.c @@ -0,0 +1,32 @@ +/* A simple plugin implementing example_plugins application's plugin interface */ + +#include "plugin_interface.h" + +#include + +static int example_plugin1_method1() { + return 42; +} + +static int example_plugin1_method2(int arg1, int arg2) { + return arg1 + arg2; +} + +/* Actual implementation of app<>plugin interface */ +static const ExamplePlugin example_plugin1 = { + .name = "Demo App Plugin 1", + .method1 = &example_plugin1_method1, + .method2 = &example_plugin1_method2, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor example_plugin1_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &example_plugin1, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* example_plugin1_ep() { + return &example_plugin1_descriptor; +} diff --git a/applications/examples/example_plugins/plugin2.c b/applications/examples/example_plugins/plugin2.c new file mode 100644 index 000000000..0b774dad2 --- /dev/null +++ b/applications/examples/example_plugins/plugin2.c @@ -0,0 +1,32 @@ +/* Second plugin implementing example_plugins application's plugin interface */ + +#include "plugin_interface.h" + +#include + +static int example_plugin2_method1() { + return 1337; +} + +static int example_plugin2_method2(int arg1, int arg2) { + return arg1 - arg2; +} + +/* Actual implementation of app<>plugin interface */ +static const ExamplePlugin example_plugin2 = { + .name = "Demo App Plugin 2", + .method1 = &example_plugin2_method1, + .method2 = &example_plugin2_method2, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor example_plugin2_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &example_plugin2, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* example_plugin2_ep() { + return &example_plugin2_descriptor; +} diff --git a/applications/examples/example_plugins/plugin_interface.h b/applications/examples/example_plugins/plugin_interface.h new file mode 100644 index 000000000..e24bc47bf --- /dev/null +++ b/applications/examples/example_plugins/plugin_interface.h @@ -0,0 +1,12 @@ +#pragma once + +/* Common interface between a plugin and host applicaion */ + +#define PLUGIN_APP_ID "example_plugins" +#define PLUGIN_API_VERSION 1 + +typedef struct { + const char* name; + int (*method1)(); + int (*method2)(int, int); +} ExamplePlugin; diff --git a/applications/examples/example_plugins_advanced/app_api.c b/applications/examples/example_plugins_advanced/app_api.c new file mode 100644 index 000000000..42b3a1860 --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api.c @@ -0,0 +1,25 @@ +#include "app_api.h" + +/* Actual implementation of app's API and its private state */ + +static uint32_t accumulator = 0; + +void app_api_accumulator_set(uint32_t value) { + accumulator = value; +} + +uint32_t app_api_accumulator_get() { + return accumulator; +} + +void app_api_accumulator_add(uint32_t value) { + accumulator += value; +} + +void app_api_accumulator_sub(uint32_t value) { + accumulator -= value; +} + +void app_api_accumulator_mul(uint32_t value) { + accumulator *= value; +} diff --git a/applications/examples/example_plugins_advanced/app_api.h b/applications/examples/example_plugins_advanced/app_api.h new file mode 100644 index 000000000..7035b79f5 --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api.h @@ -0,0 +1,25 @@ +#pragma once + +/* + * This file contains an API that is internally implemented by the application + * It is also exposed to plugins to allow them to use the application's API. + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void app_api_accumulator_set(uint32_t value); + +uint32_t app_api_accumulator_get(); + +void app_api_accumulator_add(uint32_t value); + +void app_api_accumulator_sub(uint32_t value); + +void app_api_accumulator_mul(uint32_t value); + +#ifdef __cplusplus +} +#endif diff --git a/applications/examples/example_plugins_advanced/app_api_interface.h b/applications/examples/example_plugins_advanced/app_api_interface.h new file mode 100644 index 000000000..d0db44c4a --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api_interface.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +/* + * Resolver interface with private application's symbols. + * Implementation is contained in app_api_table.c + */ +extern const ElfApiInterface* const application_api_interface; \ No newline at end of file diff --git a/applications/examples/example_plugins_advanced/app_api_table.cpp b/applications/examples/example_plugins_advanced/app_api_table.cpp new file mode 100644 index 000000000..aacfb8c18 --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api_table.cpp @@ -0,0 +1,27 @@ +#include +#include + +/* + * This file contains an implementation of a symbol table + * with private app's symbols. It is used by composite API resolver + * to load plugins that use internal application's APIs. + */ +#include "app_api_table_i.h" + +static_assert(!has_hash_collisions(app_api_table), "Detected API method hash collision!"); + +constexpr HashtableApiInterface applicaton_hashtable_api_interface{ + { + .api_version_major = 0, + .api_version_minor = 0, + /* generic resolver using pre-sorted array */ + .resolver_callback = &elf_resolve_from_hashtable, + }, + /* pointers to application's API table boundaries */ + .table_cbegin = app_api_table.cbegin(), + .table_cend = app_api_table.cend(), +}; + +/* Casting to generic resolver to use in Composite API resolver */ +extern "C" const ElfApiInterface* const application_api_interface = + &applicaton_hashtable_api_interface; diff --git a/applications/examples/example_plugins_advanced/app_api_table_i.h b/applications/examples/example_plugins_advanced/app_api_table_i.h new file mode 100644 index 000000000..17cc8be5f --- /dev/null +++ b/applications/examples/example_plugins_advanced/app_api_table_i.h @@ -0,0 +1,13 @@ +#include "app_api.h" + +/* + * A list of app's private functions and objects to expose for plugins. + * It is used to generate a table of symbols for import resolver to use. + * TBD: automatically generate this table from app's header files + */ +static constexpr auto app_api_table = sort(create_array_t( + API_METHOD(app_api_accumulator_set, void, (uint32_t)), + API_METHOD(app_api_accumulator_get, uint32_t, ()), + API_METHOD(app_api_accumulator_add, void, (uint32_t)), + API_METHOD(app_api_accumulator_sub, void, (uint32_t)), + API_METHOD(app_api_accumulator_mul, void, (uint32_t)))); \ No newline at end of file diff --git a/applications/examples/example_plugins_advanced/application.fam b/applications/examples/example_plugins_advanced/application.fam new file mode 100644 index 000000000..d40c0dde2 --- /dev/null +++ b/applications/examples/example_plugins_advanced/application.fam @@ -0,0 +1,24 @@ +App( + appid="example_advanced_plugins", + name="Example: advanced plugins", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_advanced_plugins_app", + stack_size=2 * 1024, + fap_category="Examples", +) + +App( + appid="advanced_plugin1", + apptype=FlipperAppType.PLUGIN, + entry_point="advanced_plugin1_ep", + requires=["example_advanced_plugins"], + sources=["plugin1.c"], +) + +App( + appid="advanced_plugin2", + apptype=FlipperAppType.PLUGIN, + entry_point="advanced_plugin2_ep", + requires=["example_advanced_plugins"], + sources=["plugin2.c"], +) diff --git a/applications/examples/example_plugins_advanced/example_advanced_plugins.c b/applications/examples/example_plugins_advanced/example_advanced_plugins.c new file mode 100644 index 000000000..f27b0a084 --- /dev/null +++ b/applications/examples/example_plugins_advanced/example_advanced_plugins.c @@ -0,0 +1,48 @@ +#include "app_api.h" +#include "plugin_interface.h" +#include "app_api_interface.h" + +#include +#include +#include + +#include + +#define TAG "example_advanced_plugins" + +int32_t example_advanced_plugins_app(void* p) { + UNUSED(p); + + FURI_LOG_I(TAG, "Starting"); + + CompositeApiResolver* resolver = composite_api_resolver_alloc(); + composite_api_resolver_add(resolver, firmware_api_interface); + composite_api_resolver_add(resolver, application_api_interface); + + PluginManager* manager = plugin_manager_alloc( + PLUGIN_APP_ID, PLUGIN_API_VERSION, composite_api_resolver_get(resolver)); + + do { + if(plugin_manager_load_all(manager, APP_DATA_PATH("plugins")) != PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load all libs"); + break; + } + + uint32_t plugin_count = plugin_manager_get_count(manager); + FURI_LOG_I(TAG, "Loaded libs: %lu", plugin_count); + + for(uint32_t i = 0; i < plugin_count; i++) { + const AdvancedPlugin* plugin = plugin_manager_get_ep(manager, i); + FURI_LOG_I(TAG, "plugin name: %s. Calling methods", plugin->name); + plugin->method1(228); + plugin->method2(); + FURI_LOG_I(TAG, "Accumulator: %lu", app_api_accumulator_get()); + } + } while(0); + + plugin_manager_free(manager); + composite_api_resolver_free(resolver); + FURI_LOG_I(TAG, "Goodbye!"); + + return 0; +} diff --git a/applications/examples/example_plugins_advanced/plugin1.c b/applications/examples/example_plugins_advanced/plugin1.c new file mode 100644 index 000000000..bf0ab50b4 --- /dev/null +++ b/applications/examples/example_plugins_advanced/plugin1.c @@ -0,0 +1,40 @@ +/* + * This plugin uses both firmware's API interface and private application headers. + * It can be loaded by a plugin manager that uses CompoundApiInterface, + * which combines both interfaces. + */ + +#include "app_api.h" +#include "plugin_interface.h" + +#include +#include + +static void advanced_plugin1_method1(int arg1) { + /* This function is implemented inside host application */ + app_api_accumulator_add(arg1); +} + +static void advanced_plugin1_method2() { + /* Accumulator value is stored inside host application */ + FURI_LOG_I("TEST", "Plugin 1, accumulator: %lu", app_api_accumulator_get()); +} + +/* Actual implementation of app<>plugin interface */ +static const AdvancedPlugin advanced_plugin1 = { + .name = "Advanced Plugin 1", + .method1 = &advanced_plugin1_method1, + .method2 = &advanced_plugin1_method2, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor advanced_plugin1_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &advanced_plugin1, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* advanced_plugin1_ep() { + return &advanced_plugin1_descriptor; +} diff --git a/applications/examples/example_plugins_advanced/plugin2.c b/applications/examples/example_plugins_advanced/plugin2.c new file mode 100644 index 000000000..f0b2f726d --- /dev/null +++ b/applications/examples/example_plugins_advanced/plugin2.c @@ -0,0 +1,40 @@ +/* + * This plugin uses both firmware's API interface and private application headers. + * It can be loaded by a plugin manager that uses CompoundApiInterface, + * which combines both interfaces. + */ + +#include "app_api.h" +#include "plugin_interface.h" + +#include +#include + +static void advanced_plugin2_method1(int arg1) { + /* This function is implemented inside host application */ + app_api_accumulator_mul(arg1); +} + +static void advanced_plugin2_method2() { + /* Accumulator value is stored inside host application */ + FURI_LOG_I("TEST", "Plugin 2, accumulator: %lu", app_api_accumulator_get()); +} + +/* Actual implementation of app<>plugin interface */ +static const AdvancedPlugin advanced_plugin2 = { + .name = "Advanced Plugin 2", + .method1 = &advanced_plugin2_method1, + .method2 = &advanced_plugin2_method2, +}; + +/* Plugin descriptor to comply with basic plugin specification */ +static const FlipperAppPluginDescriptor advanced_plugin2_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &advanced_plugin2, +}; + +/* Plugin entry point - must return a pointer to const descriptor */ +const FlipperAppPluginDescriptor* advanced_plugin2_ep() { + return &advanced_plugin2_descriptor; +} diff --git a/applications/examples/example_plugins_advanced/plugin_interface.h b/applications/examples/example_plugins_advanced/plugin_interface.h new file mode 100644 index 000000000..e8b5a22d6 --- /dev/null +++ b/applications/examples/example_plugins_advanced/plugin_interface.h @@ -0,0 +1,12 @@ +#pragma once + +/* Common interface between a plugin and host applicaion */ + +#define PLUGIN_APP_ID "example_plugins_advanced" +#define PLUGIN_API_VERSION 1 + +typedef struct { + const char* name; + void (*method1)(int); + void (*method2)(); +} AdvancedPlugin; diff --git a/applications/external/application.fam b/applications/external/application.fam new file mode 100644 index 000000000..12dc1cc1a --- /dev/null +++ b/applications/external/application.fam @@ -0,0 +1,6 @@ +# Placeholder +App( + appid="external_apps", + name="External apps bundle", + apptype=FlipperAppType.METAPACKAGE, +) diff --git a/applications/plugins/clock/application.fam b/applications/external/clock/application.fam similarity index 82% rename from applications/plugins/clock/application.fam rename to applications/external/clock/application.fam index 590f5dfe0..a6a2eff3e 100644 --- a/applications/plugins/clock/application.fam +++ b/applications/external/clock/application.fam @@ -1,7 +1,7 @@ App( appid="clock", name="Clock", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="clock_app", requires=["gui"], stack_size=2 * 1024, diff --git a/applications/plugins/clock/clock.png b/applications/external/clock/clock.png similarity index 100% rename from applications/plugins/clock/clock.png rename to applications/external/clock/clock.png diff --git a/applications/plugins/clock/clock_app.c b/applications/external/clock/clock_app.c similarity index 100% rename from applications/plugins/clock/clock_app.c rename to applications/external/clock/clock_app.c diff --git a/applications/plugins/dap_link/README.md b/applications/external/dap_link/README.md similarity index 100% rename from applications/plugins/dap_link/README.md rename to applications/external/dap_link/README.md diff --git a/applications/plugins/dap_link/application.fam b/applications/external/dap_link/application.fam similarity index 92% rename from applications/plugins/dap_link/application.fam rename to applications/external/dap_link/application.fam index 711e4833d..017143803 100644 --- a/applications/plugins/dap_link/application.fam +++ b/applications/external/dap_link/application.fam @@ -1,7 +1,7 @@ App( appid="dap_link", name="DAP Link", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="dap_link_app", requires=[ "gui", diff --git a/applications/plugins/dap_link/dap_config.h b/applications/external/dap_link/dap_config.h similarity index 100% rename from applications/plugins/dap_link/dap_config.h rename to applications/external/dap_link/dap_config.h diff --git a/applications/plugins/dap_link/dap_link.c b/applications/external/dap_link/dap_link.c similarity index 100% rename from applications/plugins/dap_link/dap_link.c rename to applications/external/dap_link/dap_link.c diff --git a/applications/plugins/dap_link/dap_link.h b/applications/external/dap_link/dap_link.h similarity index 100% rename from applications/plugins/dap_link/dap_link.h rename to applications/external/dap_link/dap_link.h diff --git a/applications/plugins/dap_link/dap_link.png b/applications/external/dap_link/dap_link.png similarity index 100% rename from applications/plugins/dap_link/dap_link.png rename to applications/external/dap_link/dap_link.png diff --git a/applications/plugins/dap_link/gui/dap_gui.c b/applications/external/dap_link/gui/dap_gui.c similarity index 100% rename from applications/plugins/dap_link/gui/dap_gui.c rename to applications/external/dap_link/gui/dap_gui.c diff --git a/applications/plugins/dap_link/gui/dap_gui.h b/applications/external/dap_link/gui/dap_gui.h similarity index 100% rename from applications/plugins/dap_link/gui/dap_gui.h rename to applications/external/dap_link/gui/dap_gui.h diff --git a/applications/plugins/dap_link/gui/dap_gui_custom_event.h b/applications/external/dap_link/gui/dap_gui_custom_event.h similarity index 100% rename from applications/plugins/dap_link/gui/dap_gui_custom_event.h rename to applications/external/dap_link/gui/dap_gui_custom_event.h diff --git a/applications/plugins/dap_link/gui/dap_gui_i.h b/applications/external/dap_link/gui/dap_gui_i.h similarity index 100% rename from applications/plugins/dap_link/gui/dap_gui_i.h rename to applications/external/dap_link/gui/dap_gui_i.h diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene.c b/applications/external/dap_link/gui/scenes/config/dap_scene.c similarity index 100% rename from applications/plugins/dap_link/gui/scenes/config/dap_scene.c rename to applications/external/dap_link/gui/scenes/config/dap_scene.c diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene.h b/applications/external/dap_link/gui/scenes/config/dap_scene.h similarity index 100% rename from applications/plugins/dap_link/gui/scenes/config/dap_scene.h rename to applications/external/dap_link/gui/scenes/config/dap_scene.h diff --git a/applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h b/applications/external/dap_link/gui/scenes/config/dap_scene_config.h similarity index 100% rename from applications/plugins/dap_link/gui/scenes/config/dap_scene_config.h rename to applications/external/dap_link/gui/scenes/config/dap_scene_config.h diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_about.c b/applications/external/dap_link/gui/scenes/dap_scene_about.c similarity index 100% rename from applications/plugins/dap_link/gui/scenes/dap_scene_about.c rename to applications/external/dap_link/gui/scenes/dap_scene_about.c diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_config.c b/applications/external/dap_link/gui/scenes/dap_scene_config.c similarity index 100% rename from applications/plugins/dap_link/gui/scenes/dap_scene_config.c rename to applications/external/dap_link/gui/scenes/dap_scene_config.c diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_help.c b/applications/external/dap_link/gui/scenes/dap_scene_help.c similarity index 100% rename from applications/plugins/dap_link/gui/scenes/dap_scene_help.c rename to applications/external/dap_link/gui/scenes/dap_scene_help.c diff --git a/applications/plugins/dap_link/gui/scenes/dap_scene_main.c b/applications/external/dap_link/gui/scenes/dap_scene_main.c similarity index 100% rename from applications/plugins/dap_link/gui/scenes/dap_scene_main.c rename to applications/external/dap_link/gui/scenes/dap_scene_main.c diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.c b/applications/external/dap_link/gui/views/dap_main_view.c similarity index 100% rename from applications/plugins/dap_link/gui/views/dap_main_view.c rename to applications/external/dap_link/gui/views/dap_main_view.c diff --git a/applications/plugins/dap_link/gui/views/dap_main_view.h b/applications/external/dap_link/gui/views/dap_main_view.h similarity index 100% rename from applications/plugins/dap_link/gui/views/dap_main_view.h rename to applications/external/dap_link/gui/views/dap_main_view.h diff --git a/applications/plugins/dap_link/icons/ActiveConnection_50x64.png b/applications/external/dap_link/icons/ActiveConnection_50x64.png similarity index 100% rename from applications/plugins/dap_link/icons/ActiveConnection_50x64.png rename to applications/external/dap_link/icons/ActiveConnection_50x64.png diff --git a/applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png b/applications/external/dap_link/icons/ArrowDownEmpty_12x18.png similarity index 100% rename from applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png rename to applications/external/dap_link/icons/ArrowDownEmpty_12x18.png diff --git a/applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png b/applications/external/dap_link/icons/ArrowDownFilled_12x18.png similarity index 100% rename from applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png rename to applications/external/dap_link/icons/ArrowDownFilled_12x18.png diff --git a/applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png b/applications/external/dap_link/icons/ArrowUpEmpty_12x18.png similarity index 100% rename from applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png rename to applications/external/dap_link/icons/ArrowUpEmpty_12x18.png diff --git a/applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png b/applications/external/dap_link/icons/ArrowUpFilled_12x18.png similarity index 100% rename from applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png rename to applications/external/dap_link/icons/ArrowUpFilled_12x18.png diff --git a/applications/plugins/dap_link/lib/free-dap b/applications/external/dap_link/lib/free-dap similarity index 100% rename from applications/plugins/dap_link/lib/free-dap rename to applications/external/dap_link/lib/free-dap diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.c b/applications/external/dap_link/usb/dap_v2_usb.c similarity index 100% rename from applications/plugins/dap_link/usb/dap_v2_usb.c rename to applications/external/dap_link/usb/dap_v2_usb.c diff --git a/applications/plugins/dap_link/usb/dap_v2_usb.h b/applications/external/dap_link/usb/dap_v2_usb.h similarity index 100% rename from applications/plugins/dap_link/usb/dap_v2_usb.h rename to applications/external/dap_link/usb/dap_v2_usb.h diff --git a/applications/plugins/dap_link/usb/usb_winusb.h b/applications/external/dap_link/usb/usb_winusb.h similarity index 100% rename from applications/plugins/dap_link/usb/usb_winusb.h rename to applications/external/dap_link/usb/usb_winusb.h diff --git a/applications/plugins/hid_app/application.fam b/applications/external/hid_app/application.fam similarity index 86% rename from applications/plugins/hid_app/application.fam rename to applications/external/hid_app/application.fam index b6e4e3bf8..a9d8305dd 100644 --- a/applications/plugins/hid_app/application.fam +++ b/applications/external/hid_app/application.fam @@ -1,7 +1,7 @@ App( appid="hid_usb", name="Remote", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="hid_usb_app", stack_size=1 * 1024, fap_category="USB", @@ -14,7 +14,7 @@ App( App( appid="hid_ble", name="Remote", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="hid_ble_app", stack_size=1 * 1024, fap_category="Bluetooth", diff --git a/applications/plugins/hid_app/assets/Arr_dwn_7x9.png b/applications/external/hid_app/assets/Arr_dwn_7x9.png similarity index 100% rename from applications/plugins/hid_app/assets/Arr_dwn_7x9.png rename to applications/external/hid_app/assets/Arr_dwn_7x9.png diff --git a/applications/plugins/hid_app/assets/Arr_up_7x9.png b/applications/external/hid_app/assets/Arr_up_7x9.png similarity index 100% rename from applications/plugins/hid_app/assets/Arr_up_7x9.png rename to applications/external/hid_app/assets/Arr_up_7x9.png diff --git a/applications/plugins/hid_app/assets/Ble_connected_15x15.png b/applications/external/hid_app/assets/Ble_connected_15x15.png similarity index 100% rename from applications/plugins/hid_app/assets/Ble_connected_15x15.png rename to applications/external/hid_app/assets/Ble_connected_15x15.png diff --git a/applications/plugins/hid_app/assets/Ble_disconnected_15x15.png b/applications/external/hid_app/assets/Ble_disconnected_15x15.png similarity index 100% rename from applications/plugins/hid_app/assets/Ble_disconnected_15x15.png rename to applications/external/hid_app/assets/Ble_disconnected_15x15.png diff --git a/applications/plugins/hid_app/assets/ButtonDown_7x4.png b/applications/external/hid_app/assets/ButtonDown_7x4.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonDown_7x4.png rename to applications/external/hid_app/assets/ButtonDown_7x4.png diff --git a/applications/plugins/hid_app/assets/ButtonF10_5x8.png b/applications/external/hid_app/assets/ButtonF10_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF10_5x8.png rename to applications/external/hid_app/assets/ButtonF10_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF11_5x8.png b/applications/external/hid_app/assets/ButtonF11_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF11_5x8.png rename to applications/external/hid_app/assets/ButtonF11_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF12_5x8.png b/applications/external/hid_app/assets/ButtonF12_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF12_5x8.png rename to applications/external/hid_app/assets/ButtonF12_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF1_5x8.png b/applications/external/hid_app/assets/ButtonF1_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF1_5x8.png rename to applications/external/hid_app/assets/ButtonF1_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF2_5x8.png b/applications/external/hid_app/assets/ButtonF2_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF2_5x8.png rename to applications/external/hid_app/assets/ButtonF2_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF3_5x8.png b/applications/external/hid_app/assets/ButtonF3_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF3_5x8.png rename to applications/external/hid_app/assets/ButtonF3_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF4_5x8.png b/applications/external/hid_app/assets/ButtonF4_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF4_5x8.png rename to applications/external/hid_app/assets/ButtonF4_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF5_5x8.png b/applications/external/hid_app/assets/ButtonF5_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF5_5x8.png rename to applications/external/hid_app/assets/ButtonF5_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF6_5x8.png b/applications/external/hid_app/assets/ButtonF6_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF6_5x8.png rename to applications/external/hid_app/assets/ButtonF6_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF7_5x8.png b/applications/external/hid_app/assets/ButtonF7_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF7_5x8.png rename to applications/external/hid_app/assets/ButtonF7_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF8_5x8.png b/applications/external/hid_app/assets/ButtonF8_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF8_5x8.png rename to applications/external/hid_app/assets/ButtonF8_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF9_5x8.png b/applications/external/hid_app/assets/ButtonF9_5x8.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonF9_5x8.png rename to applications/external/hid_app/assets/ButtonF9_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonLeft_4x7.png b/applications/external/hid_app/assets/ButtonLeft_4x7.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonLeft_4x7.png rename to applications/external/hid_app/assets/ButtonLeft_4x7.png diff --git a/applications/plugins/hid_app/assets/ButtonRight_4x7.png b/applications/external/hid_app/assets/ButtonRight_4x7.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonRight_4x7.png rename to applications/external/hid_app/assets/ButtonRight_4x7.png diff --git a/applications/plugins/hid_app/assets/ButtonUp_7x4.png b/applications/external/hid_app/assets/ButtonUp_7x4.png similarity index 100% rename from applications/plugins/hid_app/assets/ButtonUp_7x4.png rename to applications/external/hid_app/assets/ButtonUp_7x4.png diff --git a/applications/plugins/hid_app/assets/Button_18x18.png b/applications/external/hid_app/assets/Button_18x18.png similarity index 100% rename from applications/plugins/hid_app/assets/Button_18x18.png rename to applications/external/hid_app/assets/Button_18x18.png diff --git a/applications/plugins/hid_app/assets/Circles_47x47.png b/applications/external/hid_app/assets/Circles_47x47.png similarity index 100% rename from applications/plugins/hid_app/assets/Circles_47x47.png rename to applications/external/hid_app/assets/Circles_47x47.png diff --git a/applications/plugins/hid_app/assets/Left_mouse_icon_9x9.png b/applications/external/hid_app/assets/Left_mouse_icon_9x9.png similarity index 100% rename from applications/plugins/hid_app/assets/Left_mouse_icon_9x9.png rename to applications/external/hid_app/assets/Left_mouse_icon_9x9.png diff --git a/applications/plugins/hid_app/assets/Like_def_11x9.png b/applications/external/hid_app/assets/Like_def_11x9.png similarity index 100% rename from applications/plugins/hid_app/assets/Like_def_11x9.png rename to applications/external/hid_app/assets/Like_def_11x9.png diff --git a/applications/plugins/hid_app/assets/Like_pressed_17x17.png b/applications/external/hid_app/assets/Like_pressed_17x17.png similarity index 100% rename from applications/plugins/hid_app/assets/Like_pressed_17x17.png rename to applications/external/hid_app/assets/Like_pressed_17x17.png diff --git a/applications/plugins/hid_app/assets/Ok_btn_9x9.png b/applications/external/hid_app/assets/Ok_btn_9x9.png similarity index 100% rename from applications/plugins/hid_app/assets/Ok_btn_9x9.png rename to applications/external/hid_app/assets/Ok_btn_9x9.png diff --git a/applications/plugins/hid_app/assets/Ok_btn_pressed_13x13.png b/applications/external/hid_app/assets/Ok_btn_pressed_13x13.png similarity index 100% rename from applications/plugins/hid_app/assets/Ok_btn_pressed_13x13.png rename to applications/external/hid_app/assets/Ok_btn_pressed_13x13.png diff --git a/applications/plugins/hid_app/assets/Pin_arrow_down_7x9.png b/applications/external/hid_app/assets/Pin_arrow_down_7x9.png similarity index 100% rename from applications/plugins/hid_app/assets/Pin_arrow_down_7x9.png rename to applications/external/hid_app/assets/Pin_arrow_down_7x9.png diff --git a/applications/plugins/hid_app/assets/Pin_arrow_left_9x7.png b/applications/external/hid_app/assets/Pin_arrow_left_9x7.png similarity index 100% rename from applications/plugins/hid_app/assets/Pin_arrow_left_9x7.png rename to applications/external/hid_app/assets/Pin_arrow_left_9x7.png diff --git a/applications/plugins/hid_app/assets/Pin_arrow_right_9x7.png b/applications/external/hid_app/assets/Pin_arrow_right_9x7.png similarity index 100% rename from applications/plugins/hid_app/assets/Pin_arrow_right_9x7.png rename to applications/external/hid_app/assets/Pin_arrow_right_9x7.png diff --git a/applications/plugins/hid_app/assets/Pin_arrow_up_7x9.png b/applications/external/hid_app/assets/Pin_arrow_up_7x9.png similarity index 100% rename from applications/plugins/hid_app/assets/Pin_arrow_up_7x9.png rename to applications/external/hid_app/assets/Pin_arrow_up_7x9.png diff --git a/applications/plugins/hid_app/assets/Pin_back_arrow_10x8.png b/applications/external/hid_app/assets/Pin_back_arrow_10x8.png similarity index 100% rename from applications/plugins/hid_app/assets/Pin_back_arrow_10x8.png rename to applications/external/hid_app/assets/Pin_back_arrow_10x8.png diff --git a/applications/plugins/hid_app/assets/Pressed_Button_13x13.png b/applications/external/hid_app/assets/Pressed_Button_13x13.png similarity index 100% rename from applications/plugins/hid_app/assets/Pressed_Button_13x13.png rename to applications/external/hid_app/assets/Pressed_Button_13x13.png diff --git a/applications/plugins/hid_app/assets/Right_mouse_icon_9x9.png b/applications/external/hid_app/assets/Right_mouse_icon_9x9.png similarity index 100% rename from applications/plugins/hid_app/assets/Right_mouse_icon_9x9.png rename to applications/external/hid_app/assets/Right_mouse_icon_9x9.png diff --git a/applications/plugins/hid_app/assets/Space_65x18.png b/applications/external/hid_app/assets/Space_65x18.png similarity index 100% rename from applications/plugins/hid_app/assets/Space_65x18.png rename to applications/external/hid_app/assets/Space_65x18.png diff --git a/applications/plugins/hid_app/assets/Voldwn_6x6.png b/applications/external/hid_app/assets/Voldwn_6x6.png similarity index 100% rename from applications/plugins/hid_app/assets/Voldwn_6x6.png rename to applications/external/hid_app/assets/Voldwn_6x6.png diff --git a/applications/plugins/hid_app/assets/Volup_8x6.png b/applications/external/hid_app/assets/Volup_8x6.png similarity index 100% rename from applications/plugins/hid_app/assets/Volup_8x6.png rename to applications/external/hid_app/assets/Volup_8x6.png diff --git a/applications/plugins/hid_app/hid.c b/applications/external/hid_app/hid.c similarity index 100% rename from applications/plugins/hid_app/hid.c rename to applications/external/hid_app/hid.c diff --git a/applications/plugins/hid_app/hid.h b/applications/external/hid_app/hid.h similarity index 100% rename from applications/plugins/hid_app/hid.h rename to applications/external/hid_app/hid.h diff --git a/applications/plugins/hid_app/hid_ble_10px.png b/applications/external/hid_app/hid_ble_10px.png similarity index 100% rename from applications/plugins/hid_app/hid_ble_10px.png rename to applications/external/hid_app/hid_ble_10px.png diff --git a/applications/plugins/hid_app/hid_usb_10px.png b/applications/external/hid_app/hid_usb_10px.png similarity index 100% rename from applications/plugins/hid_app/hid_usb_10px.png rename to applications/external/hid_app/hid_usb_10px.png diff --git a/applications/plugins/hid_app/views.h b/applications/external/hid_app/views.h similarity index 100% rename from applications/plugins/hid_app/views.h rename to applications/external/hid_app/views.h diff --git a/applications/plugins/hid_app/views/hid_keyboard.c b/applications/external/hid_app/views/hid_keyboard.c similarity index 100% rename from applications/plugins/hid_app/views/hid_keyboard.c rename to applications/external/hid_app/views/hid_keyboard.c diff --git a/applications/plugins/hid_app/views/hid_keyboard.h b/applications/external/hid_app/views/hid_keyboard.h similarity index 100% rename from applications/plugins/hid_app/views/hid_keyboard.h rename to applications/external/hid_app/views/hid_keyboard.h diff --git a/applications/plugins/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c similarity index 100% rename from applications/plugins/hid_app/views/hid_keynote.c rename to applications/external/hid_app/views/hid_keynote.c diff --git a/applications/plugins/hid_app/views/hid_keynote.h b/applications/external/hid_app/views/hid_keynote.h similarity index 100% rename from applications/plugins/hid_app/views/hid_keynote.h rename to applications/external/hid_app/views/hid_keynote.h diff --git a/applications/plugins/hid_app/views/hid_media.c b/applications/external/hid_app/views/hid_media.c similarity index 100% rename from applications/plugins/hid_app/views/hid_media.c rename to applications/external/hid_app/views/hid_media.c diff --git a/applications/plugins/hid_app/views/hid_media.h b/applications/external/hid_app/views/hid_media.h similarity index 100% rename from applications/plugins/hid_app/views/hid_media.h rename to applications/external/hid_app/views/hid_media.h diff --git a/applications/plugins/hid_app/views/hid_mouse.c b/applications/external/hid_app/views/hid_mouse.c similarity index 100% rename from applications/plugins/hid_app/views/hid_mouse.c rename to applications/external/hid_app/views/hid_mouse.c diff --git a/applications/plugins/hid_app/views/hid_mouse.h b/applications/external/hid_app/views/hid_mouse.h similarity index 100% rename from applications/plugins/hid_app/views/hid_mouse.h rename to applications/external/hid_app/views/hid_mouse.h diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.c b/applications/external/hid_app/views/hid_mouse_jiggler.c similarity index 100% rename from applications/plugins/hid_app/views/hid_mouse_jiggler.c rename to applications/external/hid_app/views/hid_mouse_jiggler.c diff --git a/applications/plugins/hid_app/views/hid_mouse_jiggler.h b/applications/external/hid_app/views/hid_mouse_jiggler.h similarity index 100% rename from applications/plugins/hid_app/views/hid_mouse_jiggler.h rename to applications/external/hid_app/views/hid_mouse_jiggler.h diff --git a/applications/plugins/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c similarity index 100% rename from applications/plugins/hid_app/views/hid_tiktok.c rename to applications/external/hid_app/views/hid_tiktok.c diff --git a/applications/plugins/hid_app/views/hid_tiktok.h b/applications/external/hid_app/views/hid_tiktok.h similarity index 100% rename from applications/plugins/hid_app/views/hid_tiktok.h rename to applications/external/hid_app/views/hid_tiktok.h diff --git a/applications/plugins/music_player/application.fam b/applications/external/music_player/application.fam similarity index 87% rename from applications/plugins/music_player/application.fam rename to applications/external/music_player/application.fam index c51abf194..3414c0a48 100644 --- a/applications/plugins/music_player/application.fam +++ b/applications/external/music_player/application.fam @@ -1,9 +1,8 @@ App( appid="music_player", name="Music Player", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="music_player_app", - cdefines=["APP_MUSIC_PLAYER"], requires=[ "gui", "dialogs", diff --git a/applications/plugins/music_player/icons/music_10px.png b/applications/external/music_player/icons/music_10px.png similarity index 100% rename from applications/plugins/music_player/icons/music_10px.png rename to applications/external/music_player/icons/music_10px.png diff --git a/applications/plugins/music_player/music_player.c b/applications/external/music_player/music_player.c similarity index 100% rename from applications/plugins/music_player/music_player.c rename to applications/external/music_player/music_player.c diff --git a/applications/plugins/music_player/music_player_cli.c b/applications/external/music_player/music_player_cli.c similarity index 100% rename from applications/plugins/music_player/music_player_cli.c rename to applications/external/music_player/music_player_cli.c diff --git a/applications/plugins/music_player/music_player_worker.c b/applications/external/music_player/music_player_worker.c similarity index 100% rename from applications/plugins/music_player/music_player_worker.c rename to applications/external/music_player/music_player_worker.c diff --git a/applications/plugins/music_player/music_player_worker.h b/applications/external/music_player/music_player_worker.h similarity index 100% rename from applications/plugins/music_player/music_player_worker.h rename to applications/external/music_player/music_player_worker.h diff --git a/applications/plugins/nfc_magic/application.fam b/applications/external/nfc_magic/application.fam similarity index 100% rename from applications/plugins/nfc_magic/application.fam rename to applications/external/nfc_magic/application.fam diff --git a/applications/plugins/nfc_magic/assets/DolphinCommon_56x48.png b/applications/external/nfc_magic/assets/DolphinCommon_56x48.png similarity index 100% rename from applications/plugins/nfc_magic/assets/DolphinCommon_56x48.png rename to applications/external/nfc_magic/assets/DolphinCommon_56x48.png diff --git a/applications/plugins/nfc_magic/assets/DolphinNice_96x59.png b/applications/external/nfc_magic/assets/DolphinNice_96x59.png similarity index 100% rename from applications/plugins/nfc_magic/assets/DolphinNice_96x59.png rename to applications/external/nfc_magic/assets/DolphinNice_96x59.png diff --git a/applications/plugins/nfc_magic/assets/Loading_24.png b/applications/external/nfc_magic/assets/Loading_24.png similarity index 100% rename from applications/plugins/nfc_magic/assets/Loading_24.png rename to applications/external/nfc_magic/assets/Loading_24.png diff --git a/applications/plugins/nfc_magic/assets/NFC_manual_60x50.png b/applications/external/nfc_magic/assets/NFC_manual_60x50.png similarity index 100% rename from applications/plugins/nfc_magic/assets/NFC_manual_60x50.png rename to applications/external/nfc_magic/assets/NFC_manual_60x50.png diff --git a/applications/plugins/nfc_magic/lib/magic/magic.c b/applications/external/nfc_magic/lib/magic/magic.c similarity index 100% rename from applications/plugins/nfc_magic/lib/magic/magic.c rename to applications/external/nfc_magic/lib/magic/magic.c diff --git a/applications/plugins/nfc_magic/lib/magic/magic.h b/applications/external/nfc_magic/lib/magic/magic.h similarity index 100% rename from applications/plugins/nfc_magic/lib/magic/magic.h rename to applications/external/nfc_magic/lib/magic/magic.h diff --git a/applications/plugins/nfc_magic/nfc_magic.c b/applications/external/nfc_magic/nfc_magic.c similarity index 100% rename from applications/plugins/nfc_magic/nfc_magic.c rename to applications/external/nfc_magic/nfc_magic.c diff --git a/applications/plugins/nfc_magic/nfc_magic.h b/applications/external/nfc_magic/nfc_magic.h similarity index 100% rename from applications/plugins/nfc_magic/nfc_magic.h rename to applications/external/nfc_magic/nfc_magic.h diff --git a/applications/plugins/nfc_magic/nfc_magic_i.h b/applications/external/nfc_magic/nfc_magic_i.h similarity index 100% rename from applications/plugins/nfc_magic/nfc_magic_i.h rename to applications/external/nfc_magic/nfc_magic_i.h diff --git a/applications/plugins/nfc_magic/nfc_magic_worker.c b/applications/external/nfc_magic/nfc_magic_worker.c similarity index 100% rename from applications/plugins/nfc_magic/nfc_magic_worker.c rename to applications/external/nfc_magic/nfc_magic_worker.c diff --git a/applications/plugins/nfc_magic/nfc_magic_worker.h b/applications/external/nfc_magic/nfc_magic_worker.h similarity index 100% rename from applications/plugins/nfc_magic/nfc_magic_worker.h rename to applications/external/nfc_magic/nfc_magic_worker.h diff --git a/applications/plugins/nfc_magic/nfc_magic_worker_i.h b/applications/external/nfc_magic/nfc_magic_worker_i.h similarity index 100% rename from applications/plugins/nfc_magic/nfc_magic_worker_i.h rename to applications/external/nfc_magic/nfc_magic_worker_i.h diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene.c b/applications/external/nfc_magic/scenes/nfc_magic_scene.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene.h b/applications/external/nfc_magic/scenes/nfc_magic_scene.h similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene.h rename to applications/external/nfc_magic/scenes/nfc_magic_scene.h diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_check.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_check.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_check.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_check.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_config.h b/applications/external/nfc_magic/scenes/nfc_magic_scene_config.h similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_config.h rename to applications/external/nfc_magic/scenes/nfc_magic_scene_config.h diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_file_select.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_magic_info.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_magic_info.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_not_magic.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_not_magic.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_start.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_start.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_success.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_success.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_success.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_success.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_write.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_write.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_confirm.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write_confirm.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_confirm.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_write_confirm.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_fail.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write_fail.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_write_fail.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_write_fail.c diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c similarity index 100% rename from applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c rename to applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c diff --git a/applications/plugins/picopass/125_10px.png b/applications/external/picopass/125_10px.png similarity index 100% rename from applications/plugins/picopass/125_10px.png rename to applications/external/picopass/125_10px.png diff --git a/applications/plugins/picopass/application.fam b/applications/external/picopass/application.fam similarity index 100% rename from applications/plugins/picopass/application.fam rename to applications/external/picopass/application.fam diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.c b/applications/external/picopass/helpers/iclass_elite_dict.c similarity index 100% rename from applications/plugins/picopass/helpers/iclass_elite_dict.c rename to applications/external/picopass/helpers/iclass_elite_dict.c diff --git a/applications/plugins/picopass/helpers/iclass_elite_dict.h b/applications/external/picopass/helpers/iclass_elite_dict.h similarity index 100% rename from applications/plugins/picopass/helpers/iclass_elite_dict.h rename to applications/external/picopass/helpers/iclass_elite_dict.h diff --git a/applications/plugins/picopass/icons/DolphinMafia_115x62.png b/applications/external/picopass/icons/DolphinMafia_115x62.png similarity index 100% rename from applications/plugins/picopass/icons/DolphinMafia_115x62.png rename to applications/external/picopass/icons/DolphinMafia_115x62.png diff --git a/applications/plugins/picopass/icons/DolphinNice_96x59.png b/applications/external/picopass/icons/DolphinNice_96x59.png similarity index 100% rename from applications/plugins/picopass/icons/DolphinNice_96x59.png rename to applications/external/picopass/icons/DolphinNice_96x59.png diff --git a/applications/plugins/picopass/icons/Nfc_10px.png b/applications/external/picopass/icons/Nfc_10px.png similarity index 100% rename from applications/plugins/picopass/icons/Nfc_10px.png rename to applications/external/picopass/icons/Nfc_10px.png diff --git a/applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png b/applications/external/picopass/icons/RFIDDolphinReceive_97x61.png similarity index 100% rename from applications/plugins/picopass/icons/RFIDDolphinReceive_97x61.png rename to applications/external/picopass/icons/RFIDDolphinReceive_97x61.png diff --git a/applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png b/applications/external/picopass/icons/RFIDDolphinSend_97x61.png similarity index 100% rename from applications/plugins/picopass/icons/RFIDDolphinSend_97x61.png rename to applications/external/picopass/icons/RFIDDolphinSend_97x61.png diff --git a/applications/plugins/picopass/lib/loclass/optimized_cipher.c b/applications/external/picopass/lib/loclass/optimized_cipher.c similarity index 100% rename from applications/plugins/picopass/lib/loclass/optimized_cipher.c rename to applications/external/picopass/lib/loclass/optimized_cipher.c diff --git a/applications/plugins/picopass/lib/loclass/optimized_cipher.h b/applications/external/picopass/lib/loclass/optimized_cipher.h similarity index 100% rename from applications/plugins/picopass/lib/loclass/optimized_cipher.h rename to applications/external/picopass/lib/loclass/optimized_cipher.h diff --git a/applications/plugins/picopass/lib/loclass/optimized_cipherutils.c b/applications/external/picopass/lib/loclass/optimized_cipherutils.c similarity index 100% rename from applications/plugins/picopass/lib/loclass/optimized_cipherutils.c rename to applications/external/picopass/lib/loclass/optimized_cipherutils.c diff --git a/applications/plugins/picopass/lib/loclass/optimized_cipherutils.h b/applications/external/picopass/lib/loclass/optimized_cipherutils.h similarity index 100% rename from applications/plugins/picopass/lib/loclass/optimized_cipherutils.h rename to applications/external/picopass/lib/loclass/optimized_cipherutils.h diff --git a/applications/plugins/picopass/lib/loclass/optimized_elite.c b/applications/external/picopass/lib/loclass/optimized_elite.c similarity index 100% rename from applications/plugins/picopass/lib/loclass/optimized_elite.c rename to applications/external/picopass/lib/loclass/optimized_elite.c diff --git a/applications/plugins/picopass/lib/loclass/optimized_elite.h b/applications/external/picopass/lib/loclass/optimized_elite.h similarity index 100% rename from applications/plugins/picopass/lib/loclass/optimized_elite.h rename to applications/external/picopass/lib/loclass/optimized_elite.h diff --git a/applications/plugins/picopass/lib/loclass/optimized_ikeys.c b/applications/external/picopass/lib/loclass/optimized_ikeys.c similarity index 100% rename from applications/plugins/picopass/lib/loclass/optimized_ikeys.c rename to applications/external/picopass/lib/loclass/optimized_ikeys.c diff --git a/applications/plugins/picopass/lib/loclass/optimized_ikeys.h b/applications/external/picopass/lib/loclass/optimized_ikeys.h similarity index 100% rename from applications/plugins/picopass/lib/loclass/optimized_ikeys.h rename to applications/external/picopass/lib/loclass/optimized_ikeys.h diff --git a/applications/plugins/picopass/picopass.c b/applications/external/picopass/picopass.c similarity index 100% rename from applications/plugins/picopass/picopass.c rename to applications/external/picopass/picopass.c diff --git a/applications/plugins/picopass/picopass.h b/applications/external/picopass/picopass.h similarity index 100% rename from applications/plugins/picopass/picopass.h rename to applications/external/picopass/picopass.h diff --git a/applications/plugins/picopass/picopass_device.c b/applications/external/picopass/picopass_device.c similarity index 100% rename from applications/plugins/picopass/picopass_device.c rename to applications/external/picopass/picopass_device.c diff --git a/applications/plugins/picopass/picopass_device.h b/applications/external/picopass/picopass_device.h similarity index 100% rename from applications/plugins/picopass/picopass_device.h rename to applications/external/picopass/picopass_device.h diff --git a/applications/plugins/picopass/picopass_i.h b/applications/external/picopass/picopass_i.h similarity index 100% rename from applications/plugins/picopass/picopass_i.h rename to applications/external/picopass/picopass_i.h diff --git a/applications/plugins/picopass/picopass_keys.c b/applications/external/picopass/picopass_keys.c similarity index 100% rename from applications/plugins/picopass/picopass_keys.c rename to applications/external/picopass/picopass_keys.c diff --git a/applications/plugins/picopass/picopass_keys.h b/applications/external/picopass/picopass_keys.h similarity index 100% rename from applications/plugins/picopass/picopass_keys.h rename to applications/external/picopass/picopass_keys.h diff --git a/applications/plugins/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c similarity index 100% rename from applications/plugins/picopass/picopass_worker.c rename to applications/external/picopass/picopass_worker.c diff --git a/applications/plugins/picopass/picopass_worker.h b/applications/external/picopass/picopass_worker.h similarity index 100% rename from applications/plugins/picopass/picopass_worker.h rename to applications/external/picopass/picopass_worker.h diff --git a/applications/plugins/picopass/picopass_worker_i.h b/applications/external/picopass/picopass_worker_i.h similarity index 100% rename from applications/plugins/picopass/picopass_worker_i.h rename to applications/external/picopass/picopass_worker_i.h diff --git a/applications/plugins/picopass/rfal_picopass.c b/applications/external/picopass/rfal_picopass.c similarity index 100% rename from applications/plugins/picopass/rfal_picopass.c rename to applications/external/picopass/rfal_picopass.c diff --git a/applications/plugins/picopass/rfal_picopass.h b/applications/external/picopass/rfal_picopass.h similarity index 100% rename from applications/plugins/picopass/rfal_picopass.h rename to applications/external/picopass/rfal_picopass.h diff --git a/applications/plugins/picopass/scenes/picopass_scene.c b/applications/external/picopass/scenes/picopass_scene.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene.c rename to applications/external/picopass/scenes/picopass_scene.c diff --git a/applications/plugins/picopass/scenes/picopass_scene.h b/applications/external/picopass/scenes/picopass_scene.h similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene.h rename to applications/external/picopass/scenes/picopass_scene.h diff --git a/applications/plugins/picopass/scenes/picopass_scene_card_menu.c b/applications/external/picopass/scenes/picopass_scene_card_menu.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_card_menu.c rename to applications/external/picopass/scenes/picopass_scene_card_menu.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_config.h b/applications/external/picopass/scenes/picopass_scene_config.h similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_config.h rename to applications/external/picopass/scenes/picopass_scene_config.h diff --git a/applications/plugins/picopass/scenes/picopass_scene_delete.c b/applications/external/picopass/scenes/picopass_scene_delete.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_delete.c rename to applications/external/picopass/scenes/picopass_scene_delete.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_delete_success.c b/applications/external/picopass/scenes/picopass_scene_delete_success.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_delete_success.c rename to applications/external/picopass/scenes/picopass_scene_delete_success.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_device_info.c rename to applications/external/picopass/scenes/picopass_scene_device_info.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_file_select.c b/applications/external/picopass/scenes/picopass_scene_file_select.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_file_select.c rename to applications/external/picopass/scenes/picopass_scene_file_select.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_key_menu.c b/applications/external/picopass/scenes/picopass_scene_key_menu.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_key_menu.c rename to applications/external/picopass/scenes/picopass_scene_key_menu.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card.c b/applications/external/picopass/scenes/picopass_scene_read_card.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_read_card.c rename to applications/external/picopass/scenes/picopass_scene_read_card.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_read_card_success.c rename to applications/external/picopass/scenes/picopass_scene_read_card_success.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_read_factory_success.c rename to applications/external/picopass/scenes/picopass_scene_read_factory_success.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_save_name.c b/applications/external/picopass/scenes/picopass_scene_save_name.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_save_name.c rename to applications/external/picopass/scenes/picopass_scene_save_name.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_save_success.c b/applications/external/picopass/scenes/picopass_scene_save_success.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_save_success.c rename to applications/external/picopass/scenes/picopass_scene_save_success.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_saved_menu.c b/applications/external/picopass/scenes/picopass_scene_saved_menu.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_saved_menu.c rename to applications/external/picopass/scenes/picopass_scene_saved_menu.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_start.c b/applications/external/picopass/scenes/picopass_scene_start.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_start.c rename to applications/external/picopass/scenes/picopass_scene_start.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_card.c b/applications/external/picopass/scenes/picopass_scene_write_card.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_write_card.c rename to applications/external/picopass/scenes/picopass_scene_write_card.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_card_success.c b/applications/external/picopass/scenes/picopass_scene_write_card_success.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_write_card_success.c rename to applications/external/picopass/scenes/picopass_scene_write_card_success.c diff --git a/applications/plugins/picopass/scenes/picopass_scene_write_key.c b/applications/external/picopass/scenes/picopass_scene_write_key.c similarity index 100% rename from applications/plugins/picopass/scenes/picopass_scene_write_key.c rename to applications/external/picopass/scenes/picopass_scene_write_key.c diff --git a/applications/plugins/signal_generator/application.fam b/applications/external/signal_generator/application.fam similarity index 78% rename from applications/plugins/signal_generator/application.fam rename to applications/external/signal_generator/application.fam index 60f8deffb..094e784cc 100644 --- a/applications/plugins/signal_generator/application.fam +++ b/applications/external/signal_generator/application.fam @@ -1,9 +1,8 @@ App( appid="signal_generator", name="Signal Generator", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="signal_gen_app", - cdefines=["APP_SIGNAL_GEN"], requires=["gui"], stack_size=1 * 1024, order=50, diff --git a/applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png b/applications/external/signal_generator/icons/SmallArrowDown_3x5.png similarity index 100% rename from applications/plugins/signal_generator/icons/SmallArrowDown_3x5.png rename to applications/external/signal_generator/icons/SmallArrowDown_3x5.png diff --git a/applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png b/applications/external/signal_generator/icons/SmallArrowUp_3x5.png similarity index 100% rename from applications/plugins/signal_generator/icons/SmallArrowUp_3x5.png rename to applications/external/signal_generator/icons/SmallArrowUp_3x5.png diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene.c b/applications/external/signal_generator/scenes/signal_gen_scene.c similarity index 100% rename from applications/plugins/signal_generator/scenes/signal_gen_scene.c rename to applications/external/signal_generator/scenes/signal_gen_scene.c diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene.h b/applications/external/signal_generator/scenes/signal_gen_scene.h similarity index 100% rename from applications/plugins/signal_generator/scenes/signal_gen_scene.h rename to applications/external/signal_generator/scenes/signal_gen_scene.h diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene_config.h b/applications/external/signal_generator/scenes/signal_gen_scene_config.h similarity index 100% rename from applications/plugins/signal_generator/scenes/signal_gen_scene_config.h rename to applications/external/signal_generator/scenes/signal_gen_scene_config.h diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene_mco.c b/applications/external/signal_generator/scenes/signal_gen_scene_mco.c similarity index 100% rename from applications/plugins/signal_generator/scenes/signal_gen_scene_mco.c rename to applications/external/signal_generator/scenes/signal_gen_scene_mco.c diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c similarity index 100% rename from applications/plugins/signal_generator/scenes/signal_gen_scene_pwm.c rename to applications/external/signal_generator/scenes/signal_gen_scene_pwm.c diff --git a/applications/plugins/signal_generator/scenes/signal_gen_scene_start.c b/applications/external/signal_generator/scenes/signal_gen_scene_start.c similarity index 100% rename from applications/plugins/signal_generator/scenes/signal_gen_scene_start.c rename to applications/external/signal_generator/scenes/signal_gen_scene_start.c diff --git a/applications/plugins/signal_generator/signal_gen_10px.png b/applications/external/signal_generator/signal_gen_10px.png similarity index 100% rename from applications/plugins/signal_generator/signal_gen_10px.png rename to applications/external/signal_generator/signal_gen_10px.png diff --git a/applications/plugins/signal_generator/signal_gen_app.c b/applications/external/signal_generator/signal_gen_app.c similarity index 100% rename from applications/plugins/signal_generator/signal_gen_app.c rename to applications/external/signal_generator/signal_gen_app.c diff --git a/applications/plugins/signal_generator/signal_gen_app_i.h b/applications/external/signal_generator/signal_gen_app_i.h similarity index 100% rename from applications/plugins/signal_generator/signal_gen_app_i.h rename to applications/external/signal_generator/signal_gen_app_i.h diff --git a/applications/plugins/signal_generator/views/signal_gen_pwm.c b/applications/external/signal_generator/views/signal_gen_pwm.c similarity index 100% rename from applications/plugins/signal_generator/views/signal_gen_pwm.c rename to applications/external/signal_generator/views/signal_gen_pwm.c diff --git a/applications/plugins/signal_generator/views/signal_gen_pwm.h b/applications/external/signal_generator/views/signal_gen_pwm.h similarity index 100% rename from applications/plugins/signal_generator/views/signal_gen_pwm.h rename to applications/external/signal_generator/views/signal_gen_pwm.h diff --git a/applications/plugins/snake_game/application.fam b/applications/external/snake_game/application.fam similarity index 75% rename from applications/plugins/snake_game/application.fam rename to applications/external/snake_game/application.fam index d55f53bb1..c736a4ddc 100644 --- a/applications/plugins/snake_game/application.fam +++ b/applications/external/snake_game/application.fam @@ -1,9 +1,8 @@ App( appid="snake_game", name="Snake Game", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, entry_point="snake_game_app", - cdefines=["APP_SNAKE_GAME"], requires=["gui"], stack_size=1 * 1024, order=30, diff --git a/applications/plugins/snake_game/snake_10px.png b/applications/external/snake_game/snake_10px.png similarity index 100% rename from applications/plugins/snake_game/snake_10px.png rename to applications/external/snake_game/snake_10px.png diff --git a/applications/plugins/snake_game/snake_game.c b/applications/external/snake_game/snake_game.c similarity index 100% rename from applications/plugins/snake_game/snake_game.c rename to applications/external/snake_game/snake_game.c diff --git a/applications/plugins/spi_mem_manager/application.fam b/applications/external/spi_mem_manager/application.fam similarity index 100% rename from applications/plugins/spi_mem_manager/application.fam rename to applications/external/spi_mem_manager/application.fam diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_01.png similarity index 100% rename from applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png rename to applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_01.png diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_02.png b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_02.png similarity index 100% rename from applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_02.png rename to applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_02.png diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_03.png similarity index 100% rename from applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png rename to applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_03.png diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_rate similarity index 100% rename from applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate rename to applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_rate diff --git a/applications/plugins/spi_mem_manager/images/Dip8_10px.png b/applications/external/spi_mem_manager/images/Dip8_10px.png similarity index 100% rename from applications/plugins/spi_mem_manager/images/Dip8_10px.png rename to applications/external/spi_mem_manager/images/Dip8_10px.png diff --git a/applications/plugins/spi_mem_manager/images/Dip8_32x36.png b/applications/external/spi_mem_manager/images/Dip8_32x36.png similarity index 100% rename from applications/plugins/spi_mem_manager/images/Dip8_32x36.png rename to applications/external/spi_mem_manager/images/Dip8_32x36.png diff --git a/applications/plugins/spi_mem_manager/images/DolphinMafia_115x62.png b/applications/external/spi_mem_manager/images/DolphinMafia_115x62.png similarity index 100% rename from applications/plugins/spi_mem_manager/images/DolphinMafia_115x62.png rename to applications/external/spi_mem_manager/images/DolphinMafia_115x62.png diff --git a/applications/plugins/spi_mem_manager/images/DolphinNice_96x59.png b/applications/external/spi_mem_manager/images/DolphinNice_96x59.png similarity index 100% rename from applications/plugins/spi_mem_manager/images/DolphinNice_96x59.png rename to applications/external/spi_mem_manager/images/DolphinNice_96x59.png diff --git a/applications/plugins/spi_mem_manager/images/SDQuestion_35x43.png b/applications/external/spi_mem_manager/images/SDQuestion_35x43.png similarity index 100% rename from applications/plugins/spi_mem_manager/images/SDQuestion_35x43.png rename to applications/external/spi_mem_manager/images/SDQuestion_35x43.png diff --git a/applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png b/applications/external/spi_mem_manager/images/Wiring_SPI_128x64.png similarity index 100% rename from applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png rename to applications/external/spi_mem_manager/images/Wiring_SPI_128x64.png diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip.c similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.c rename to applications/external/spi_mem_manager/lib/spi/spi_mem_chip.c diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip.h similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h rename to applications/external/spi_mem_manager/lib/spi/spi_mem_chip.h diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_arr.c similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c rename to applications/external/spi_mem_manager/lib/spi/spi_mem_chip_arr.c diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_i.h similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h rename to applications/external/spi_mem_manager/lib/spi/spi_mem_chip_i.h diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.c similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c rename to applications/external/spi_mem_manager/lib/spi/spi_mem_tools.c diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.h similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h rename to applications/external/spi_mem_manager/lib/spi/spi_mem_tools.h diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.c similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c rename to applications/external/spi_mem_manager/lib/spi/spi_mem_worker.c diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.h similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h rename to applications/external/spi_mem_manager/lib/spi/spi_mem_worker.h diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_i.h similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h rename to applications/external/spi_mem_manager/lib/spi/spi_mem_worker_i.h diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_modes.c similarity index 100% rename from applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c rename to applications/external/spi_mem_manager/lib/spi/spi_mem_worker_modes.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h b/applications/external/spi_mem_manager/scenes/spi_mem_scene.h similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h rename to applications/external/spi_mem_manager/scenes/spi_mem_scene.h diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_about.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_about.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_error.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_error.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h b/applications/external/spi_mem_manager/scenes/spi_mem_scene_config.h similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_config.h diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_erase.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_erase.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_file_info.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_file_info.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_read.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_read.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_read_filename.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_read_filename.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_file.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_select_file.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_model.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_select_model.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_start.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_start.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_storage_error.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_storage_error.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_success.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_success.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_verify.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify_error.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_verify_error.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_wiring.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_wiring.c diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_write.c similarity index 100% rename from applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c rename to applications/external/spi_mem_manager/scenes/spi_mem_scene_write.c diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.c b/applications/external/spi_mem_manager/spi_mem_app.c similarity index 100% rename from applications/plugins/spi_mem_manager/spi_mem_app.c rename to applications/external/spi_mem_manager/spi_mem_app.c diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.h b/applications/external/spi_mem_manager/spi_mem_app.h similarity index 100% rename from applications/plugins/spi_mem_manager/spi_mem_app.h rename to applications/external/spi_mem_manager/spi_mem_app.h diff --git a/applications/plugins/spi_mem_manager/spi_mem_app_i.h b/applications/external/spi_mem_manager/spi_mem_app_i.h similarity index 100% rename from applications/plugins/spi_mem_manager/spi_mem_app_i.h rename to applications/external/spi_mem_manager/spi_mem_app_i.h diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.c b/applications/external/spi_mem_manager/spi_mem_files.c similarity index 100% rename from applications/plugins/spi_mem_manager/spi_mem_files.c rename to applications/external/spi_mem_manager/spi_mem_files.c diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.h b/applications/external/spi_mem_manager/spi_mem_files.h similarity index 100% rename from applications/plugins/spi_mem_manager/spi_mem_files.h rename to applications/external/spi_mem_manager/spi_mem_files.h diff --git a/applications/plugins/spi_mem_manager/tools/README.md b/applications/external/spi_mem_manager/tools/README.md similarity index 100% rename from applications/plugins/spi_mem_manager/tools/README.md rename to applications/external/spi_mem_manager/tools/README.md diff --git a/applications/plugins/spi_mem_manager/tools/chiplist/LICENSE b/applications/external/spi_mem_manager/tools/chiplist/LICENSE similarity index 100% rename from applications/plugins/spi_mem_manager/tools/chiplist/LICENSE rename to applications/external/spi_mem_manager/tools/chiplist/LICENSE diff --git a/applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml b/applications/external/spi_mem_manager/tools/chiplist/chiplist.xml similarity index 100% rename from applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml rename to applications/external/spi_mem_manager/tools/chiplist/chiplist.xml diff --git a/applications/plugins/spi_mem_manager/tools/chiplist_convert.py b/applications/external/spi_mem_manager/tools/chiplist_convert.py similarity index 100% rename from applications/plugins/spi_mem_manager/tools/chiplist_convert.py rename to applications/external/spi_mem_manager/tools/chiplist_convert.py diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c b/applications/external/spi_mem_manager/views/spi_mem_view_detect.c similarity index 100% rename from applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c rename to applications/external/spi_mem_manager/views/spi_mem_view_detect.c diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h b/applications/external/spi_mem_manager/views/spi_mem_view_detect.h similarity index 100% rename from applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h rename to applications/external/spi_mem_manager/views/spi_mem_view_detect.h diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c b/applications/external/spi_mem_manager/views/spi_mem_view_progress.c similarity index 100% rename from applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c rename to applications/external/spi_mem_manager/views/spi_mem_view_progress.c diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h b/applications/external/spi_mem_manager/views/spi_mem_view_progress.h similarity index 100% rename from applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h rename to applications/external/spi_mem_manager/views/spi_mem_view_progress.h diff --git a/applications/plugins/weather_station/application.fam b/applications/external/weather_station/application.fam similarity index 79% rename from applications/plugins/weather_station/application.fam rename to applications/external/weather_station/application.fam index 935f92573..8dcaa1259 100644 --- a/applications/plugins/weather_station/application.fam +++ b/applications/external/weather_station/application.fam @@ -1,10 +1,9 @@ App( appid="weather_station", name="Weather Station", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.EXTERNAL, targets=["f7"], entry_point="weather_station_app", - cdefines=["APP_WEATHER_STATION"], requires=["gui"], stack_size=4 * 1024, order=50, diff --git a/applications/plugins/weather_station/helpers/weather_station_event.h b/applications/external/weather_station/helpers/weather_station_event.h similarity index 100% rename from applications/plugins/weather_station/helpers/weather_station_event.h rename to applications/external/weather_station/helpers/weather_station_event.h diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/external/weather_station/helpers/weather_station_types.h similarity index 100% rename from applications/plugins/weather_station/helpers/weather_station_types.h rename to applications/external/weather_station/helpers/weather_station_types.h diff --git a/applications/plugins/weather_station/images/Humid_10x15.png b/applications/external/weather_station/images/Humid_10x15.png similarity index 100% rename from applications/plugins/weather_station/images/Humid_10x15.png rename to applications/external/weather_station/images/Humid_10x15.png diff --git a/applications/plugins/weather_station/images/Humid_8x13.png b/applications/external/weather_station/images/Humid_8x13.png similarity index 100% rename from applications/plugins/weather_station/images/Humid_8x13.png rename to applications/external/weather_station/images/Humid_8x13.png diff --git a/applications/plugins/weather_station/images/Lock_7x8.png b/applications/external/weather_station/images/Lock_7x8.png similarity index 100% rename from applications/plugins/weather_station/images/Lock_7x8.png rename to applications/external/weather_station/images/Lock_7x8.png diff --git a/applications/plugins/weather_station/images/Pin_back_arrow_10x8.png b/applications/external/weather_station/images/Pin_back_arrow_10x8.png similarity index 100% rename from applications/plugins/weather_station/images/Pin_back_arrow_10x8.png rename to applications/external/weather_station/images/Pin_back_arrow_10x8.png diff --git a/applications/plugins/weather_station/images/Quest_7x8.png b/applications/external/weather_station/images/Quest_7x8.png similarity index 100% rename from applications/plugins/weather_station/images/Quest_7x8.png rename to applications/external/weather_station/images/Quest_7x8.png diff --git a/applications/plugins/weather_station/images/Scanning_123x52.png b/applications/external/weather_station/images/Scanning_123x52.png similarity index 100% rename from applications/plugins/weather_station/images/Scanning_123x52.png rename to applications/external/weather_station/images/Scanning_123x52.png diff --git a/applications/plugins/weather_station/images/Therm_7x16.png b/applications/external/weather_station/images/Therm_7x16.png similarity index 100% rename from applications/plugins/weather_station/images/Therm_7x16.png rename to applications/external/weather_station/images/Therm_7x16.png diff --git a/applications/plugins/weather_station/images/Timer_11x11.png b/applications/external/weather_station/images/Timer_11x11.png similarity index 100% rename from applications/plugins/weather_station/images/Timer_11x11.png rename to applications/external/weather_station/images/Timer_11x11.png diff --git a/applications/plugins/weather_station/images/Unlock_7x8.png b/applications/external/weather_station/images/Unlock_7x8.png similarity index 100% rename from applications/plugins/weather_station/images/Unlock_7x8.png rename to applications/external/weather_station/images/Unlock_7x8.png diff --git a/applications/plugins/weather_station/images/WarningDolphin_45x42.png b/applications/external/weather_station/images/WarningDolphin_45x42.png similarity index 100% rename from applications/plugins/weather_station/images/WarningDolphin_45x42.png rename to applications/external/weather_station/images/WarningDolphin_45x42.png diff --git a/applications/plugins/weather_station/images/station_icon.png b/applications/external/weather_station/images/station_icon.png similarity index 100% rename from applications/plugins/weather_station/images/station_icon.png rename to applications/external/weather_station/images/station_icon.png diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.c b/applications/external/weather_station/protocols/acurite_592txr.c similarity index 100% rename from applications/plugins/weather_station/protocols/acurite_592txr.c rename to applications/external/weather_station/protocols/acurite_592txr.c diff --git a/applications/plugins/weather_station/protocols/acurite_592txr.h b/applications/external/weather_station/protocols/acurite_592txr.h similarity index 100% rename from applications/plugins/weather_station/protocols/acurite_592txr.h rename to applications/external/weather_station/protocols/acurite_592txr.h diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.c b/applications/external/weather_station/protocols/acurite_606tx.c similarity index 100% rename from applications/plugins/weather_station/protocols/acurite_606tx.c rename to applications/external/weather_station/protocols/acurite_606tx.c diff --git a/applications/plugins/weather_station/protocols/acurite_606tx.h b/applications/external/weather_station/protocols/acurite_606tx.h similarity index 100% rename from applications/plugins/weather_station/protocols/acurite_606tx.h rename to applications/external/weather_station/protocols/acurite_606tx.h diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.c b/applications/external/weather_station/protocols/acurite_609txc.c similarity index 100% rename from applications/plugins/weather_station/protocols/acurite_609txc.c rename to applications/external/weather_station/protocols/acurite_609txc.c diff --git a/applications/plugins/weather_station/protocols/acurite_609txc.h b/applications/external/weather_station/protocols/acurite_609txc.h similarity index 100% rename from applications/plugins/weather_station/protocols/acurite_609txc.h rename to applications/external/weather_station/protocols/acurite_609txc.h diff --git a/applications/plugins/weather_station/protocols/ambient_weather.c b/applications/external/weather_station/protocols/ambient_weather.c similarity index 100% rename from applications/plugins/weather_station/protocols/ambient_weather.c rename to applications/external/weather_station/protocols/ambient_weather.c diff --git a/applications/plugins/weather_station/protocols/ambient_weather.h b/applications/external/weather_station/protocols/ambient_weather.h similarity index 100% rename from applications/plugins/weather_station/protocols/ambient_weather.h rename to applications/external/weather_station/protocols/ambient_weather.h diff --git a/applications/plugins/weather_station/protocols/auriol_hg0601a.c b/applications/external/weather_station/protocols/auriol_hg0601a.c similarity index 100% rename from applications/plugins/weather_station/protocols/auriol_hg0601a.c rename to applications/external/weather_station/protocols/auriol_hg0601a.c diff --git a/applications/plugins/weather_station/protocols/auriol_hg0601a.h b/applications/external/weather_station/protocols/auriol_hg0601a.h similarity index 100% rename from applications/plugins/weather_station/protocols/auriol_hg0601a.h rename to applications/external/weather_station/protocols/auriol_hg0601a.h diff --git a/applications/plugins/weather_station/protocols/gt_wt_02.c b/applications/external/weather_station/protocols/gt_wt_02.c similarity index 100% rename from applications/plugins/weather_station/protocols/gt_wt_02.c rename to applications/external/weather_station/protocols/gt_wt_02.c diff --git a/applications/plugins/weather_station/protocols/gt_wt_02.h b/applications/external/weather_station/protocols/gt_wt_02.h similarity index 100% rename from applications/plugins/weather_station/protocols/gt_wt_02.h rename to applications/external/weather_station/protocols/gt_wt_02.h diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.c b/applications/external/weather_station/protocols/gt_wt_03.c similarity index 100% rename from applications/plugins/weather_station/protocols/gt_wt_03.c rename to applications/external/weather_station/protocols/gt_wt_03.c diff --git a/applications/plugins/weather_station/protocols/gt_wt_03.h b/applications/external/weather_station/protocols/gt_wt_03.h similarity index 100% rename from applications/plugins/weather_station/protocols/gt_wt_03.h rename to applications/external/weather_station/protocols/gt_wt_03.h diff --git a/applications/plugins/weather_station/protocols/infactory.c b/applications/external/weather_station/protocols/infactory.c similarity index 100% rename from applications/plugins/weather_station/protocols/infactory.c rename to applications/external/weather_station/protocols/infactory.c diff --git a/applications/plugins/weather_station/protocols/infactory.h b/applications/external/weather_station/protocols/infactory.h similarity index 100% rename from applications/plugins/weather_station/protocols/infactory.h rename to applications/external/weather_station/protocols/infactory.h diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.c b/applications/external/weather_station/protocols/lacrosse_tx.c similarity index 100% rename from applications/plugins/weather_station/protocols/lacrosse_tx.c rename to applications/external/weather_station/protocols/lacrosse_tx.c diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.h b/applications/external/weather_station/protocols/lacrosse_tx.h similarity index 100% rename from applications/plugins/weather_station/protocols/lacrosse_tx.h rename to applications/external/weather_station/protocols/lacrosse_tx.h diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c similarity index 100% rename from applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.c rename to applications/external/weather_station/protocols/lacrosse_tx141thbv2.c diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.h similarity index 100% rename from applications/plugins/weather_station/protocols/lacrosse_tx141thbv2.h rename to applications/external/weather_station/protocols/lacrosse_tx141thbv2.h diff --git a/applications/plugins/weather_station/protocols/nexus_th.c b/applications/external/weather_station/protocols/nexus_th.c similarity index 100% rename from applications/plugins/weather_station/protocols/nexus_th.c rename to applications/external/weather_station/protocols/nexus_th.c diff --git a/applications/plugins/weather_station/protocols/nexus_th.h b/applications/external/weather_station/protocols/nexus_th.h similarity index 100% rename from applications/plugins/weather_station/protocols/nexus_th.h rename to applications/external/weather_station/protocols/nexus_th.h diff --git a/applications/plugins/weather_station/protocols/oregon2.c b/applications/external/weather_station/protocols/oregon2.c similarity index 100% rename from applications/plugins/weather_station/protocols/oregon2.c rename to applications/external/weather_station/protocols/oregon2.c diff --git a/applications/plugins/weather_station/protocols/oregon2.h b/applications/external/weather_station/protocols/oregon2.h similarity index 100% rename from applications/plugins/weather_station/protocols/oregon2.h rename to applications/external/weather_station/protocols/oregon2.h diff --git a/applications/plugins/weather_station/protocols/oregon_v1.c b/applications/external/weather_station/protocols/oregon_v1.c similarity index 100% rename from applications/plugins/weather_station/protocols/oregon_v1.c rename to applications/external/weather_station/protocols/oregon_v1.c diff --git a/applications/plugins/weather_station/protocols/oregon_v1.h b/applications/external/weather_station/protocols/oregon_v1.h similarity index 100% rename from applications/plugins/weather_station/protocols/oregon_v1.h rename to applications/external/weather_station/protocols/oregon_v1.h diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c similarity index 100% rename from applications/plugins/weather_station/protocols/protocol_items.c rename to applications/external/weather_station/protocols/protocol_items.c diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h similarity index 100% rename from applications/plugins/weather_station/protocols/protocol_items.h rename to applications/external/weather_station/protocols/protocol_items.h diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.c b/applications/external/weather_station/protocols/thermopro_tx4.c similarity index 100% rename from applications/plugins/weather_station/protocols/thermopro_tx4.c rename to applications/external/weather_station/protocols/thermopro_tx4.c diff --git a/applications/plugins/weather_station/protocols/thermopro_tx4.h b/applications/external/weather_station/protocols/thermopro_tx4.h similarity index 100% rename from applications/plugins/weather_station/protocols/thermopro_tx4.h rename to applications/external/weather_station/protocols/thermopro_tx4.h diff --git a/applications/plugins/weather_station/protocols/tx_8300.c b/applications/external/weather_station/protocols/tx_8300.c similarity index 100% rename from applications/plugins/weather_station/protocols/tx_8300.c rename to applications/external/weather_station/protocols/tx_8300.c diff --git a/applications/plugins/weather_station/protocols/tx_8300.h b/applications/external/weather_station/protocols/tx_8300.h similarity index 100% rename from applications/plugins/weather_station/protocols/tx_8300.h rename to applications/external/weather_station/protocols/tx_8300.h diff --git a/applications/plugins/weather_station/protocols/ws_generic.c b/applications/external/weather_station/protocols/ws_generic.c similarity index 100% rename from applications/plugins/weather_station/protocols/ws_generic.c rename to applications/external/weather_station/protocols/ws_generic.c diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/external/weather_station/protocols/ws_generic.h similarity index 100% rename from applications/plugins/weather_station/protocols/ws_generic.h rename to applications/external/weather_station/protocols/ws_generic.h diff --git a/applications/plugins/weather_station/scenes/weather_station_receiver.c b/applications/external/weather_station/scenes/weather_station_receiver.c similarity index 100% rename from applications/plugins/weather_station/scenes/weather_station_receiver.c rename to applications/external/weather_station/scenes/weather_station_receiver.c diff --git a/applications/plugins/weather_station/scenes/weather_station_scene.c b/applications/external/weather_station/scenes/weather_station_scene.c similarity index 100% rename from applications/plugins/weather_station/scenes/weather_station_scene.c rename to applications/external/weather_station/scenes/weather_station_scene.c diff --git a/applications/plugins/weather_station/scenes/weather_station_scene.h b/applications/external/weather_station/scenes/weather_station_scene.h similarity index 100% rename from applications/plugins/weather_station/scenes/weather_station_scene.h rename to applications/external/weather_station/scenes/weather_station_scene.h diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_about.c b/applications/external/weather_station/scenes/weather_station_scene_about.c similarity index 100% rename from applications/plugins/weather_station/scenes/weather_station_scene_about.c rename to applications/external/weather_station/scenes/weather_station_scene_about.c diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_config.h b/applications/external/weather_station/scenes/weather_station_scene_config.h similarity index 100% rename from applications/plugins/weather_station/scenes/weather_station_scene_config.h rename to applications/external/weather_station/scenes/weather_station_scene_config.h diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c b/applications/external/weather_station/scenes/weather_station_scene_receiver_config.c similarity index 100% rename from applications/plugins/weather_station/scenes/weather_station_scene_receiver_config.c rename to applications/external/weather_station/scenes/weather_station_scene_receiver_config.c diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c b/applications/external/weather_station/scenes/weather_station_scene_receiver_info.c similarity index 100% rename from applications/plugins/weather_station/scenes/weather_station_scene_receiver_info.c rename to applications/external/weather_station/scenes/weather_station_scene_receiver_info.c diff --git a/applications/plugins/weather_station/scenes/weather_station_scene_start.c b/applications/external/weather_station/scenes/weather_station_scene_start.c similarity index 100% rename from applications/plugins/weather_station/scenes/weather_station_scene_start.c rename to applications/external/weather_station/scenes/weather_station_scene_start.c diff --git a/applications/plugins/weather_station/views/weather_station_receiver.c b/applications/external/weather_station/views/weather_station_receiver.c similarity index 100% rename from applications/plugins/weather_station/views/weather_station_receiver.c rename to applications/external/weather_station/views/weather_station_receiver.c diff --git a/applications/plugins/weather_station/views/weather_station_receiver.h b/applications/external/weather_station/views/weather_station_receiver.h similarity index 100% rename from applications/plugins/weather_station/views/weather_station_receiver.h rename to applications/external/weather_station/views/weather_station_receiver.h diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.c b/applications/external/weather_station/views/weather_station_receiver_info.c similarity index 100% rename from applications/plugins/weather_station/views/weather_station_receiver_info.c rename to applications/external/weather_station/views/weather_station_receiver_info.c diff --git a/applications/plugins/weather_station/views/weather_station_receiver_info.h b/applications/external/weather_station/views/weather_station_receiver_info.h similarity index 100% rename from applications/plugins/weather_station/views/weather_station_receiver_info.h rename to applications/external/weather_station/views/weather_station_receiver_info.h diff --git a/applications/plugins/weather_station/weather_station_10px.png b/applications/external/weather_station/weather_station_10px.png similarity index 100% rename from applications/plugins/weather_station/weather_station_10px.png rename to applications/external/weather_station/weather_station_10px.png diff --git a/applications/plugins/weather_station/weather_station_app.c b/applications/external/weather_station/weather_station_app.c similarity index 100% rename from applications/plugins/weather_station/weather_station_app.c rename to applications/external/weather_station/weather_station_app.c diff --git a/applications/plugins/weather_station/weather_station_app_i.c b/applications/external/weather_station/weather_station_app_i.c similarity index 100% rename from applications/plugins/weather_station/weather_station_app_i.c rename to applications/external/weather_station/weather_station_app_i.c diff --git a/applications/plugins/weather_station/weather_station_app_i.h b/applications/external/weather_station/weather_station_app_i.h similarity index 100% rename from applications/plugins/weather_station/weather_station_app_i.h rename to applications/external/weather_station/weather_station_app_i.h diff --git a/applications/plugins/weather_station/weather_station_history.c b/applications/external/weather_station/weather_station_history.c similarity index 100% rename from applications/plugins/weather_station/weather_station_history.c rename to applications/external/weather_station/weather_station_history.c diff --git a/applications/plugins/weather_station/weather_station_history.h b/applications/external/weather_station/weather_station_history.h similarity index 100% rename from applications/plugins/weather_station/weather_station_history.h rename to applications/external/weather_station/weather_station_history.h diff --git a/applications/main/fap_loader/application.fam b/applications/main/fap_loader/application.fam index 784ee9508..b0e67cd42 100644 --- a/applications/main/fap_loader/application.fam +++ b/applications/main/fap_loader/application.fam @@ -7,6 +7,7 @@ App( requires=[ "gui", "storage", + "loader", ], stack_size=int(1.5 * 1024), icon="A_Plugins_14", diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp b/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp deleted file mode 100644 index e845ed008..000000000 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "compilesort.hpp" -#include "elf_hashtable.h" -#include "elf_hashtable_entry.h" -#include "elf_hashtable_checks.hpp" - -#include -#include - -/* Generated table */ -#include - -#define TAG "elf_hashtable" - -static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); - -/** - * Get function address by function name - * @param name function name - * @param address output for function address - * @return true if the table contains a function - */ - -bool elf_resolve_from_hashtable(const char* name, Elf32_Addr* address) { - bool result = false; - uint32_t gnu_sym_hash = elf_gnu_hash(name); - - sym_entry key = { - .hash = gnu_sym_hash, - .address = 0, - }; - - auto find_res = std::lower_bound(elf_api_table.cbegin(), elf_api_table.cend(), key); - if((find_res == elf_api_table.cend() || (find_res->hash != gnu_sym_hash))) { - FURI_LOG_W(TAG, "Can't find symbol '%s' (hash %lx)!", name, gnu_sym_hash); - result = false; - } else { - result = true; - *address = find_res->address; - } - - return result; -} - -const ElfApiInterface hashtable_api_interface = { - .api_version_major = (elf_api_version >> 16), - .api_version_minor = (elf_api_version & 0xFFFF), - .resolver_callback = &elf_resolve_from_hashtable, -}; diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable.h b/applications/main/fap_loader/elf_cpp/elf_hashtable.h deleted file mode 100644 index e574f1169..000000000 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -extern const ElfApiInterface hashtable_api_interface; - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable_checks.hpp b/applications/main/fap_loader/elf_cpp/elf_hashtable_checks.hpp deleted file mode 100644 index 61ee80e91..000000000 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable_checks.hpp +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Check for multiple entries with the same hash value at compilation time. - */ - -#pragma once -#include -#include "elf_hashtable_entry.h" - -template -constexpr bool has_hash_collisions(const std::array api_methods) { - for(std::size_t i = 0; i < (N - 1); ++i) { - if(api_methods[i].hash == api_methods[i + 1].hash) { - return true; - } - } - - return false; -} diff --git a/applications/main/fap_loader/elf_cpp/elf_hashtable_entry.h b/applications/main/fap_loader/elf_cpp/elf_hashtable_entry.h deleted file mode 100644 index 7b540fba6..000000000 --- a/applications/main/fap_loader/elf_cpp/elf_hashtable_entry.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -struct sym_entry { - uint32_t hash; - uint32_t address; -}; - -#ifdef __cplusplus -} - -#include -#include - -#define API_METHOD(x, ret_type, args_type) \ - sym_entry { \ - .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast(x)) \ - } - -#define API_VARIABLE(x, var_type) \ - sym_entry { \ - .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), \ - } - -constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) { - return k1.hash < k2.hash; -} - -constexpr uint32_t elf_gnu_hash(const char* s) { - uint32_t h = 0x1505; - for(unsigned char c = *s; c != '\0'; c = *++s) { - h = (h << 5) + h + c; - } - return h; -} - -#endif diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index e81a3ce4c..dcbad8e13 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -7,7 +7,7 @@ #include #include #include -#include "elf_cpp/elf_hashtable.h" +#include #include "fap_loader_app.h" #define TAG "fap_loader_app" @@ -27,7 +27,7 @@ bool fap_loader_load_name_and_icon( Storage* storage, uint8_t** icon_ptr, FuriString* item_name) { - FlipperApplication* app = flipper_application_alloc(storage, &hashtable_api_interface); + FlipperApplication* app = flipper_application_alloc(storage, firmware_api_interface); FlipperApplicationPreloadStatus preload_res = flipper_application_preload_manifest(app, furi_string_get_cstr(path)); @@ -71,7 +71,7 @@ static bool fap_loader_run_selected_app(FapLoader* loader) { bool show_error = true; do { file_selected = true; - loader->app = flipper_application_alloc(loader->storage, &hashtable_api_interface); + loader->app = flipper_application_alloc(loader->storage, firmware_api_interface); size_t start = furi_get_tick(); FURI_LOG_I(TAG, "FAP Loader is loading %s", furi_string_get_cstr(loader->fap_path)); diff --git a/applications/plugins/application.fam b/applications/plugins/application.fam deleted file mode 100644 index 6d25e45aa..000000000 --- a/applications/plugins/application.fam +++ /dev/null @@ -1,9 +0,0 @@ -App( - appid="basic_plugins", - name="Basic applications for plug-in menu", - apptype=FlipperAppType.METAPACKAGE, - provides=[ - "music_player", - "snake_game", - ], -) diff --git a/applications/services/applications.h b/applications/services/applications.h index 871e9af54..85f736742 100644 --- a/applications/services/applications.h +++ b/applications/services/applications.h @@ -39,18 +39,6 @@ extern const size_t FLIPPER_APPS_COUNT; extern const FlipperOnStartHook FLIPPER_ON_SYSTEM_START[]; extern const size_t FLIPPER_ON_SYSTEM_START_COUNT; -/* Plugins list - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_PLUGINS[]; -extern const size_t FLIPPER_PLUGINS_COUNT; - -/* Debug menu apps - * Spawned by loader - */ -extern const FlipperApplication FLIPPER_DEBUG_APPS[]; -extern const size_t FLIPPER_DEBUG_APPS_COUNT; - /* System apps * Can only be spawned by loader by name */ diff --git a/applications/services/loader/application.fam b/applications/services/loader/application.fam index 91103e46e..49f3c4148 100644 --- a/applications/services/loader/application.fam +++ b/applications/services/loader/application.fam @@ -7,5 +7,8 @@ App( requires=["gui"], stack_size=2 * 1024, order=90, - sdk_headers=["loader.h"], + sdk_headers=[ + "loader.h", + "firmware_api/firmware_api.h", + ], ) diff --git a/applications/services/loader/firmware_api/firmware_api.cpp b/applications/services/loader/firmware_api/firmware_api.cpp new file mode 100644 index 000000000..814dd82c9 --- /dev/null +++ b/applications/services/loader/firmware_api/firmware_api.cpp @@ -0,0 +1,21 @@ +#include "firmware_api.h" + +#include +#include + +/* Generated table */ +#include + +static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!"); + +constexpr HashtableApiInterface elf_api_interface{ + { + .api_version_major = (elf_api_version >> 16), + .api_version_minor = (elf_api_version & 0xFFFF), + .resolver_callback = &elf_resolve_from_hashtable, + }, + .table_cbegin = elf_api_table.cbegin(), + .table_cend = elf_api_table.cend(), +}; + +const ElfApiInterface* const firmware_api_interface = &elf_api_interface; diff --git a/applications/services/loader/firmware_api/firmware_api.h b/applications/services/loader/firmware_api/firmware_api.h new file mode 100644 index 000000000..c73ae8960 --- /dev/null +++ b/applications/services/loader/firmware_api/firmware_api.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +extern const ElfApiInterface* const firmware_api_interface; diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 5f2d8a2e7..f83d47d63 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -88,10 +88,6 @@ static FlipperApplication const* loader_find_application_by_name_in_list( const FlipperApplication* loader_find_application_by_name(const char* name) { const FlipperApplication* application = NULL; application = loader_find_application_by_name_in_list(name, FLIPPER_APPS, FLIPPER_APPS_COUNT); - if(!application) { - application = - loader_find_application_by_name_in_list(name, FLIPPER_PLUGINS, FLIPPER_PLUGINS_COUNT); - } if(!application) { application = loader_find_application_by_name_in_list( name, FLIPPER_SETTINGS_APPS, FLIPPER_SETTINGS_APPS_COUNT); @@ -100,10 +96,6 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { application = loader_find_application_by_name_in_list( name, FLIPPER_SYSTEM_APPS, FLIPPER_SYSTEM_APPS_COUNT); } - if(!application) { - application = loader_find_application_by_name_in_list( - name, FLIPPER_DEBUG_APPS, FLIPPER_DEBUG_APPS_COUNT); - } return application; } @@ -160,18 +152,6 @@ static void loader_cli_list(Cli* cli, FuriString* args, Loader* instance) { for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { printf("\t%s\r\n", FLIPPER_APPS[i].name); } - - printf("Plugins:\r\n"); - for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { - printf("\t%s\r\n", FLIPPER_PLUGINS[i].name); - } - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - printf("Debug:\r\n"); - for(size_t i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { - printf("\t%s\r\n", FLIPPER_DEBUG_APPS[i].name); - } - } } static void loader_cli_info(Cli* cli, FuriString* args, Loader* instance) { @@ -341,22 +321,6 @@ static Loader* loader_alloc() { view_set_previous_callback(menu_get_view(instance->primary_menu), loader_hide_menu); view_dispatcher_add_view( instance->view_dispatcher, LoaderMenuViewPrimary, menu_get_view(instance->primary_menu)); - // Plugins menu - instance->plugins_menu = submenu_alloc(); - view_set_context(submenu_get_view(instance->plugins_menu), instance->plugins_menu); - view_set_previous_callback( - submenu_get_view(instance->plugins_menu), loader_back_to_primary_menu); - view_dispatcher_add_view( - instance->view_dispatcher, - LoaderMenuViewPlugins, - submenu_get_view(instance->plugins_menu)); - // Debug menu - instance->debug_menu = submenu_alloc(); - view_set_context(submenu_get_view(instance->debug_menu), instance->debug_menu); - view_set_previous_callback( - submenu_get_view(instance->debug_menu), loader_back_to_primary_menu); - view_dispatcher_add_view( - instance->view_dispatcher, LoaderMenuViewDebug, submenu_get_view(instance->debug_menu)); // Settings menu instance->settings_menu = submenu_alloc(); view_set_context(submenu_get_view(instance->settings_menu), instance->settings_menu); @@ -385,10 +349,6 @@ static void loader_free(Loader* instance) { menu_free(loader_instance->primary_menu); view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); - submenu_free(loader_instance->plugins_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPlugins); - submenu_free(loader_instance->debug_menu); - view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewDebug); submenu_free(loader_instance->settings_menu); view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewSettings); view_dispatcher_free(loader_instance->view_dispatcher); @@ -411,24 +371,6 @@ static void loader_build_menu() { loader_menu_callback, (void*)&FLIPPER_APPS[i]); } - if(FLIPPER_PLUGINS_COUNT != 0) { - menu_add_item( - loader_instance->primary_menu, - "Plugins", - &A_Plugins_14, - i++, - loader_submenu_callback, - (void*)LoaderMenuViewPlugins); - } - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) && (FLIPPER_DEBUG_APPS_COUNT > 0)) { - menu_add_item( - loader_instance->primary_menu, - "Debug Tools", - &A_Debug_14, - i++, - loader_submenu_callback, - (void*)LoaderMenuViewDebug); - } menu_add_item( loader_instance->primary_menu, "Settings", @@ -439,29 +381,8 @@ static void loader_build_menu() { } static void loader_build_submenu() { - FURI_LOG_I(TAG, "Building plugins menu"); - size_t i; - for(i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { - submenu_add_item( - loader_instance->plugins_menu, - FLIPPER_PLUGINS[i].name, - i, - loader_menu_callback, - (void*)&FLIPPER_PLUGINS[i]); - } - - FURI_LOG_I(TAG, "Building debug menu"); - for(i = 0; i < FLIPPER_DEBUG_APPS_COUNT; i++) { - submenu_add_item( - loader_instance->debug_menu, - FLIPPER_DEBUG_APPS[i].name, - i, - loader_menu_callback, - (void*)&FLIPPER_DEBUG_APPS[i]); - } - FURI_LOG_I(TAG, "Building settings menu"); - for(i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { + for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { submenu_add_item( loader_instance->settings_menu, FLIPPER_SETTINGS_APPS[i].name, diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index db91f806c..00028cd6b 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -26,8 +26,6 @@ struct Loader { ViewDispatcher* view_dispatcher; Menu* primary_menu; - Submenu* plugins_menu; - Submenu* debug_menu; Submenu* settings_menu; volatile uint8_t lock_count; @@ -37,7 +35,5 @@ struct Loader { typedef enum { LoaderMenuViewPrimary, - LoaderMenuViewPlugins, - LoaderMenuViewDebug, LoaderMenuViewSettings, } LoaderMenuView; diff --git a/assets/.gitignore b/assets/.gitignore index 269577047..a66a6eed4 100644 --- a/assets/.gitignore +++ b/assets/.gitignore @@ -2,3 +2,4 @@ /resources/Manifest /resources/apps/* /resources/dolphin/* +/resources/apps_data/**/*.fal diff --git a/documentation/Doxyfile b/documentation/Doxyfile index 1824e5a52..9611e7f1a 100644 --- a/documentation/Doxyfile +++ b/documentation/Doxyfile @@ -938,7 +938,7 @@ EXCLUDE = \ lib/microtar \ lib/mbedtls \ lib/cxxheaderparser \ - applications/plugins/dap_link/lib/free-dap + applications/external/dap_link/lib/free-dap # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/fbt_options.py b/fbt_options.py index 4850389ad..a10c64b96 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -71,11 +71,6 @@ FIRMWARE_APPS = { "system_apps", # Settings "settings_apps", - # Stock plugins - no longer built into fw, now they're .faps - # Yet you can still build them as a part of fw - # "basic_plugins", - # Debug - # "debug_apps", ], "unit_tests": [ "basic_services", diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 07c323a1b..61195aba6 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,+,18.1,, +Version,+,18.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -28,6 +28,7 @@ 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/firmware_api/firmware_api.h,, Header,+,applications/services/loader/loader.h,, Header,+,applications/services/locale/locale.h,, Header,+,applications/services/notification/notification.h,, @@ -104,7 +105,11 @@ 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/api_hashtable/api_hashtable.h,, +Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, Header,+,lib/flipper_application/flipper_application.h,, +Header,+,lib/flipper_application/plugins/composite_resolver.h,, +Header,+,lib/flipper_application/plugins/plugin_manager.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,, @@ -567,6 +572,10 @@ 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,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*" +Function,+,composite_api_resolver_alloc,CompositeApiResolver*, +Function,+,composite_api_resolver_free,void,CompositeApiResolver* +Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* 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* @@ -639,6 +648,7 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ 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,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* @@ -696,14 +706,16 @@ 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_is_plugin,_Bool,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_plugin_get_descriptor,const FlipperAppPluginDescriptor*,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_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* @@ -1473,6 +1485,13 @@ Function,-,pcTaskGetName,char*,TaskHandle_t Function,-,pcTimerGetName,const char*,TimerHandle_t Function,-,pclose,int,FILE* Function,-,perror,void,const char* +Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" +Function,+,plugin_manager_free,void,PluginManager* +Function,+,plugin_manager_get,const FlipperAppPluginDescriptor*,"PluginManager*, uint32_t" +Function,+,plugin_manager_get_count,uint32_t,PluginManager* +Function,+,plugin_manager_get_ep,const void*,"PluginManager*, uint32_t" +Function,+,plugin_manager_load_all,PluginManagerError,"PluginManager*, const char*" +Function,+,plugin_manager_load_single,PluginManagerError,"PluginManager*, const char*" Function,-,popen,FILE*,"const char*, const char*" Function,+,popup_alloc,Popup*, Function,+,popup_disable_timeout,void,Popup* @@ -2053,6 +2072,7 @@ Variable,-,_sys_nerr,int, Variable,-,_timezone,long, Variable,-,_tzname,char*[2], Variable,+,cli_vcp,CliSession, +Variable,+,firmware_api_interface,const ElfApiInterface*, Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index fcacaeee9..e46322f4b 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,+,18.1,, +Version,+,18.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -28,6 +28,7 @@ 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/firmware_api/firmware_api.h,, Header,+,applications/services/loader/loader.h,, Header,+,applications/services/locale/locale.h,, Header,+,applications/services/notification/notification.h,, @@ -110,7 +111,11 @@ 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/api_hashtable/api_hashtable.h,, +Header,+,lib/flipper_application/api_hashtable/compilesort.hpp,, Header,+,lib/flipper_application/flipper_application.h,, +Header,+,lib/flipper_application/plugins/composite_resolver.h,, +Header,+,lib/flipper_application/plugins/plugin_manager.h,, Header,+,lib/flipper_format/flipper_format.h,, Header,+,lib/flipper_format/flipper_format_i.h,, Header,+,lib/infrared/encoder_decoder/infrared.h,, @@ -679,6 +684,10 @@ 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,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiInterface*" +Function,+,composite_api_resolver_alloc,CompositeApiResolver*, +Function,+,composite_api_resolver_free,void,CompositeApiResolver* +Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" @@ -778,6 +787,7 @@ Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_ 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,+,elf_resolve_from_hashtable,_Bool,"const ElfApiInterface*, const char*, Elf32_Addr*" Function,+,empty_screen_alloc,EmptyScreen*, Function,+,empty_screen_free,void,EmptyScreen* Function,+,empty_screen_get_view,View*,EmptyScreen* @@ -863,14 +873,16 @@ 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_is_plugin,_Bool,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_plugin_get_descriptor,const FlipperAppPluginDescriptor*,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_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* @@ -2091,6 +2103,13 @@ Function,-,platformProtectST25RComm,void, Function,-,platformSetIrqCallback,void,PlatformIrqCallback Function,-,platformSpiTxRx,_Bool,"const uint8_t*, uint8_t*, uint16_t" Function,-,platformUnprotectST25RComm,void, +Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" +Function,+,plugin_manager_free,void,PluginManager* +Function,+,plugin_manager_get,const FlipperAppPluginDescriptor*,"PluginManager*, uint32_t" +Function,+,plugin_manager_get_count,uint32_t,PluginManager* +Function,+,plugin_manager_get_ep,const void*,"PluginManager*, uint32_t" +Function,+,plugin_manager_load_all,PluginManagerError,"PluginManager*, const char*" +Function,+,plugin_manager_load_single,PluginManagerError,"PluginManager*, const char*" Function,-,popen,FILE*,"const char*, const char*" Function,+,popup_alloc,Popup*, Function,+,popup_disable_timeout,void,Popup* @@ -3021,6 +3040,7 @@ Variable,-,_sys_nerr,int, Variable,-,_timezone,long, Variable,-,_tzname,char*[2], Variable,+,cli_vcp,CliSession, +Variable,+,firmware_api_interface,const ElfApiInterface*, Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, diff --git a/furi/flipper.c b/furi/flipper.c index f0147c060..5c2ad8138 100644 --- a/furi/flipper.c +++ b/furi/flipper.c @@ -33,7 +33,7 @@ void flipper_init() { FURI_LOG_I(TAG, "Boot mode %d, starting services", furi_hal_rtc_get_boot_mode()); for(size_t i = 0; i < FLIPPER_SERVICES_COUNT; i++) { - FURI_LOG_I(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); + FURI_LOG_D(TAG, "Starting service %s", FLIPPER_SERVICES[i].name); FuriThread* thread = furi_thread_alloc_ex( FLIPPER_SERVICES[i].name, diff --git a/lib/flipper_application/SConscript b/lib/flipper_application/SConscript index 9fbbf95d1..d253cc82c 100644 --- a/lib/flipper_application/SConscript +++ b/lib/flipper_application/SConscript @@ -6,6 +6,10 @@ env.Append( ], SDK_HEADERS=[ File("flipper_application.h"), + File("plugins/plugin_manager.h"), + File("plugins/composite_resolver.h"), + File("api_hashtable/api_hashtable.h"), + File("api_hashtable/compilesort.hpp"), ], ) @@ -14,6 +18,7 @@ libenv = env.Clone(FW_LIB_NAME="flipper_application") libenv.ApplyLibFlags() sources = libenv.GlobRecursive("*.c") +sources.append(File("api_hashtable/api_hashtable.cpp")) lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/lib/flipper_application/api_hashtable/api_hashtable.cpp b/lib/flipper_application/api_hashtable/api_hashtable.cpp new file mode 100644 index 000000000..022792dce --- /dev/null +++ b/lib/flipper_application/api_hashtable/api_hashtable.cpp @@ -0,0 +1,38 @@ +#include "api_hashtable.h" + +#include +#include + +#define TAG "hashtable_api" + +bool elf_resolve_from_hashtable( + const ElfApiInterface* interface, + const char* name, + Elf32_Addr* address) { + const HashtableApiInterface* hashtable_interface = + static_cast(interface); + bool result = false; + uint32_t gnu_sym_hash = elf_gnu_hash(name); + + sym_entry key = { + .hash = gnu_sym_hash, + .address = 0, + }; + + auto find_res = + std::lower_bound(hashtable_interface->table_cbegin, hashtable_interface->table_cend, key); + if((find_res == hashtable_interface->table_cend || (find_res->hash != gnu_sym_hash))) { + FURI_LOG_W( + TAG, + "Can't find symbol '%s' (hash %lx) @ %p!", + name, + gnu_sym_hash, + hashtable_interface->table_cbegin); + result = false; + } else { + result = true; + *address = find_res->address; + } + + return result; +} diff --git a/lib/flipper_application/api_hashtable/api_hashtable.h b/lib/flipper_application/api_hashtable/api_hashtable.h new file mode 100644 index 000000000..7e4b4aba1 --- /dev/null +++ b/lib/flipper_application/api_hashtable/api_hashtable.h @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Symbol table entry + */ +struct sym_entry { + uint32_t hash; + uint32_t address; +}; + +/** + * @brief Resolver for API entries using a pre-sorted table with hashes + * @param interface pointer to HashtableApiInterface + * @param name function name + * @param address output for function address + * @return true if the table contains a function + */ +bool elf_resolve_from_hashtable( + const ElfApiInterface* interface, + const char* name, + Elf32_Addr* address); + +#ifdef __cplusplus +} + +#include +#include + +/** + * @brief HashtableApiInterface is an implementation of ElfApiInterface + * that uses a hash table to resolve function addresses. + * table_cbegin and table_cend must point to a sorted array of sym_entry + */ +struct HashtableApiInterface : public ElfApiInterface { + const sym_entry *table_cbegin, *table_cend; +}; + +#define API_METHOD(x, ret_type, args_type) \ + sym_entry { \ + .hash = elf_gnu_hash(#x), .address = (uint32_t)(static_cast(x)) \ + } + +#define API_VARIABLE(x, var_type) \ + sym_entry { .hash = elf_gnu_hash(#x), .address = (uint32_t)(&(x)), } + +constexpr bool operator<(const sym_entry& k1, const sym_entry& k2) { + return k1.hash < k2.hash; +} + +/** + * @brief Calculate hash for a string using the ELF GNU hash algorithm + * @param s string to calculate hash for + * @return hash value + */ +constexpr uint32_t elf_gnu_hash(const char* s) { + uint32_t h = 0x1505; + for(unsigned char c = *s; c != '\0'; c = *++s) { + h = (h << 5) + h + c; + } + return h; +} + +/* Compile-time check for hash collisions in API table. + * Usage: static_assert(!has_hash_collisions(api_methods), "Hash collision detected"); + */ +template +constexpr bool has_hash_collisions(const std::array& api_methods) { + for(std::size_t i = 0; i < (N - 1); ++i) { + if(api_methods[i].hash == api_methods[i + 1].hash) { + return true; + } + } + + return false; +} + +#endif \ No newline at end of file diff --git a/applications/main/fap_loader/elf_cpp/compilesort.hpp b/lib/flipper_application/api_hashtable/compilesort.hpp similarity index 99% rename from applications/main/fap_loader/elf_cpp/compilesort.hpp rename to lib/flipper_application/api_hashtable/compilesort.hpp index 746611697..9737fd022 100644 --- a/applications/main/fap_loader/elf_cpp/compilesort.hpp +++ b/lib/flipper_application/api_hashtable/compilesort.hpp @@ -4,6 +4,8 @@ #pragma once +#ifdef __cplusplus + #include #include @@ -109,3 +111,5 @@ constexpr auto create_array_t(const Ts&&... values) { static_assert(traits::are_same_type(), "all elements must have same type"); return std::array{static_cast(values)...}; } + +#endif diff --git a/lib/flipper_application/elf/elf_api_interface.h b/lib/flipper_application/elf/elf_api_interface.h index ca31fc483..f07df4edb 100644 --- a/lib/flipper_application/elf/elf_api_interface.h +++ b/lib/flipper_application/elf/elf_api_interface.h @@ -3,10 +3,14 @@ #include #include -#define ELF_INVALID_ADDRESS 0xFFFFFFFF - -typedef struct { +/** + * @brief Interface for ELF loader to resolve symbols + */ +typedef struct ElfApiInterface { uint16_t api_version_major; uint16_t api_version_minor; - bool (*resolver_callback)(const char* name, Elf32_Addr* address); + bool (*resolver_callback)( + const struct ElfApiInterface* interface, + const char* name, + Elf32_Addr* address); } ElfApiInterface; diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index 58e315333..146afccb5 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -17,6 +17,8 @@ #define FURI_LOG_D(...) #endif +#define ELF_INVALID_ADDRESS 0xFFFFFFFF + #define TRAMPOLINE_CODE_SIZE 6 /** @@ -166,7 +168,7 @@ static ELFSection* elf_section_of(ELFFile* elf, int index) { static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) { if(sym->st_shndx == SHN_UNDEF) { Elf32_Addr addr = 0; - if(elf->api_interface->resolver_callback(sName, &addr)) { + if(elf->api_interface->resolver_callback(elf->api_interface, sName, &addr)) { return addr; } } else { @@ -514,10 +516,13 @@ static SectionType elf_preload_section( section_p->sec_idx = section_idx; if(section_header->sh_type == SHT_PREINIT_ARRAY) { + furi_assert(elf->preinit_array == NULL); elf->preinit_array = section_p; } else if(section_header->sh_type == SHT_INIT_ARRAY) { + furi_assert(elf->init_array == NULL); elf->init_array = section_p; } else if(section_header->sh_type == SHT_FINI_ARRAY) { + furi_assert(elf->fini_array == NULL); elf->fini_array = section_p; } @@ -605,10 +610,17 @@ ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface) elf->api_interface = api_interface; ELFSectionDict_init(elf->sections); AddressCache_init(elf->trampoline_cache); + elf->init_array_called = false; return elf; } void elf_file_free(ELFFile* elf) { + // furi_check(!elf->init_array_called); + if(elf->init_array_called) { + FURI_LOG_W(TAG, "Init array was called, but fini array wasn't"); + elf_file_call_section_list(elf->fini_array, true); + } + // free sections data { ELFSectionDict_it_t it; @@ -774,19 +786,26 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { return status; } -void elf_file_pre_run(ELFFile* elf) { +void elf_file_call_init(ELFFile* elf) { + furi_check(!elf->init_array_called); elf_file_call_section_list(elf->preinit_array, false); elf_file_call_section_list(elf->init_array, false); + elf->init_array_called = true; } -int32_t elf_file_run(ELFFile* elf, void* args) { - int32_t result; - result = ((int32_t(*)(void*))elf->entry)(args); - return result; +bool elf_file_is_init_complete(ELFFile* elf) { + return elf->init_array_called; } -void elf_file_post_run(ELFFile* elf) { +void* elf_file_get_entry_point(ELFFile* elf) { + furi_check(elf->init_array_called); + return (void*)elf->entry; +} + +void elf_file_call_fini(ELFFile* elf) { + furi_check(elf->init_array_called); elf_file_call_section_list(elf->fini_array, true); + elf->init_array_called = false; } const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) { diff --git a/lib/flipper_application/elf/elf_file.h b/lib/flipper_application/elf/elf_file.h index f371cdb22..631fe122f 100644 --- a/lib/flipper_application/elf/elf_file.h +++ b/lib/flipper_application/elf/elf_file.h @@ -82,24 +82,34 @@ bool elf_file_load_section_table(ELFFile* elf_file); ELFFileLoadStatus elf_file_load_sections(ELFFile* elf_file); /** - * @brief Execute ELF file pre-run stage, call static constructors for example (load stage #3) + * @brief Execute ELF file pre-run stage, + * call static constructors for example (load stage #3) + * Must be done before invoking any code from the ELF file * @param elf */ -void elf_file_pre_run(ELFFile* elf); +void elf_file_call_init(ELFFile* elf); /** - * @brief Run ELF file (load stage #4) + * @brief Check if ELF file pre-run stage was executed and its code is runnable + * @param elf + */ +bool elf_file_is_init_complete(ELFFile* elf); + +/** + * @brief Get actual entry point for ELF file * @param elf_file * @param args * @return int32_t */ -int32_t elf_file_run(ELFFile* elf_file, void* args); +void* elf_file_get_entry_point(ELFFile* elf_file); /** - * @brief Execute ELF file post-run stage, call static destructors for example (load stage #5) + * @brief Execute ELF file post-run stage, + * call static destructors for example (load stage #5) + * Must be done if any code from the ELF file was executed * @param elf */ -void elf_file_post_run(ELFFile* elf); +void elf_file_call_fini(ELFFile* elf); /** * @brief Get ELF file API interface diff --git a/lib/flipper_application/elf/elf_file_i.h b/lib/flipper_application/elf/elf_file_i.h index 9b38540b7..af9a1d9b4 100644 --- a/lib/flipper_application/elf/elf_file_i.h +++ b/lib/flipper_application/elf/elf_file_i.h @@ -45,6 +45,8 @@ struct ELFFile { ELFSection* preinit_array; ELFSection* init_array; ELFSection* fini_array; + + bool init_array_called; }; #ifdef __cplusplus diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 6e20c0809..ca917cf1a 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -10,6 +10,7 @@ struct FlipperApplication { FlipperApplicationManifest manifest; ELFFile* elf; FuriThread* thread; + void* ep_thread_args; }; /* For debugger access to app state */ @@ -20,9 +21,14 @@ FlipperApplication* FlipperApplication* app = malloc(sizeof(FlipperApplication)); app->elf = elf_file_alloc(storage, api_interface); app->thread = NULL; + app->ep_thread_args = NULL; return app; } +bool flipper_application_is_plugin(FlipperApplication* app) { + return app->manifest.stack_size == 0; +} + void flipper_application_free(FlipperApplication* app) { furi_assert(app); @@ -31,9 +37,16 @@ void flipper_application_free(FlipperApplication* app) { furi_thread_free(app->thread); } - last_loaded_app = NULL; + if(!flipper_application_is_plugin(app)) { + last_loaded_app = NULL; + } elf_file_clear_debug_info(&app->state); + + if(elf_file_is_init_complete(app->elf)) { + elf_file_call_fini(app->elf); + } + elf_file_free(app->elf); free(app); } @@ -140,7 +153,9 @@ const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplic } FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) { - last_loaded_app = app; + if(!flipper_application_is_plugin(app)) { + last_loaded_app = app; + } ELFFileLoadStatus status = elf_file_load_sections(app->elf); switch(status) { @@ -157,9 +172,15 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio } static int32_t flipper_application_thread(void* context) { - elf_file_pre_run(last_loaded_app->elf); - int32_t result = elf_file_run(last_loaded_app->elf, context); - elf_file_post_run(last_loaded_app->elf); + furi_assert(context); + FlipperApplication* app = (FlipperApplication*)context; + + elf_file_call_init(app->elf); + + FlipperApplicationEntryPoint entry_point = elf_file_get_entry_point(app->elf); + int32_t ret_code = entry_point(app->ep_thread_args); + + elf_file_call_fini(app->elf); // wait until all notifications from RAM are completed NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); @@ -169,17 +190,17 @@ static int32_t flipper_application_thread(void* context) { notification_message_block(notifications, &sequence_empty); furi_record_close(RECORD_NOTIFICATION); - return result; + return ret_code; } FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) { furi_check(app->thread == NULL); + furi_check(!flipper_application_is_plugin(app)); + app->ep_thread_args = args; const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app); - furi_check(manifest->stack_size > 0); - app->thread = furi_thread_alloc_ex( - manifest->name, manifest->stack_size, flipper_application_thread, args); + manifest->name, manifest->stack_size, flipper_application_thread, app); return app->thread; } @@ -213,3 +234,28 @@ const char* flipper_application_load_status_to_string(FlipperApplicationLoadStat } return load_status_strings[status]; } + +const FlipperAppPluginDescriptor* + flipper_application_plugin_get_descriptor(FlipperApplication* app) { + if(!flipper_application_is_plugin(app)) { + return NULL; + } + + if(!elf_file_is_init_complete(app->elf)) { + elf_file_call_init(app->elf); + } + + typedef const FlipperAppPluginDescriptor* (*get_lib_descriptor_t)(void); + get_lib_descriptor_t lib_ep = elf_file_get_entry_point(app->elf); + furi_check(lib_ep); + + const FlipperAppPluginDescriptor* lib_descriptor = lib_ep(); + + FURI_LOG_D( + TAG, + "Library for %s, API v. %lu loaded", + lib_descriptor->appid, + lib_descriptor->ep_api_version); + + return lib_descriptor; +} \ No newline at end of file diff --git a/lib/flipper_application/flipper_application.h b/lib/flipper_application/flipper_application.h index b3e5996bb..519cc3971 100644 --- a/lib/flipper_application/flipper_application.h +++ b/lib/flipper_application/flipper_application.h @@ -115,6 +115,40 @@ FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplicatio */ FuriThread* flipper_application_spawn(FlipperApplication* app, void* args); +/** + * @brief Check if application is a plugin (not a runnable standalone app) + * @param app Application pointer + * @return true if application is a plugin, false otherwise + */ +bool flipper_application_is_plugin(FlipperApplication* app); + +/** + * @brief Entry point prototype for standalone applications + */ +typedef int32_t (*FlipperApplicationEntryPoint)(void*); + +/** + * @brief An object that describes a plugin - must be returned by plugin's entry point + */ +typedef struct { + const char* appid; + const uint32_t ep_api_version; + const void* entry_point; +} FlipperAppPluginDescriptor; + +/** + * @brief Entry point prototype for plugins + */ +typedef const FlipperAppPluginDescriptor* (*FlipperApplicationPluginEntryPoint)(void); + +/** + * @brief Get plugin descriptor for preloaded plugin + * @param app Application pointer + * @return Pointer to plugin descriptor + */ +const FlipperAppPluginDescriptor* + flipper_application_plugin_get_descriptor(FlipperApplication* app); + #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/lib/flipper_application/plugins/composite_resolver.c b/lib/flipper_application/plugins/composite_resolver.c new file mode 100644 index 000000000..1402c3ad0 --- /dev/null +++ b/lib/flipper_application/plugins/composite_resolver.c @@ -0,0 +1,52 @@ +#include "composite_resolver.h" + +#include +#include + +LIST_DEF(ElfApiInterfaceList, const ElfApiInterface*, M_POD_OPLIST) +#define M_OPL_ElfApiInterfaceList_t() LIST_OPLIST(ElfApiInterfaceList, M_POD_OPLIST) + +struct CompositeApiResolver { + ElfApiInterface api_interface; + ElfApiInterfaceList_t interfaces; +}; + +static bool composite_api_resolver_callback( + const ElfApiInterface* interface, + const char* name, + Elf32_Addr* address) { + CompositeApiResolver* resolver = (CompositeApiResolver*)interface; + for + M_EACH(interface, resolver->interfaces, ElfApiInterfaceList_t) { + if((*interface)->resolver_callback(*interface, name, address)) { + return true; + } + } + return false; +} + +CompositeApiResolver* composite_api_resolver_alloc() { + CompositeApiResolver* resolver = malloc(sizeof(CompositeApiResolver)); + resolver->api_interface.api_version_major = 0; + resolver->api_interface.api_version_minor = 0; + resolver->api_interface.resolver_callback = &composite_api_resolver_callback; + ElfApiInterfaceList_init(resolver->interfaces); + return resolver; +} + +void composite_api_resolver_free(CompositeApiResolver* resolver) { + ElfApiInterfaceList_clear(resolver->interfaces); + free(resolver); +} + +void composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface) { + if(ElfApiInterfaceList_empty_p(resolver->interfaces)) { + resolver->api_interface.api_version_major = interface->api_version_major; + resolver->api_interface.api_version_minor = interface->api_version_minor; + } + ElfApiInterfaceList_push_back(resolver->interfaces, interface); +} + +const ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver) { + return &resolver->api_interface; +} diff --git a/lib/flipper_application/plugins/composite_resolver.h b/lib/flipper_application/plugins/composite_resolver.h new file mode 100644 index 000000000..a2d4bab25 --- /dev/null +++ b/lib/flipper_application/plugins/composite_resolver.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Composite API resolver + * Resolves API interface by calling all resolvers in order + * Uses API version from first resolver + * Note: when using hashtable resolvers, collisions between tables are not detected + * Can be cast to ElfApiInterface* + */ +typedef struct CompositeApiResolver CompositeApiResolver; + +/** + * @brief Allocate composite API resolver + * @return CompositeApiResolver* instance + */ +CompositeApiResolver* composite_api_resolver_alloc(); + +/** + * @brief Free composite API resolver + * @param resolver Instance + */ +void composite_api_resolver_free(CompositeApiResolver* resolver); + +/** + * @brief Add API resolver to composite resolver + * @param resolver Instance + * @param interface API resolver + */ +void composite_api_resolver_add(CompositeApiResolver* resolver, const ElfApiInterface* interface); + +/** + * @brief Get API interface from composite resolver + * @param resolver Instance + * @return API interface + */ +const ElfApiInterface* composite_api_resolver_get(CompositeApiResolver* resolver); + +#ifdef __cplusplus +} +#endif diff --git a/lib/flipper_application/plugins/plugin_manager.c b/lib/flipper_application/plugins/plugin_manager.c new file mode 100644 index 000000000..101471dc5 --- /dev/null +++ b/lib/flipper_application/plugins/plugin_manager.c @@ -0,0 +1,153 @@ +#include "plugin_manager.h" + +#include +#include +#include + +#include +#include + +#include + +#define TAG "libmgr" + +ARRAY_DEF(FlipperApplicationList, FlipperApplication*, M_PTR_OPLIST) +#define M_OPL_FlipperApplicationList_t() ARRAY_OPLIST(FlipperApplicationList, M_PTR_OPLIST) + +struct PluginManager { + const char* application_id; + uint32_t api_version; + Storage* storage; + FlipperApplicationList_t libs; + const ElfApiInterface* api_interface; +}; + +PluginManager* plugin_manager_alloc( + const char* application_id, + uint32_t api_version, + const ElfApiInterface* api_interface) { + PluginManager* manager = malloc(sizeof(PluginManager)); + manager->application_id = application_id; + manager->api_version = api_version; + manager->api_interface = api_interface ? api_interface : firmware_api_interface; + manager->storage = furi_record_open(RECORD_STORAGE); + FlipperApplicationList_init(manager->libs); + return manager; +} + +void plugin_manager_free(PluginManager* manager) { + for + M_EACH(loaded_lib, manager->libs, FlipperApplicationList_t) { + flipper_application_free(*loaded_lib); + } + FlipperApplicationList_clear(manager->libs); + furi_record_close(RECORD_STORAGE); + free(manager); +} + +PluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path) { + FlipperApplication* lib = flipper_application_alloc(manager->storage, manager->api_interface); + + PluginManagerError error = PluginManagerErrorNone; + do { + FlipperApplicationPreloadStatus preload_res = flipper_application_preload(lib, path); + + if(preload_res != FlipperApplicationPreloadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to preload %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + if(!flipper_application_is_plugin(lib)) { + FURI_LOG_E(TAG, "Not a plugin %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + FlipperApplicationLoadStatus load_status = flipper_application_map_to_memory(lib); + if(load_status != FlipperApplicationLoadStatusSuccess) { + FURI_LOG_E(TAG, "Failed to load module_demo_plugin1.fal"); + break; + } + + const FlipperAppPluginDescriptor* app_descriptor = + flipper_application_plugin_get_descriptor(lib); + + if(!app_descriptor) { + FURI_LOG_E(TAG, "Failed to get descriptor %s", path); + error = PluginManagerErrorLoaderError; + break; + } + + if(strcmp(app_descriptor->appid, manager->application_id) != 0) { + FURI_LOG_E(TAG, "Application id mismatch %s", path); + error = PluginManagerErrorApplicationIdMismatch; + break; + } + + if(app_descriptor->ep_api_version != manager->api_version) { + FURI_LOG_E(TAG, "API version mismatch %s", path); + error = PluginManagerErrorAPIVersionMismatch; + break; + } + + FlipperApplicationList_push_back(manager->libs, lib); + } while(false); + + if(error != PluginManagerErrorNone) { + flipper_application_free(lib); + } + + return error; +} + +PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path) { + File* directory = storage_file_alloc(manager->storage); + char file_name_buffer[256]; + FuriString* file_name = furi_string_alloc(); + do { + if(!storage_dir_open(directory, path)) { + FURI_LOG_E(TAG, "Failed to open directory %s", path); + break; + } + while(true) { + if(!storage_dir_read(directory, NULL, file_name_buffer, sizeof(file_name_buffer))) { + break; + } + + furi_string_set(file_name, file_name_buffer); + if(!furi_string_end_with_str(file_name, ".fal")) { + continue; + } + + path_concat(path, file_name_buffer, file_name); + FURI_LOG_D(TAG, "Loading %s", furi_string_get_cstr(file_name)); + PluginManagerError error = + plugin_manager_load_single(manager, furi_string_get_cstr(file_name)); + + if(error != PluginManagerErrorNone) { + FURI_LOG_E(TAG, "Failed to load %s", furi_string_get_cstr(file_name)); + break; + } + } + } while(false); + storage_dir_close(directory); + storage_file_free(directory); + furi_string_free(file_name); + return PluginManagerErrorNone; +} + +uint32_t plugin_manager_get_count(PluginManager* manager) { + return FlipperApplicationList_size(manager->libs); +} + +const FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index) { + FlipperApplication* app = *FlipperApplicationList_get(manager->libs, index); + return flipper_application_plugin_get_descriptor(app); +} + +const void* plugin_manager_get_ep(PluginManager* manager, uint32_t index) { + const FlipperAppPluginDescriptor* lib_descr = plugin_manager_get(manager, index); + furi_check(lib_descr); + return lib_descr->entry_point; +} diff --git a/lib/flipper_application/plugins/plugin_manager.h b/lib/flipper_application/plugins/plugin_manager.h new file mode 100644 index 000000000..d94c25db9 --- /dev/null +++ b/lib/flipper_application/plugins/plugin_manager.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Object that manages plugins for an application + * Implements mass loading of plugins and provides access to their descriptors + */ +typedef struct PluginManager PluginManager; + +typedef enum { + PluginManagerErrorNone = 0, + PluginManagerErrorLoaderError, + PluginManagerErrorApplicationIdMismatch, + PluginManagerErrorAPIVersionMismatch, +} PluginManagerError; + +/** + * @brief Allocates new PluginManager + * @param application_id Application ID filter - only plugins with matching ID will be loaded + * @param api_version Application API version filter - only plugins with matching API version + * @param api_interface Application API interface - used to resolve plugins' API imports + * If plugin uses private application's API, use CompoundApiInterface + * @return new PluginManager instance + */ +PluginManager* plugin_manager_alloc( + const char* application_id, + uint32_t api_version, + const ElfApiInterface* api_interface); + +/** + * @brief Frees PluginManager + * @param manager PluginManager instance + */ +void plugin_manager_free(PluginManager* manager); + +/** + * @brief Loads single plugin by full path + * @param manager PluginManager instance + * @param path Path to plugin + * @return Error code + */ +PluginManagerError plugin_manager_load_single(PluginManager* manager, const char* path); + +/** + * @brief Loads all plugins from specified directory + * @param manager PluginManager instance + * @param path Path to directory + * @return Error code + */ +PluginManagerError plugin_manager_load_all(PluginManager* manager, const char* path); + +/** + * @brief Returns number of loaded plugins + * @param manager PluginManager instance + * @return Number of loaded plugins + */ +uint32_t plugin_manager_get_count(PluginManager* manager); + +/** + * @brief Returns plugin descriptor by index + * @param manager PluginManager instance + * @param index Plugin index + * @return Plugin descriptor + */ +const FlipperAppPluginDescriptor* plugin_manager_get(PluginManager* manager, uint32_t index); + +/** + * @brief Returns plugin entry point by index + * @param manager PluginManager instance + * @param index Plugin index + * @return Plugin entry point + */ +const void* plugin_manager_get_ep(PluginManager* manager, uint32_t index); + +#ifdef __cplusplus +} +#endif diff --git a/scripts/distfap.py b/scripts/distfap.py new file mode 100644 index 000000000..060fe26ff --- /dev/null +++ b/scripts/distfap.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +from flipper.app import App +from flipper.storage import FlipperStorage, FlipperStorageOperations +from flipper.utils.cdc import resolve_port + +import os +import posixpath + + +class Main(App): + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + self.parser.add_argument( + "-n", + "--no-launch", + dest="launch_app", + action="store_false", + help="Don't launch app", + ) + + self.parser.add_argument("fap_src_path", help="App file to upload") + self.parser.add_argument( + "--fap_dst_dir", help="Upload path", default="/ext/apps", required=False + ) + self.parser.set_defaults(func=self.install) + + def install(self): + if not (port := resolve_port(self.logger, self.args.port)): + return 1 + + try: + with FlipperStorage(port) as storage: + storage_ops = FlipperStorageOperations(storage) + fap_local_path = self.args.fap_src_path + self.args.fap_dst_dir = self.args.fap_dst_dir.rstrip("/\\") + + if not os.path.isfile(fap_local_path): + self.logger.error( + f"Error: source .fap ({fap_local_path}) not found" + ) + return 2 + + fap_dst_path = posixpath.join( + self.args.fap_dst_dir, os.path.basename(fap_local_path) + ) + + self.logger.info(f'Installing "{fap_local_path}" to {fap_dst_path}') + + storage_ops.recursive_send(fap_dst_path, fap_local_path, False) + + if not self.args.launch_app: + return 0 + + storage.send_and_wait_eol( + f'loader open "Applications" {fap_dst_path}\r' + ) + + if len(result := storage.read.until(storage.CLI_EOL)): + self.logger.error(f"Unexpected response: {result.decode('ascii')}") + return 3 + return 0 + + except Exception as e: + self.logger.error(f"Error: {e}") + # raise + return 4 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index eb1652b78..37ddc4348 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -12,13 +12,13 @@ class FlipperAppType(Enum): SERVICE = "Service" SYSTEM = "System" APP = "App" - PLUGIN = "Plugin" DEBUG = "Debug" ARCHIVE = "Archive" SETTINGS = "Settings" STARTUP = "StartupHook" EXTERNAL = "External" METAPACKAGE = "Package" + PLUGIN = "Plugin" @dataclass @@ -69,12 +69,22 @@ class FlipperApplication: fap_private_libs: List[Library] = field(default_factory=list) fap_file_assets: Optional[str] = None # Internally used by fbt + _appmanager: Optional["AppManager"] = None _appdir: Optional[object] = None _apppath: Optional[str] = None + _plugins: List["FlipperApplication"] = field(default_factory=list) def supports_hardware_target(self, target: str): return target in self.targets or "all" in self.targets + @property + def is_default_deployable(self): + return self.apptype != FlipperAppType.DEBUG and self.fap_category != "Examples" + + def __post_init__(self): + if self.apptype == FlipperAppType.PLUGIN: + self.stack_size = 0 + class AppManager: def __init__(self): @@ -94,6 +104,23 @@ class AppManager: return app return None + def _validate_app_params(self, *args, **kw): + apptype = kw.get("apptype") + if apptype == FlipperAppType.PLUGIN: + if kw.get("stack_size"): + raise FlipperManifestException( + f"Plugin {kw.get('appid')} cannot have stack (did you mean FlipperAppType.EXTERNAL?)" + ) + if not kw.get("requires"): + raise FlipperManifestException( + f"Plugin {kw.get('appid')} must have 'requires' in manifest" + ) + # Harmless - cdefines for external apps are meaningless + # if apptype == FlipperAppType.EXTERNAL and kw.get("cdefines"): + # raise FlipperManifestException( + # f"External app {kw.get('appid')} must not have 'cdefines' in manifest" + # ) + def load_manifest(self, app_manifest_path: str, app_dir_node: object): if not os.path.exists(app_manifest_path): raise FlipperManifestException( @@ -105,12 +132,14 @@ class AppManager: def App(*args, **kw): nonlocal app_manifests + self._validate_app_params(*args, **kw) app_manifests.append( FlipperApplication( *args, **kw, _appdir=app_dir_node, _apppath=os.path.dirname(app_manifest_path), + _appmanager=self, ), ) @@ -155,7 +184,6 @@ class AppBuildset: FlipperAppType.SERVICE, FlipperAppType.SYSTEM, FlipperAppType.APP, - FlipperAppType.PLUGIN, FlipperAppType.DEBUG, FlipperAppType.ARCHIVE, FlipperAppType.SETTINGS, @@ -182,6 +210,7 @@ class AppBuildset: self._check_conflicts() self._check_unsatisfied() # unneeded? self._check_target_match() + self._group_plugins() self.apps = sorted( list(map(self.appmgr.get, self.appnames)), key=lambda app: app.appid, @@ -260,6 +289,18 @@ class AppBuildset: f"Apps incompatible with target {self.hw_target}: {', '.join(incompatible)}" ) + def _group_plugins(self): + known_extensions = self.get_apps_of_type(FlipperAppType.PLUGIN, all_known=True) + for extension_app in known_extensions: + for parent_app_id in extension_app.requires: + try: + parent_app = self.appmgr.get(parent_app_id) + parent_app._plugins.append(extension_app) + except FlipperManifestException as e: + self._writer( + f"Module {extension_app.appid} has unknown parent {parent_app_id}" + ) + def get_apps_cdefs(self): cdefs = set() for app in self.apps: @@ -301,7 +342,6 @@ class ApplicationsCGenerator: FlipperAppType.SERVICE: ("FlipperApplication", "FLIPPER_SERVICES"), FlipperAppType.SYSTEM: ("FlipperApplication", "FLIPPER_SYSTEM_APPS"), FlipperAppType.APP: ("FlipperApplication", "FLIPPER_APPS"), - FlipperAppType.PLUGIN: ("FlipperApplication", "FLIPPER_PLUGINS"), FlipperAppType.DEBUG: ("FlipperApplication", "FLIPPER_DEBUG_APPS"), FlipperAppType.SETTINGS: ("FlipperApplication", "FLIPPER_SETTINGS_APPS"), FlipperAppType.STARTUP: ("FlipperOnStartHook", "FLIPPER_ON_SYSTEM_START"), diff --git a/scripts/fbt/fapassets.py b/scripts/fbt/fapassets.py new file mode 100644 index 000000000..0649f03ef --- /dev/null +++ b/scripts/fbt/fapassets.py @@ -0,0 +1,108 @@ +import os +import hashlib +import struct +from typing import TypedDict + + +class File(TypedDict): + path: str + size: int + content_path: str + + +class Dir(TypedDict): + path: str + + +class FileBundler: + """ + u32 magic + u32 version + u32 dirs_count + u32 files_count + u32 signature_size + u8[] signature + Dirs: + u32 dir_name length + u8[] dir_name + Files: + u32 file_name length + u8[] file_name + u32 file_content_size + u8[] file_content + """ + + def __init__(self, directory_path: str): + self.directory_path = directory_path + self.file_list: list[File] = [] + self.directory_list: list[Dir] = [] + self._gather() + + def _gather(self): + for root, dirs, files in os.walk(self.directory_path): + for file_info in files: + file_path = os.path.join(root, file_info) + file_size = os.path.getsize(file_path) + self.file_list.append( + { + "path": os.path.relpath(file_path, self.directory_path), + "size": file_size, + "content_path": file_path, + } + ) + + for dir_info in dirs: + dir_path = os.path.join(root, dir_info) + # dir_size = sum( + # os.path.getsize(os.path.join(dir_path, f)) for f in os.listdir(dir_path) + # ) + self.directory_list.append( + { + "path": os.path.relpath(dir_path, self.directory_path), + } + ) + + self.file_list.sort(key=lambda f: f["path"]) + self.directory_list.sort(key=lambda d: d["path"]) + + def export(self, target_path: str): + self._md5_hash = hashlib.md5() + with open(target_path, "wb") as f: + # Write header magic and version + f.write(struct.pack(" FlipperExternalAppInfo + EXT_LIBS={}, + _APP_ICONS=[], ) env.AddMethod(BuildAppElf) - env.AddMethod(GetExtAppFromPath) + env.AddMethod(GetExtAppByIdOrPath) env.Append( BUILDERS={ "FapDist": Builder( @@ -466,7 +427,7 @@ def generate(env, **kw): generator=generate_embed_app_metadata_actions, suffix=".fap", src_suffix=".elf", - # emitter=generate_embed_app_metadata_emitter, + emitter=embed_app_metadata_emitter, ), "ValidateAppImports": Builder( action=[ diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index 3a37eacc9..324819818 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -220,7 +220,7 @@ def gen_sdk_data(sdk_cache: SdkCache): def _check_sdk_is_up2date(sdk_cache: SdkCache): if not sdk_cache.is_buildable(): raise UserError( - "SDK version is not finalized, please review changes and re-run operation" + "SDK version is not finalized, please review changes and re-run operation. See AppsOnSDCard.md for more details" ) diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index 9c9f52958..47e11236d 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -4,6 +4,9 @@ import serial import time import hashlib import math +import logging +import posixpath +import enum def timing(func): @@ -25,12 +28,47 @@ def timing(func): return wrapper +class StorageErrorCode(enum.Enum): + OK = "OK" + NOT_READY = "filesystem not ready" + EXIST = "file/dir already exist" + NOT_EXIST = "file/dir not exist" + INVALID_PARAMETER = "invalid parameter" + DENIED = "access denied" + INVALID_NAME = "invalid name/path" + INTERNAL = "internal error" + NOT_IMPLEMENTED = "function not implemented" + ALREADY_OPEN = "file is already open" + UNKNOWN = "unknown error" + + @property + def is_error(self): + return self != self.OK + + @classmethod + def from_value(cls, s: str | bytes): + if isinstance(s, bytes): + s = s.decode("ascii") + for code in cls: + if code.value == s: + return code + return cls.UNKNOWN + + +class FlipperStorageException(Exception): + def __init__(self, message): + super().__init__(f"Storage error: {message}") + + def __init__(self, path: str, error_code: StorageErrorCode): + super().__init__(f"Storage error: path '{path}': {error_code.value}") + + class BufferedRead: def __init__(self, stream): self.buffer = bytearray() self.stream = stream - def until(self, eol="\n", cut_eol=True): + def until(self, eol: str = "\n", cut_eol: bool = True): eol = eol.encode("ascii") while True: # search in buffer @@ -59,9 +97,15 @@ class FlipperStorage: self.port.timeout = 2 self.port.baudrate = 115200 # Doesn't matter for VCP self.read = BufferedRead(self.port) - self.last_error = "" self.chunk_size = chunk_size + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.stop() + def start(self): self.port.open() self.port.reset_input_buffer() @@ -71,37 +115,34 @@ class FlipperStorage: # And read buffer until we get prompt self.read.until(self.CLI_PROMPT) - def stop(self): + def stop(self) -> None: self.port.close() - def send(self, line): + def send(self, line: str) -> None: self.port.write(line.encode("ascii")) - def send_and_wait_eol(self, line): + def send_and_wait_eol(self, line: str): self.send(line) return self.read.until(self.CLI_EOL) - def send_and_wait_prompt(self, line): + def send_and_wait_prompt(self, line: str): self.send(line) return self.read.until(self.CLI_PROMPT) - def has_error(self, data): - """Is data has error""" - if data.find(b"Storage error") != -1: - return True - else: - return False + def has_error(self, data: bytes | str) -> bool: + """Is data an error message""" + return data.find(b"Storage error:") != -1 - def get_error(self, data): + def get_error(self, data: bytes) -> StorageErrorCode: """Extract error text from data and print it""" - error, error_text = data.decode("ascii").split(": ") - return error_text.strip() + _, error_text = data.decode("ascii").split(": ") + return StorageErrorCode.from_value(error_text.strip()) - def list_tree(self, path="/", level=0): + def list_tree(self, path: str = "/", level: int = 0): """List files and dirs on Flipper""" path = path.replace("//", "/") - self.send_and_wait_eol('storage list "' + path + '"\r') + self.send_and_wait_eol(f'storage list "{path}"\r') data = self.read.until(self.CLI_PROMPT) lines = data.split(b"\r\n") @@ -139,7 +180,7 @@ class FlipperStorage: # Something wrong, pass pass - def walk(self, path="/"): + def walk(self, path: str = "/"): dirs = [] nondirs = [] walk_dirs = [] @@ -181,14 +222,15 @@ class FlipperStorage: # Something wrong, pass pass - # topdown walk, yield before recursy + # topdown walk, yield before recursing yield path, dirs, nondirs for new_path in walk_dirs: yield from self.walk(new_path) - def send_file(self, filename_from, filename_to): + def send_file(self, filename_from: str, filename_to: str): """Send file from local device to Flipper""" - self.remove(filename_to) + if self.exist_file(filename_to): + self.remove(filename_to) with open(filename_from, "rb") as file: filesize = os.fstat(file.fileno()).st_size @@ -203,9 +245,9 @@ class FlipperStorage: self.send_and_wait_eol(f'storage write_chunk "{filename_to}" {size}\r') answer = self.read.until(self.CLI_EOL) if self.has_error(answer): - self.last_error = self.get_error(answer) + last_error = self.get_error(answer) self.read.until(self.CLI_PROMPT) - return False + raise FlipperStorageException(filename_to, last_error) self.port.write(filedata) self.read.until(self.CLI_PROMPT) @@ -218,9 +260,8 @@ class FlipperStorage: ) sys.stdout.flush() print() - return True - def read_file(self, filename): + def read_file(self, filename: str): """Receive file from Flipper, and get filedata (bytes)""" buffer_size = self.chunk_size self.send_and_wait_eol( @@ -229,9 +270,10 @@ class FlipperStorage: answer = self.read.until(self.CLI_EOL) filedata = bytearray() if self.has_error(answer): - self.last_error = self.get_error(answer) + last_error = self.get_error(answer) self.read.until(self.CLI_PROMPT) - return filedata + raise FlipperStorageException(filename, last_error) + # return filedata size = int(answer.split(b": ")[1]) read_size = 0 @@ -251,121 +293,89 @@ class FlipperStorage: self.read.until(self.CLI_PROMPT) return filedata - def receive_file(self, filename_from, filename_to): + def receive_file(self, filename_from: str, filename_to: str): """Receive file from Flipper to local storage""" with open(filename_to, "wb") as file: data = self.read_file(filename_from) - if not data: - return False - else: - file.write(data) - return True + file.write(data) - def exist(self, path): - """Is file or dir exist on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + def exist(self, path: str): + """Does file or dir exist on Flipper""" + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True + return not self.has_error(response) - def exist_dir(self, path): - """Is dir exist on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + def exist_dir(self, path: str): + """Does dir exist on Flipper""" + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) + self.read.until(self.CLI_PROMPT) + if self.has_error(response): + error_code = self.get_error(response) + if error_code in ( + StorageErrorCode.NOT_EXIST, + StorageErrorCode.INVALID_NAME, + ): + return False + raise FlipperStorageException(path, error_code) + + return True + + def exist_file(self, path: str): + """Does file exist on Flipper""" + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - if answer.find(b"Directory") != -1: - return True - elif answer.find(b"Storage") != -1: - return True - else: - return False + return response.find(b"File, size:") != -1 - def exist_file(self, path): - """Is file exist on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) - self.read.until(self.CLI_PROMPT) + def _check_no_error(self, response, path=None): + if self.has_error(response): + raise FlipperStorageException(self.get_error(response)) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - if answer.find(b"File, size:") != -1: - return True - else: - return False - - def size(self, path): + def size(self, path: str): """file size on Flipper""" - self.send_and_wait_eol('storage stat "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + self.send_and_wait_eol(f'storage stat "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - if answer.find(b"File, size:") != -1: - size = int( - "".join( - ch - for ch in answer.split(b": ")[1].decode("ascii") - if ch.isdigit() - ) + self._check_no_error(response, path) + if response.find(b"File, size:") != -1: + size = int( + "".join( + ch + for ch in response.split(b": ")[1].decode("ascii") + if ch.isdigit() ) - return size - else: - self.last_error = "access denied" - return -1 + ) + return size + raise FlipperStorageException("Not a file") - def mkdir(self, path): + def mkdir(self, path: str): """Create a directory on Flipper""" - self.send_and_wait_eol('storage mkdir "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + self.send_and_wait_eol(f'storage mkdir "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) - - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True + self._check_no_error(response, path) def format_ext(self): - """Create a directory on Flipper""" + """Format external storage on Flipper""" self.send_and_wait_eol("storage format /ext\r") self.send_and_wait_eol("y\r") - answer = self.read.until(self.CLI_EOL) + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) + self._check_no_error(response, "/ext") - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True - - def remove(self, path): + def remove(self, path: str): """Remove file or directory on Flipper""" - self.send_and_wait_eol('storage remove "' + path + '"\r') - answer = self.read.until(self.CLI_EOL) + self.send_and_wait_eol(f'storage remove "{path}"\r') + response = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) + self._check_no_error(response, path) - if self.has_error(answer): - self.last_error = self.get_error(answer) - return False - else: - return True - - def hash_local(self, filename): + def hash_local(self, filename: str): """Hash of local file""" hash_md5 = hashlib.md5() with open(filename, "rb") as f: @@ -373,14 +383,112 @@ class FlipperStorage: hash_md5.update(chunk) return hash_md5.hexdigest() - def hash_flipper(self, filename): + def hash_flipper(self, filename: str): """Get hash of file on Flipper""" self.send_and_wait_eol('storage md5 "' + filename + '"\r') hash = self.read.until(self.CLI_EOL) self.read.until(self.CLI_PROMPT) + self._check_no_error(hash, filename) + return hash.decode("ascii") - if self.has_error(hash): - self.last_error = self.get_error(hash) - return "" + +class FlipperStorageOperations: + def __init__(self, storage): + self.storage: FlipperStorage = storage + self.logger = logging.getLogger("FStorageOps") + + def send_file_to_storage( + self, flipper_file_path: str, local_file_path: str, force: bool = False + ): + self.logger.debug( + f"* send_file_to_storage: {local_file_path}->{flipper_file_path}, {force=}" + ) + exists = self.storage.exist_file(flipper_file_path) + do_upload = not exists + if exists: + hash_local = self.storage.hash_local(local_file_path) + hash_flipper = self.storage.hash_flipper(flipper_file_path) + self.logger.debug(f"hash check: local {hash_local}, flipper {hash_flipper}") + do_upload = force or (hash_local != hash_flipper) + + if do_upload: + self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') + self.storage.send_file(local_file_path, flipper_file_path) + + # make directory with exist check + def mkpath(self, flipper_dir_path: str): + path_components, dirs_to_create = flipper_dir_path.split("/"), [] + while not self.storage.exist_dir(dir_path := "/".join(path_components)): + self.logger.debug(f'"{dir_path}" does not exist, will create') + dirs_to_create.append(path_components.pop()) + for dir_to_create in reversed(dirs_to_create): + path_components.append(dir_to_create) + self.storage.mkdir("/".join(path_components)) + + # send file or folder recursively + def recursive_send(self, flipper_path: str, local_path: str, force: bool = False): + if not os.path.exists(local_path): + raise FlipperStorageException(f'"{local_path}" does not exist') + + if os.path.isdir(local_path): + # create parent dir + self.mkpath(flipper_path) + + for dirpath, dirnames, filenames in os.walk(local_path): + self.logger.debug(f'Processing directory "{os.path.normpath(dirpath)}"') + dirnames.sort() + filenames.sort() + rel_path = os.path.relpath(dirpath, local_path) + + # create subdirs + for dirname in dirnames: + flipper_dir_path = os.path.join(flipper_path, rel_path, dirname) + flipper_dir_path = os.path.normpath(flipper_dir_path).replace( + os.sep, "/" + ) + self.mkpath(flipper_dir_path) + + # send files + for filename in filenames: + flipper_file_path = os.path.join(flipper_path, rel_path, filename) + flipper_file_path = os.path.normpath(flipper_file_path).replace( + os.sep, "/" + ) + local_file_path = os.path.normpath(os.path.join(dirpath, filename)) + self.send_file_to_storage(flipper_file_path, local_file_path, force) else: - return hash.decode("ascii") + self.mkpath(posixpath.dirname(flipper_path)) + self.send_file_to_storage(flipper_path, local_path, force) + + def recursive_receive(self, flipper_path: str, local_path: str): + if self.storage.exist_dir(flipper_path): + for dirpath, dirnames, filenames in self.storage.walk(flipper_path): + self.logger.debug( + f'Processing directory "{os.path.normpath(dirpath)}"'.replace( + os.sep, "/" + ) + ) + dirnames.sort() + filenames.sort() + + rel_path = os.path.relpath(dirpath, flipper_path) + + for dirname in dirnames: + local_dir_path = os.path.join(local_path, rel_path, dirname) + local_dir_path = os.path.normpath(local_dir_path) + os.makedirs(local_dir_path, exist_ok=True) + + for filename in filenames: + local_file_path = os.path.join(local_path, rel_path, filename) + local_file_path = os.path.normpath(local_file_path) + flipper_file_path = os.path.normpath( + os.path.join(dirpath, filename) + ).replace(os.sep, "/") + self.logger.info( + f'Receiving "{flipper_file_path}" to "{local_file_path}"' + ) + self.storage.receive_file(flipper_file_path, local_file_path) + + else: + self.logger.info(f'Receiving "{flipper_path}" to "{local_path}"') + self.storage.receive_file(flipper_path, local_path) diff --git a/scripts/requirements.txt b/scripts/requirements.txt deleted file mode 100644 index 5b6fac5f7..000000000 --- a/scripts/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -ansi==0.3.6 -black==22.6.0 -colorlog==6.7.0 -heatshrink2==0.11.0 -Pillow==9.1.1 -protobuf==3.20.1 -pyserial==3.5 -python3-protobuf==2.5.0 -SCons==4.4.0 diff --git a/scripts/runfap.py b/scripts/runfap.py index 410b3e7d2..f8ff607c1 100644 --- a/scripts/runfap.py +++ b/scripts/runfap.py @@ -1,108 +1,86 @@ #!/usr/bin/env python3 -import posixpath -from typing import final from flipper.app import App -from flipper.storage import FlipperStorage +from flipper.storage import FlipperStorage, FlipperStorageOperations from flipper.utils.cdc import resolve_port -import logging import os -import pathlib -import serial.tools.list_ports as list_ports +import posixpath +from functools import reduce +import operator class Main(App): def init(self): self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") self.parser.add_argument( - "-n", - "--no-launch", - dest="launch_app", - action="store_false", - help="Don't launch app", + "--sources", + "-s", + nargs="+", + action="append", + default=[], + help="Files to send", + ) + self.parser.add_argument( + "--targets", + "-t", + nargs="+", + action="append", + default=[], + help="File destinations (must be same length as -s)", + ) + self.parser.add_argument( + "--host-app", + "-a", + help="Host app to launch", ) - self.parser.add_argument("fap_src_path", help="App file to upload") - self.parser.add_argument( - "--fap_dst_dir", help="Upload path", default="/ext/apps", required=False - ) self.parser.set_defaults(func=self.install) - # logging - self.logger = logging.getLogger() - - # make directory with exist check - def mkdir_on_storage(self, storage, flipper_dir_path): - if not storage.exist_dir(flipper_dir_path): - self.logger.debug(f'"{flipper_dir_path}" does not exist, creating') - if not storage.mkdir(flipper_dir_path): - self.logger.error(f"Error: {storage.last_error}") - return False - else: - self.logger.debug(f'"{flipper_dir_path}" already exists') - return True - - # send file with exist check and hash check - def send_file_to_storage(self, storage, flipper_file_path, local_file_path, force): - exists = storage.exist_file(flipper_file_path) - do_upload = not exists - if exists: - hash_local = storage.hash_local(local_file_path) - hash_flipper = storage.hash_flipper(flipper_file_path) - self.logger.debug(f"hash check: local {hash_local}, flipper {hash_flipper}") - do_upload = force or (hash_local != hash_flipper) - - if do_upload: - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - return False - return True + @staticmethod + def flatten(l): + return reduce(operator.concat, l, []) def install(self): - if not (port := resolve_port(self.logger, self.args.port)): + self.args.sources = self.flatten(self.args.sources) + self.args.targets = self.flatten(self.args.targets) + + if len(self.args.sources) != len(self.args.targets): + self.logger.error( + f"Error: sources ({self.args.sources}) and targets ({self.args.targets}) must be same length" + ) return 1 - storage = FlipperStorage(port) - storage.start() + if not (port := resolve_port(self.logger, self.args.port)): + return 2 try: - fap_local_path = self.args.fap_src_path - self.args.fap_dst_dir = self.args.fap_dst_dir.rstrip("/\\") + with FlipperStorage(port) as storage: + storage_ops = FlipperStorageOperations(storage) + for fap_local_path, fap_dst_path in zip( + self.args.sources, self.args.targets + ): + self.logger.info(f'Installing "{fap_local_path}" to {fap_dst_path}') - if not os.path.isfile(fap_local_path): - self.logger.error(f"Error: source .fap ({fap_local_path}) not found") - return -1 + storage_ops.recursive_send(fap_dst_path, fap_local_path, False) - fap_dst_path = posixpath.join( - self.args.fap_dst_dir, os.path.basename(fap_local_path) - ) + fap_host_app = self.args.targets[0] + startup_command = f'"Applications" {fap_host_app}' + if self.args.host_app: + startup_command = self.args.host_app - self.logger.info(f'Installing "{fap_local_path}" to {fap_dst_path}') + self.logger.info(f"Launching app: {startup_command}") + storage.send_and_wait_eol(f"loader open {startup_command}\r") - if not self.mkdir_on_storage(storage, self.args.fap_dst_dir): - self.logger.error(f"Error: cannot create dir: {storage.last_error}") - return -2 - - if not self.send_file_to_storage( - storage, fap_dst_path, fap_local_path, False - ): - self.logger.error(f"Error: upload failed: {storage.last_error}") - return -3 - - if self.args.launch_app: - storage.send_and_wait_eol( - f'loader open "Applications" {fap_dst_path}\r' - ) - result = storage.read.until(storage.CLI_EOL) - if len(result): + if len(result := storage.read.until(storage.CLI_EOL)): self.logger.error(f"Unexpected response: {result.decode('ascii')}") - return -4 + return 3 + return 0 - return 0 - finally: - storage.stop() + except Exception as e: + self.logger.error(f"Error: {e}") + # raise + return 4 if __name__ == "__main__": diff --git a/scripts/selfupdate.py b/scripts/selfupdate.py index 1c16c5ca6..9bfbfefa3 100644 --- a/scripts/selfupdate.py +++ b/scripts/selfupdate.py @@ -2,7 +2,7 @@ from typing import final from flipper.app import App -from flipper.storage import FlipperStorage +from flipper.storage import FlipperStorage, FlipperStorageOperations from flipper.utils.cdc import resolve_port import logging @@ -24,89 +24,47 @@ class Main(App): # logging self.logger = logging.getLogger() - # make directory with exist check - def mkdir_on_storage(self, storage, flipper_dir_path): - if not storage.exist_dir(flipper_dir_path): - self.logger.debug(f'"{flipper_dir_path}" does not exist, creating') - if not storage.mkdir(flipper_dir_path): - self.logger.error(f"Error: {storage.last_error}") - return False - else: - self.logger.debug(f'"{flipper_dir_path}" already exists') - return True - - # send file with exist check and hash check - def send_file_to_storage(self, storage, flipper_file_path, local_file_path, force): - exists = storage.exist_file(flipper_file_path) - do_upload = not exists - if exists: - hash_local = storage.hash_local(local_file_path) - hash_flipper = storage.hash_flipper(flipper_file_path) - self.logger.debug(f"hash check: local {hash_local}, flipper {hash_flipper}") - do_upload = force or (hash_local != hash_flipper) - - if do_upload: - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - return False - return True - def install(self): if not (port := resolve_port(self.logger, self.args.port)): return 1 - storage = FlipperStorage(port) - storage.start() + if not os.path.isfile(self.args.manifest_path): + self.logger.error("Error: manifest not found") + return 2 + + manifest_path = pathlib.Path(os.path.abspath(self.args.manifest_path)) + manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2] + + pkg_dir_name = self.args.pkg_dir_name or pkg_name + update_root = "/ext/update" + flipper_update_path = f"{update_root}/{pkg_dir_name}" + + self.logger.info(f'Installing "{pkg_name}" from {flipper_update_path}') try: - if not os.path.isfile(self.args.manifest_path): - self.logger.error("Error: manifest not found") - return 2 + with FlipperStorage(port) as storage: + storage_ops = FlipperStorageOperations(storage) + storage_ops.mkpath(update_root) + storage_ops.mkpath(flipper_update_path) + storage_ops.recursive_send( + flipper_update_path, manifest_path.parents[0] + ) - manifest_path = pathlib.Path(os.path.abspath(self.args.manifest_path)) - manifest_name, pkg_name = manifest_path.parts[-1], manifest_path.parts[-2] - - pkg_dir_name = self.args.pkg_dir_name or pkg_name - update_root = "/ext/update" - flipper_update_path = f"{update_root}/{pkg_dir_name}" - - self.logger.info(f'Installing "{pkg_name}" from {flipper_update_path}') - # if not os.path.exists(self.args.manifest_path): - # self.logger.error("Error: package not found") - if not self.mkdir_on_storage( - storage, update_root - ) or not self.mkdir_on_storage(storage, flipper_update_path): - self.logger.error(f"Error: cannot create {storage.last_error}") - return -2 - - for dirpath, dirnames, filenames in os.walk(manifest_path.parents[0]): - for fname in filenames: - self.logger.debug(f"Uploading {fname}") - local_file_path = os.path.join(dirpath, fname) - flipper_file_path = f"{flipper_update_path}/{fname}" - if not self.send_file_to_storage( - storage, flipper_file_path, local_file_path, False - ): - self.logger.error(f"Error: {storage.last_error}") - return -3 - - # return -11 storage.send_and_wait_eol( f"update install {flipper_update_path}/{manifest_name}\r" ) result = storage.read.until(storage.CLI_EOL) if not b"Verifying" in result: self.logger.error(f"Unexpected response: {result.decode('ascii')}") - return -4 + return 3 result = storage.read.until(storage.CLI_EOL) if not result.startswith(b"OK"): self.logger.error(result.decode("ascii")) - return -5 - break - return 0 - finally: - storage.stop() + return 4 + return 0 + except Exception as e: + self.logger.error(e) + return 5 if __name__ == "__main__": diff --git a/scripts/storage.py b/scripts/storage.py index ee5dabd43..84c01021a 100755 --- a/scripts/storage.py +++ b/scripts/storage.py @@ -1,16 +1,28 @@ #!/usr/bin/env python3 from flipper.app import App -from flipper.storage import FlipperStorage +from flipper.storage import FlipperStorage, FlipperStorageOperations from flipper.utils.cdc import resolve_port -import logging import os import binascii import filecmp import tempfile +def WrapStorageOp(func): + def wrapper(*args, **kwargs): + try: + func(*args, **kwargs) + return 0 + except Exception as e: + print(f"Error: {e}") + # raise # uncomment to debug + return 1 + + return wrapper + + class Main(App): def init(self): self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") @@ -71,229 +83,71 @@ class Main(App): ) self.parser_stress.set_defaults(func=self.stress) - def _get_storage(self): + def _get_port(self): if not (port := resolve_port(self.logger, self.args.port)): - return None - - storage = FlipperStorage(port) - storage.start() - return storage + raise Exception("Failed to resolve port") + return port + @WrapStorageOp def mkdir(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Creating "{self.args.flipper_path}"') - if not storage.mkdir(self.args.flipper_path): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + storage.mkdir(self.args.flipper_path) + @WrapStorageOp def remove(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Removing "{self.args.flipper_path}"') - if not storage.remove(self.args.flipper_path): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + storage.remove(self.args.flipper_path) + @WrapStorageOp def receive(self): - if not (storage := self._get_storage()): - return 1 - - if storage.exist_dir(self.args.flipper_path): - for dirpath, dirnames, filenames in storage.walk(self.args.flipper_path): - self.logger.debug( - f'Processing directory "{os.path.normpath(dirpath)}"'.replace( - os.sep, "/" - ) - ) - dirnames.sort() - filenames.sort() - - rel_path = os.path.relpath(dirpath, self.args.flipper_path) - - for dirname in dirnames: - local_dir_path = os.path.join( - self.args.local_path, rel_path, dirname - ) - local_dir_path = os.path.normpath(local_dir_path) - os.makedirs(local_dir_path, exist_ok=True) - - for filename in filenames: - local_file_path = os.path.join( - self.args.local_path, rel_path, filename - ) - local_file_path = os.path.normpath(local_file_path) - flipper_file_path = os.path.normpath( - os.path.join(dirpath, filename) - ).replace(os.sep, "/") - self.logger.info( - f'Receiving "{flipper_file_path}" to "{local_file_path}"' - ) - if not storage.receive_file(flipper_file_path, local_file_path): - self.logger.error(f"Error: {storage.last_error}") - - else: - self.logger.info( - f'Receiving "{self.args.flipper_path}" to "{self.args.local_path}"' + with FlipperStorage(self._get_port()) as storage: + FlipperStorageOperations(storage).recursive_receive( + self.args.flipper_path, self.args.local_path ) - if not storage.receive_file(self.args.flipper_path, self.args.local_path): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 + @WrapStorageOp def send(self): - if not (storage := self._get_storage()): - return 1 - - self.send_to_storage( - storage, self.args.flipper_path, self.args.local_path, self.args.force - ) - storage.stop() - return 0 - - # send file or folder recursively - def send_to_storage(self, storage, flipper_path, local_path, force): - if not os.path.exists(local_path): - self.logger.error(f'Error: "{local_path}" is not exist') - - if os.path.isdir(local_path): - # create parent dir - self.mkdir_on_storage(storage, flipper_path) - - for dirpath, dirnames, filenames in os.walk(local_path): - self.logger.debug(f'Processing directory "{os.path.normpath(dirpath)}"') - dirnames.sort() - filenames.sort() - rel_path = os.path.relpath(dirpath, local_path) - - # create subdirs - for dirname in dirnames: - flipper_dir_path = os.path.join(flipper_path, rel_path, dirname) - flipper_dir_path = os.path.normpath(flipper_dir_path).replace( - os.sep, "/" - ) - self.mkdir_on_storage(storage, flipper_dir_path) - - # send files - for filename in filenames: - flipper_file_path = os.path.join(flipper_path, rel_path, filename) - flipper_file_path = os.path.normpath(flipper_file_path).replace( - os.sep, "/" - ) - local_file_path = os.path.normpath(os.path.join(dirpath, filename)) - self.send_file_to_storage( - storage, flipper_file_path, local_file_path, force - ) - else: - self.send_file_to_storage(storage, flipper_path, local_path, force) - - # make directory with exist check - def mkdir_on_storage(self, storage, flipper_dir_path): - if not storage.exist_dir(flipper_dir_path): - self.logger.debug(f'"{flipper_dir_path}" does not exist, creating') - if not storage.mkdir(flipper_dir_path): - self.logger.error(f"Error: {storage.last_error}") - else: - self.logger.debug(f'"{flipper_dir_path}" already exists') - - # send file with exist check and hash check - def send_file_to_storage(self, storage, flipper_file_path, local_file_path, force): - if not storage.exist_file(flipper_file_path): - self.logger.debug( - f'"{flipper_file_path}" does not exist, sending "{local_file_path}"' + with FlipperStorage(self._get_port()) as storage: + FlipperStorageOperations(storage).recursive_send( + self.args.flipper_path, self.args.local_path, self.args.force ) - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - elif force: - self.logger.debug( - f'"{flipper_file_path}" exists, but will be overwritten by "{local_file_path}"' - ) - self.logger.info(f'Sending "{local_file_path}" to "{flipper_file_path}"') - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") - else: - self.logger.debug( - f'"{flipper_file_path}" exists, compare hash with "{local_file_path}"' - ) - hash_local = storage.hash_local(local_file_path) - hash_flipper = storage.hash_flipper(flipper_file_path) - - if not hash_flipper: - self.logger.error(f"Error: {storage.last_error}") - - if hash_local == hash_flipper: - self.logger.debug( - f'"{flipper_file_path}" is equal to "{local_file_path}"' - ) - else: - self.logger.debug( - f'"{flipper_file_path}" is NOT equal to "{local_file_path}"' - ) - self.logger.info( - f'Sending "{local_file_path}" to "{flipper_file_path}"' - ) - if not storage.send_file(local_file_path, flipper_file_path): - self.logger.error(f"Error: {storage.last_error}") + @WrapStorageOp def read(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Reading "{self.args.flipper_path}"') - data = storage.read_file(self.args.flipper_path) - if not data: - self.logger.error(f"Error: {storage.last_error}") - else: + with FlipperStorage(self._get_port()) as storage: + data = storage.read_file(self.args.flipper_path) try: print("Text data:") print(data.decode()) except: print("Binary hexadecimal data:") print(binascii.hexlify(data).decode()) - storage.stop() - return 0 + @WrapStorageOp def size(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Getting size of "{self.args.flipper_path}"') - size = storage.size(self.args.flipper_path) - if size < 0: - self.logger.error(f"Error: {storage.last_error}") - else: - print(size) - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + print(storage.size(self.args.flipper_path)) + @WrapStorageOp def list(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug(f'Listing "{self.args.flipper_path}"') - storage.list_tree(self.args.flipper_path) - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + storage.list_tree(self.args.flipper_path) + @WrapStorageOp def format_ext(self): - if not (storage := self._get_storage()): - return 1 - self.logger.debug("Formatting /ext SD card") + with FlipperStorage(self._get_port()) as storage: + storage.format_ext() - if not storage.format_ext(): - self.logger.error(f"Error: {storage.last_error}") - storage.stop() - return 0 - + @WrapStorageOp def stress(self): self.logger.error("This test is wearing out flash memory.") - self.logger.error("Never use it with internal storage(/int)") + self.logger.error("Never use it with internal storage (/int)") if self.args.flipper_path.startswith( "/int" @@ -312,24 +166,19 @@ class Main(App): with open(send_file_name, "w") as fout: fout.write("A" * self.args.file_size) - storage = self._get_storage() - if not storage: - return 1 - - if storage.exist_file(self.args.flipper_path): - self.logger.error("File exists, remove it first") - return - while self.args.count > 0: - storage.send_file(send_file_name, self.args.flipper_path) - storage.receive_file(self.args.flipper_path, receive_file_name) - if not filecmp.cmp(receive_file_name, send_file_name): - self.logger.error("Files mismatch") - break - storage.remove(self.args.flipper_path) - os.unlink(receive_file_name) - self.args.count -= 1 - storage.stop() - return 0 + with FlipperStorage(self._get_port()) as storage: + if storage.exist_file(self.args.flipper_path): + self.logger.error("File exists, remove it first") + return + while self.args.count > 0: + storage.send_file(send_file_name, self.args.flipper_path) + storage.receive_file(self.args.flipper_path, receive_file_name) + if not filecmp.cmp(receive_file_name, send_file_name): + self.logger.error("Files mismatch") + break + storage.remove(self.args.flipper_path) + os.unlink(receive_file_name) + self.args.count -= 1 if __name__ == "__main__": diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index e3ddc59aa..d832a466e 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -194,10 +194,6 @@ vars.AddVariables( "system_apps", # Settings "settings_apps", - # Plugins - # "basic_plugins", - # Debug - # "debug_apps", ), }, ), @@ -222,7 +218,7 @@ vars.AddVariables( ("applications/settings", False), ("applications/system", False), ("applications/debug", False), - ("applications/plugins", False), + ("applications/external", False), ("applications/examples", False), ("applications_user", False), ], diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index abe1a4534..208b75775 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -1,7 +1,9 @@ from dataclasses import dataclass, field +from os.path import dirname + from SCons.Node import NodeList from SCons.Warnings import warn, WarningOnByDefault - +from SCons.Errors import UserError Import("ENV") @@ -12,7 +14,8 @@ appenv = ENV["APPENV"] = ENV.Clone( "fbt_extapps", "fbt_assets", "fbt_sdk", - ] + ], + RESOURCES_ROOT=ENV.Dir("#/assets/resources"), ) appenv.Replace( @@ -57,7 +60,7 @@ appenv.AppendUnique( @dataclass class FlipperExtAppBuildArtifacts: - applications: dict = field(default_factory=dict) + application_map: dict = field(default_factory=dict) resources_dist: NodeList = field(default_factory=NodeList) sdk_tree: NodeList = field(default_factory=NodeList) @@ -86,6 +89,9 @@ for app in known_extapps: appenv.BuildAppElf(app) +extapps = FlipperExtAppBuildArtifacts() +extapps.application_map = appenv["EXT_APPS"] + if incompatible_apps: warn( WarningOnByDefault, @@ -95,27 +101,60 @@ if incompatible_apps: if appenv["FORCE"]: appenv.AlwaysBuild( - list(app_artifact.compact for app_artifact in appenv["EXT_APPS"].values()) + list(app_artifact.compact for app_artifact in extapps.application_map.values()) ) Alias( - "faps", list(app_artifact.validator for app_artifact in appenv["EXT_APPS"].values()) + "faps", + list(app_artifact.validator for app_artifact in extapps.application_map.values()), ) -extapps = FlipperExtAppBuildArtifacts() -extapps.applications = appenv["EXT_APPS"] -extapps.resources_dist = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) +extapps.resources_dist = appenv.FapDist(appenv["RESOURCES_ROOT"], []) if appsrc := appenv.subst("$APPSRC"): - app_artifacts = appenv.GetExtAppFromPath(appsrc) + deploy_sources, flipp_dist_paths, validators = [], [], [] + run_script_extra_ars = "" + + def _add_dist_targets(app_artifacts): + validators.append(app_artifacts.validator) + for _, ext_path in app_artifacts.dist_entries: + deploy_sources.append(app_artifacts.compact) + flipp_dist_paths.append(f"/ext/{ext_path}") + return app_artifacts + + def _add_host_app_to_targets(host_app): + artifacts_app_to_run = appenv["EXT_APPS"].get(host_app.appid, None) + _add_dist_targets(artifacts_app_to_run) + for plugin in host_app._plugins: + _add_dist_targets(appenv["EXT_APPS"].get(plugin.appid, None)) + + artifacts_app_to_run = appenv.GetExtAppByIdOrPath(appsrc) + if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN: + # We deploy host app instead + host_app = appenv["APPMGR"].get(artifacts_app_to_run.app.requires[0]) + + if host_app: + if host_app.apptype == FlipperAppType.EXTERNAL: + _add_host_app_to_targets(host_app) + else: + # host app is a built-in app + run_script_extra_ars = f"-a {host_app.name}" + _add_dist_targets(artifacts_app_to_run) + else: + raise UserError("Host app is unknown") + else: + _add_host_app_to_targets(artifacts_app_to_run.app) + + # print(deploy_sources, flipp_dist_paths) appenv.PhonyTarget( "launch_app", - '${PYTHON3} "${APP_RUN_SCRIPT}" "${SOURCE}" --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', - source=app_artifacts.compact, - FAP_CATEGORY=app_artifacts.app.fap_category, + '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', + source=deploy_sources, + FLIPPER_FILE_TARGETS=flipp_dist_paths, + EXTRA_ARGS=run_script_extra_ars, ) - appenv.Alias("launch_app", app_artifacts.validator) + appenv.Alias("launch_app", validators) # SDK management From ccaa3864d54b3792b8ab0ec0a2efbd82b3c2f7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 15 Mar 2023 00:02:27 +0900 Subject: [PATCH 08/34] Dolphin: new spring animation, weight adjust, drop winter animation. (#2489) * Dolphin: add new spring animation, drop winter animation, adjust weights * Readme: update application folder structure info --- applications/ReadMe.md | 15 ++++++++---- .../external/L1_Senpai_128x64/frame_0.png | Bin 0 -> 1756 bytes .../external/L1_Senpai_128x64/frame_1.png | Bin 0 -> 1841 bytes .../external/L1_Senpai_128x64/frame_10.png | Bin 0 -> 1846 bytes .../external/L1_Senpai_128x64/frame_11.png | Bin 0 -> 1824 bytes .../external/L1_Senpai_128x64/frame_12.png | Bin 0 -> 1826 bytes .../external/L1_Senpai_128x64/frame_13.png | Bin 0 -> 1862 bytes .../external/L1_Senpai_128x64/frame_14.png | Bin 0 -> 1815 bytes .../external/L1_Senpai_128x64/frame_15.png | Bin 0 -> 1855 bytes .../external/L1_Senpai_128x64/frame_16.png | Bin 0 -> 2009 bytes .../external/L1_Senpai_128x64/frame_17.png | Bin 0 -> 1918 bytes .../external/L1_Senpai_128x64/frame_18.png | Bin 0 -> 1686 bytes .../external/L1_Senpai_128x64/frame_19.png | Bin 0 -> 1593 bytes .../external/L1_Senpai_128x64/frame_2.png | Bin 0 -> 1879 bytes .../external/L1_Senpai_128x64/frame_20.png | Bin 0 -> 1281 bytes .../external/L1_Senpai_128x64/frame_21.png | Bin 0 -> 1318 bytes .../external/L1_Senpai_128x64/frame_22.png | Bin 0 -> 1102 bytes .../external/L1_Senpai_128x64/frame_23.png | Bin 0 -> 1537 bytes .../external/L1_Senpai_128x64/frame_24.png | Bin 0 -> 1414 bytes .../external/L1_Senpai_128x64/frame_25.png | Bin 0 -> 1486 bytes .../external/L1_Senpai_128x64/frame_26.png | Bin 0 -> 1364 bytes .../external/L1_Senpai_128x64/frame_27.png | Bin 0 -> 1325 bytes .../external/L1_Senpai_128x64/frame_28.png | Bin 0 -> 1278 bytes .../external/L1_Senpai_128x64/frame_29.png | Bin 0 -> 1179 bytes .../external/L1_Senpai_128x64/frame_3.png | Bin 0 -> 1861 bytes .../external/L1_Senpai_128x64/frame_30.png | Bin 0 -> 1198 bytes .../external/L1_Senpai_128x64/frame_31.png | Bin 0 -> 1204 bytes .../external/L1_Senpai_128x64/frame_32.png | Bin 0 -> 1248 bytes .../external/L1_Senpai_128x64/frame_33.png | Bin 0 -> 1669 bytes .../external/L1_Senpai_128x64/frame_34.png | Bin 0 -> 1767 bytes .../external/L1_Senpai_128x64/frame_35.png | Bin 0 -> 1832 bytes .../external/L1_Senpai_128x64/frame_4.png | Bin 0 -> 1769 bytes .../external/L1_Senpai_128x64/frame_5.png | Bin 0 -> 1869 bytes .../external/L1_Senpai_128x64/frame_6.png | Bin 0 -> 1893 bytes .../external/L1_Senpai_128x64/frame_7.png | Bin 0 -> 1835 bytes .../external/L1_Senpai_128x64/frame_8.png | Bin 0 -> 1772 bytes .../external/L1_Senpai_128x64/frame_9.png | Bin 0 -> 1827 bytes .../external/L1_Senpai_128x64/meta.txt | 23 ++++++++++++++++++ .../L1_Sleigh_ride_128x64/frame_0.png | Bin 1656 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_1.png | Bin 1754 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_10.png | Bin 1494 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_11.png | Bin 1637 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_12.png | Bin 1713 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_13.png | Bin 1585 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_14.png | Bin 1634 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_15.png | Bin 1771 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_16.png | Bin 1681 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_17.png | Bin 1503 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_18.png | Bin 1663 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_19.png | Bin 1661 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_2.png | Bin 1681 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_20.png | Bin 1559 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_21.png | Bin 1542 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_22.png | Bin 1736 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_23.png | Bin 1621 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_24.png | Bin 1628 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_25.png | Bin 1671 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_26.png | Bin 1636 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_27.png | Bin 1621 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_28.png | Bin 1099 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_29.png | Bin 812 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_3.png | Bin 1651 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_30.png | Bin 536 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_31.png | Bin 492 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_32.png | Bin 503 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_33.png | Bin 897 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_34.png | Bin 1490 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_35.png | Bin 1741 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_36.png | Bin 1538 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_4.png | Bin 1668 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_5.png | Bin 1555 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_6.png | Bin 1521 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_7.png | Bin 1642 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_8.png | Bin 1694 -> 0 bytes .../L1_Sleigh_ride_128x64/frame_9.png | Bin 1605 -> 0 bytes .../external/L1_Sleigh_ride_128x64/meta.txt | 23 ------------------ assets/dolphin/external/manifest.txt | 18 +++++++------- 77 files changed, 42 insertions(+), 37 deletions(-) create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_10.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_11.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_12.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_13.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_14.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_15.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_16.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_17.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_18.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_19.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_20.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_21.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_22.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_23.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_24.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_25.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_26.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_27.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_28.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_29.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_30.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_31.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_32.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_33.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_34.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_35.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_4.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_5.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_6.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_7.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_8.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/frame_9.png create mode 100644 assets/dolphin/external/L1_Senpai_128x64/meta.txt delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png delete mode 100644 assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt diff --git a/applications/ReadMe.md b/applications/ReadMe.md index 6224cb45a..e50d8e46a 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -36,15 +36,20 @@ Applications for main Flipper menu. - `u2f` - U2F Application -## plugins +## External -Extra apps for Plugins & App Loader menus. +External applications deployed to SD Card -- `bt_hid_app` - BT Remote controller +- `clock` - Clock application +- `dap_link` - DAP Link OnChip debugger +- `hid_app` - USB/BT Remote controller - `music_player` - Music player app (demo) -- `picopass` - Picopass tool +- `nfc_magic` - NFC MFC Magic card application +- `picopass` - Picopass reader / writer +- `signal_generator` - Signal generator app: PWM and clock generator - `snake_game` - Snake game application - +- `spi_mem_manager` - SPI Memory reader / flasher +- `weather_station` - SubGHz weather station ## services diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_0.png b/assets/dolphin/external/L1_Senpai_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..ed37723ac245322aa254e648ab48da409e7e35d8 GIT binary patch literal 1756 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_10.png b/assets/dolphin/external/L1_Senpai_128x64/frame_10.png new file mode 100644 index 0000000000000000000000000000000000000000..e385018bf6531534e1d809b371b0172361c657af GIT binary patch literal 1846 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 O0000lE>hmW@WGRyB&Rwv znb;IXLd5HUl<&6h`@U@(?fXvS|AB4Wh=^X=Zv0w!5D`@VXut7m<8ihCH_pXIMEme` zlmbu`7^#8iJxa)HR^J=vW8)lv=%>;(K0rjOk$47lbBycZN%ZQeLm8I$az&zhIkan< z;h_Gvp{&D`NcYspf^#E5@FI&G$Lg=$d!vpgYK$(pRE{(T?OxswdH=ZgR`_~A10b@w z0BZLXW{suc<`3(!jS|4}UXS%_`MW|ctMo$&Y# z$C*Z`tugtCu3k4r;2`;W4##w7c9DwlesUm+ZZtjK5rE((nv*l9phxy zjfm)>ExzX6qn$5)v{bbESf%003LvZeAx~ugLwlww(40sgTu!-eG6FsM7i|K5O@8+7 zmx9WE$XeyN=m9%WQGT>K8f})`Ug<3RWQ+>&0wn9(Rxp}ePK5eP1ojeXR8U%@HG8CQD-mLSOn4X;)s=Zg-s2sjma|hD3G$W zc|5PT#wJou1GUbUAgEnFKqTz1V*4F9DiTGNiS9$N^!v6GC_6+2^8~IuP&ZbFWNn_v zoE1VkP*(j(=wUM=#$vjBvQ@(}^_6zt78tH4<{ z&fBDHk$EicRLTEd$sUFJMi56fzi2{oXd4xLjH8HSS9Gw($wWR2R@GF2`@@jR1X0$j zJ&e4*LiyFatQmE7HxE7k8CdqqAnwIh$V1fu+Dy3;Bn!_(kSi#iBa{NF@Jh-^r0~Y6 zAZk$7PTtiNS;gtU4fOJ6*Fsy7(A8+^e*MqN8w@(ywH2r$hjBkfB{$HUEtt zI4uQON?4^JIJB2(sI#s}ywwBj_-IKZxk>hH(zT)foNRM&ND*gT?ui zh<+!c??m+5C!Xw~@^bcNmJXTDSjVgZ{IhM_H{<%p0Bu*F{T{;^Mi5Q`|Lu5v8+r3n z6+d8|Q)pOu#y@0|XCSKq4L~Yb+q>ttERbzEB1U|Kq07CHBYDA_0YqwYAl&)&%p{_V zepE%UIvR|zUko5(?Vm{cJ_X1KRzw3H(L)>noqAo}5V*`%pGq278J>~CSEhuozZB{n zmRwoc9NbW|9+c_ZsgQ`mSEYofrC96iUXnzKqYcKY*N-?~m;sVcYa^O}jg0H@jRQn1 zOnZr=;&mMeR4HL-vU__3DXVx$f zY0*#(ff=%k{z+LQ5hN;yS6yYE0TFZwb0a9L0JKrWH)OIE#?jRT0+K(fq^|intUZDV zWHsQq1SZf_6k)gvNEtGICmw7Ro z^P_Xi&nwYs#1V8nmqjKmyc`DcP~ovtsbm=zwG>c+X9P%&>iCs=BlC7f0lvnbb4Zdd z+u)yyMY%*PDI^*49=7UT+QU|frZs-Gf0S5u0qMu8%7%=*-%}2TCP5106Ux$WOk{C~ z@i_xDpVcoqhqh=(0_A7kK46jbj1I@RPj8{DMa}?4ikZcZGI)kPk07*qoM6N<$f=Lf+7ytkO literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_13.png b/assets/dolphin/external/L1_Senpai_128x64/frame_13.png new file mode 100644 index 0000000000000000000000000000000000000000..a996443fe46be0e95aa380d17883daf5dacbb55a GIT binary patch literal 1862 zcmV-M2f6r(P)YNI1HuJ0Q1=Y|C|2OJkDSS?n`ycFz0a5VmpNo zP&APxiV}AzZnu5k_ifu`-*;($H*DJ`BJ$F9B3BUrm z>nMh2EZZv`M>l?!05f#5N>(wd>~~A+O#w7h*@K+QxXNi`Mj`aPy{IZsMu1s& ztM0Gn|G_q&S$AvKi>WRknh%wit15sV;kPw6&xZD@JwxM0cx7$KqoRGY%KOw`*aZ5S z{Tz+AjM{!kTPz!(#f+p8fmz**xmXB9`@`B% zNO&?~BgR=0V;Mzm$}$}(8S)yRgi*dDk7ETKxq2qwC+Zx<8ZSZ2ga?L<=NQG2%G9d_ zNLUeVFSlZ}MaXR+>+CUt+U)`|Zhuv_KMO}FP*#{|9D*m`x068IAwHw_$UF41RTvdBYux$6vtD1Tcz8KOoK{o4Ui?PqI z=N`3mb=_GXZ_V$bEF>HwV_&crA^n}t_A$Q6XHkqQC5h)T+gr-V z3QBNV2=D~FGQL^`J6iy#Z`=0GdHzv|4ykl|!;$Mf!no2Q z;8&vZt+~uk7JS1MvhW6WgSt9FgbH8<%+BuhkOjP5601u>^ee2Diu$GinVK9hbE+B5 z+8nmgM|nF>*t5MsqxGTy*=+bq?%$p!9yRAmto8=lqo;BJbnA6BMX=C3dwqIu@n%{g zo6ib+#gX~ft6?6|t0sN+IN2)dnFlOcMEtS4l35`Ez8&1a?(R|fWdEE6_Cu2_PnEvC z>tw%>@(hqEL55#TTg&k+2godn{fOg8t3&Sl>ek+XuA>{6B6zo^-nzAXkj!8efUKPL z^X~GmB#cphtksPwx~BwsJ~k$!1k8i$wl@Xv5{;`Tej>i0vzCLBuWBAt^`z~4M5EUe zApW*(dw(y$X8A}75VI>VYF!x~qfOIpQ;&(}s_@|P6Va~DWBbFmgEI?R>E5$SHnPE1 zh={fqS2O&L@DnKkqk%r7{tXY?V8JG5V`d&wM6@dIKKsn}Lw_`{9=*{VK?8Z}O!m)s zZ*UKSsshl;qC-ffJoI8*6*Cf1#6MDk5gxL9&hWan8h9YWvywfefwPsMSIE5<#N7Xi zswXQ0El@2xYk+plOBtv;Y68)uUbVnN$B{}f5(*;zwYT#y;sv}=VB@`2Qu_=gvZ+_Y zy!d9bKAshgv;->9{WDgaq1QW}WyCWRI30F+J)zgft5DG24DLx_v>}!_+gLBJ z7|Fm#;;l_yS7z@lpUN1Q?SllW^D*RsS^l%{yZ_I{_gWWgpH(K!rZY;*6KEvt-lR}J zv&k-MXL|;(gKc>iWtSBoFEJv3(X1QA#t4XNB$Ug{K%l0oQh@Lj#3%_W%F@07*qoM6N<$f*OH~ Aa{vGU literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_14.png b/assets/dolphin/external/L1_Senpai_128x64/frame_14.png new file mode 100644 index 0000000000000000000000000000000000000000..628d58b93a3143142a7dd78ec0a0dca53023a984 GIT binary patch literal 1815 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_16.png b/assets/dolphin/external/L1_Senpai_128x64/frame_16.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec3727989dd33181d644399bfecf66a2146b9d6 GIT binary patch literal 2009 zcmV;~2PXK5P)I;*U-BV`q?}SN%P|IO01b$e z#;)lq*Loxp1j#1Q;69dZ+qPv{WZO0wUk8?D5fOQM_QtP;4-tWzKc2twYvbc?2i~|B zi->IFq@xsoqQFQEBKs&|-$iZTxF3tw03ts&-O~di;*umI&|71?9#)a(lR7pdI=y-$ zQC|)3oktwh{!Ntgu!{I6m05625`-+WSUXXBukRautmq-T;C6E?8N9x_9XkD^?|Z|~ z2PA+&`n~+!5tmWuzbh?dX>R!bGvYyjr-#MTE!%i& zi_rrNsnF*cU{jgb=V~%|Ys~oabPq5(W~bj%>QQvipV|9`?4-|51GDVSBxOcM_OQA3 zMdazNH$b!j7ARb2Ih;A!QR#SO!OzeWGe}T5RP1MKYexVPep5VlM*S+cjSM05xE-9S zAv!myjE$q~tH=NF9Q022c&-&<)2Cg#tn9XaB7fa7P!Dc9p~0gKWX{h+dICA>Q<)=*vdFG(kM?&(jk8=63B*na%qB7m zGxL4~y`g83*V>M>mBuawpod=L$Px5>*s%7Zuz!ZyzY{Y^;6)Qe0jNDW~5J6k#n9s$|f8A3qeTH%EW< zI#3Y>|MFoD8(RxpB?oW|Avp%J`rrhqi+HOXZ{kAuTmfYx*xcT1hy8h>Ws5}^==QD zJ`8rkQwkB_6W>JSPZ9Y?M1H47miWZqQQk$T-irVt*F~%=bVro{|B1*K5&6C>%L?AR z17&URx(%0 z1kc8`?~bhFjsV1D{}z!y*v+?2W@B9imed1B1z~5CvO{IJq8yBh#mnVwuWf+qV5*2sx71?+o*u zlTsysXTKW-J89$y3DEvb`%|&D-L zlnD@Pyz2A}u&2-Z5&4z*8%4U#T&s5^Q5%m0c8wlmq7)Eytc21^n+|4(u(^_GBLbD?hS*_x|N2IWo0Zaj2{>y663gas$@W^S6z?||*ZSDDs z)&<#){EXVOLKeNFz8N$OFv_wbeqNab8982w1fD%ljOx$yMUWTXY)GG3@>SA7H(hl4 zp|$YZApa|&o%P4_1MP?QrF;`;CyoZMZn< zXQcqIZ6!Rj=vFR_?mp;1Mn&8~k(e?BuLi^fWLl|i^38^SR3&)xL#YFiFIpr0d{+kQ z92E_!9j{>6LX19QuhY+4;0#^jjCrHEN5ruQ(#{!ee&p{{dy)L7f{_uXPvq2*cBs8~ r3RpV>6#_(?g-}Ux00000NkvXXu0mjfjU?0v literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_17.png b/assets/dolphin/external/L1_Senpai_128x64/frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..11b247eca27efdf52b168912f14183521e1c1b63 GIT binary patch literal 1918 zcmV-^2Z8vBP)OPdIS%Uoa+Gy=i1;U!*>LS72w7yYa-#m;*cZm}K#lB%TjN+~@W$$X==AroFN3cK zbO4c<3!wJQV38~jFaEF|t)~Fd>CITbcYlwN%P90;c`amDoyAvi%uff{H5OC1%^%6cjVdko#y1rU1bh{mDlk<}2aUWjyl zCF6^5`xx~_^_eO^(HYwV35W#h6WtYv#xNVoa0stXQC)nd#|-0VVVk`M8U1>HTf6c0 z&Fb;cAJF8;;7E983^X1os28E1uTJ}B8b1r$B79n`qnzElEbZ=nyk{cjb#Np^8z%Bx zJ;vVu_TQE0gEEMfMwYjoD4;c6iPXY^!^uG^m~fwqI4AY$;5 zA|qbE8Wjh~`c|AZ<5t<+NB2_e`o@HIG!UI!k7-&(?-1r$vf6Q%>j~xiOopJLqwr@X z(54VsIyS1fo$u~o$X0)5`)6`z?SYq4Gvi`4~R%;#d(hsMo%AR zOg1)*0ISNf$oRHQeAa3A?qq#}k4A%8W0~ksscec2aDbV{iDX@o@WJvx;M1B}he8Gd#R zL^Y1t`gTORBIy3rI>3J-^4?n89fAqXZDxO#HB9x&;eJn<@fS;|kcQpBm zYT+!|%-oeZ7+ueb0xZH8asnKnQk3~<-#%mwDYueIADzE4#uk;%asq273;S$$fJO43 z4fNV)g+K4apm}Gb@)nNKjd22R>{-w{?l^#_lxhFfTKjY60uY=2sD_%Rm?1A-h`Zw6>p)I8a<6+}%fl5tNBXUB z_}y;^VN0Enqd=muC=Z^^x~u6_<@Amim?<>b{@MKYz&`zo7?$~4VG$ubi~X*0I_4_q zVCI}*R7QrE&F!992SA1fsAWO?Y)7$D3pJyzDg)6=d}DGeA`><--A3_8;}}lsIG2E2EBg z7`zk5rqfwft+A;572&R{jDw;KL5)YB#2Eb?3j_zsWBsDGBWO(sbu@X z6nP?ErrTjD_^DG;P^jLqtlSZdjAd#bNAzH?g`y*?f;kfzedS3pZW1ey+Wgxf1S5+z_m; zS(-RfJMbbfdpO~L7-7@9i1k;2BxU$hNv5N4NC~mOOWP(Hc^IAJgKgIx+>!2c&(eX(CEYeoxMBIzbYFcaZ8&dcE$l)8?1ONa407*qoM6N<$ Ef|8}Dz5oCK literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_18.png b/assets/dolphin/external/L1_Senpai_128x64/frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..bb15041331abe5f7a1007befcd37135b1f0b6381 GIT binary patch literal 1686 zcmV;H25I?;P)V9!Ergj2gmjfkWw1@dEpt`J3!8vQc6QU zFT5h1;7MU#zD94S`e5B9WsJ}%w$)GL=$T>fL zkWu+P8KK-6cpEFqOMBU8RHTolY_u-BC8fv?L_~MlfExh)9uX*=M%!Pt1w_Bgep(+3 z(LH_#Xg1)w*Y2aWzM+Vg6fTV+qZmcB$2i)4?lGjNjc3uUG5P%YJueG*5DBc7WR%O` zI+klUPe-)B%QpPGwQol!F*`bulWX9PuoG)&L1WcP%C&oKLKfBX$VV)Ju5(#ZDKv?h zNuLLrPAD%yr<76p)EEXCSu;NW{=xv3vR1_nd6zV}rkt1dJW0_~nQ z>79V~O@0pnCD1kM?gi2dF#uwSo7PB9MBCdeCx{-?X0wOaC^ZH|w2!v39^-nq5*eVH z-H=^#&UCYAq6p9?j~0PXKEf1oQAeTmcxH`feJ|IJIxTn@fPda=dqsiV*N{*ykoQA< z_%%(=OjvW}_iJMmpf&N1(K2VRW7X^n8GtL+qEsZS-z{n6uym_Yy|xXGgk)tDd^ILe z89*w}2iGPay^g-GbchiVs%7Hk-i-NZHj>{pR(oLp-q`E)H3dD8iPJ3OTZZ( z;&Q(ik#*7nO)|obduy#T5+aq6M@Xwb$LM4YjXzuVX#J61=$dw7LH%UHeMeqZqZMs` z^CnZ=F?ON+opTWhX#=FY$0>;DH3#r2P=j|oEmt@Tj&iCRyLx3q23Q%HtBu}z?6StF zv0RT%HGO;64?YfV9aP(&tUAG2A?LyKo`~_ivSwk*ABK2nef9n?WlOZ`Vi{RFf+fSV z9wXQ6k^IkafL&o3VrJpS>i{14J$fQ%y@Ph|4xU9jOC0dy9KYu6E1^0-(9?bs(Px(y zi*e*uGYfKSX*mg$J~w_r)|H(L_nnCTxHP{|MI>>QNhcah`+2k>dV3=%a18uGMBj+$ z7ZH7NYkL58iT)M)yEYaAAO9W{_Lts_|A#g={+ThtalQQ`e;fFNh(7)Q{Zz@LhYDBe zVL`I+3OpSHTgt-!k?Cza*$%b2v@UcsLZjP2R8H&m@${!*9kjEh@jcifN8peZMz38^ z_0i+_Dd(KI5Qo5@MD(?)(MH%I8H}FWN0jG1?3UifKITEtO6)MNXi0d~Ga6GTlmkeG zB#vIlR?IB-)?`YwIzg>(6lONBMa>S;)Pfa}HR{zbYn-0SpOo=n0BKZD_IySsK$vyp z8uid=lvAPcpaVqS=6MfJ(DL>Y!t5iPR}Tq`#qa#fg;zut+nee4z7*rh)4X^*3*dvV zWPnjo)e9{F?q!A}woz}6lTvzp7O=8By811c2qJQ=60Sw}jaAo>K1ByjhP$SJlp$s< zk|n2h4C8&YYse8+I)MaYg+~s66#9rj#&3O%3$0_#3AC|#P&>-2?*!Hsf=3I-$cH3H z6>g$=5RtvlO0+sv3#@shl-eHaJ{}}=Bu)jZDqbG6JPmymR20KSn7}h0(ix56#Zw(X zcGmX9T@$xn^h96n2-wO5R(o%yR%7X>N}z14F3Q&?wZ101~@v>wTT{)Ko)scjug!ac)!vXL6cR(%j4jh z5Q3G&3AnX3T=vsTp`|#$l|i4Dqa0l@LW{q;uA#A!)kbS)$!MAD z3;Q4jK#^X{EHx!oRJc6bQtdoEL(f(w(Arvk&=%o2!8N}WYE`c+#~BUetln#@;mrw3 zTMaGjq0Rm2U&TfheL7D9`qlc4hNat)n6dX;1tiVkEyoX40xjVMM`z!bT+i0F0ud(g gj3t5cHEmn|2Mp`eeNyah;Q#;t07*qoM6N<$f`7{!ssI20 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_19.png b/assets/dolphin/external/L1_Senpai_128x64/frame_19.png new file mode 100644 index 0000000000000000000000000000000000000000..f953c8ef1950ee6ae7dd15ce03c7a56d931e8f5c GIT binary patch literal 1593 zcmV-92FCe`P)q$gGRCt{2T-%P@I1H7I01I><`v0HVhb~Z{fcBvygTwMJQgS9a zK;a~oC5k+}Nb>aPz4!LU%i`QEz#Dt2G|ZN4=Zr|=E<>E8DMC$=5?+gYU$*G=b0UK1+>G7e;7h( z3ZUBK0t4Q|xrYl$(wNeOvN4F@F;JB^S4N4mHGZ<(Y9wU60CEH%=d)(+hQzypL-4pB;AL?Qxlp2N8D&%ST;{&ToKwM^Lt1}T5*NKx5gXY zk`g4PREAusZ!XtMSeYWuoyNE3R7x4J2-U~{$I2|UEUN_XN`h-^mD@YN1$8ZsflQ&X7cv7B*Px}Ct>K#_J z!;_##;pcI`+{CC!%uY_~oAXAiOrGe$$-L9^2iU`)h|vZtk-$?6aBZ`QVCm0WFIc=Z zI#y;Od(N;>GT69k>4JhMEM_PIMllOKMJF6XD(-gSlqN&vYz&wdEV(~Yc)@Y*3`)LP znJeZD^?!2Vgev&pJ?u4FfjF*F_Io^Z1u7!Z4qOoc8`NVouF~~*epG?GNI@0F&j_*x z;MTR8vLl{9qrA*wC_4U3F;@kv)3%280jNeWzvJJyPOY9llM*o|^oK7RX_EgYv@F0Xob**FqrMJ8?>4U+d)6usv6 z0fqn{pMN*zC|MkgX~TJ=WE3h|tw6@g#j+nVLr{;5*FJ!DZLR$PpC4Y2iLe(Efy&{# zmRgZI!t%_HWi7i80Kb2H{toX)mN*&9KKT0%^**B1I{c-4SNSh^hHTnQ2>+Bq6 z!#`d{QBdCrq`t3MuHYTFG49=nFl+qi29y=lss-AQJPV)%q70G&ZX5U+Q04jApbF7J zgvT9(%R*%?)%vo0ULliZoKFz|H(Kp|-c1s&53AgfUA(V`5fOSPUI^J85e7U4jrPBr z2-^5Ke^;RV{|ThR4n{NzWGICyj8^xS-R;niB)nAs!{UkmrZ`+^4L!L#B04u-7;*4ykMQ5pe; zFy2TIUF+_8Bj_%O5&`Qtl>l&%h+!x)R3Db?#w>|FM4x)w~n|u7Q$?AcfGC+tXgGj5HQ3 rBfvFaBKs|Q^e#JYt*w7w!Giw*R39cKIu>1m00000NkvXXu0mjfTl?|s literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_2.png b/assets/dolphin/external/L1_Senpai_128x64/frame_2.png new file mode 100644 index 0000000000000000000000000000000000000000..36c3b4abe1768c8cace03ee61dea063f00ada2d5 GIT binary patch literal 1879 zcmV-d2dMaoP)_^tiM?~TXV3fwpsn~3af zr=t*nyg&;LBJW9H&NE%DZ+PZX2CT{5F*cF<#_$s*c<(LqQ>lkTjN+Vu(7%y+Wl?pZSeJg1Q79a z0n{ED%#+1%=MU?#wGzPFy;0WB;&)GS>AC(pEX>Cj0bUx5rCVfpD=U)^WJrZR&qS2p zXq-txw#NE19V_ZNKpPnCewL|w-a&uPK3|A-`id~n``ltxNQ&~R>}I3#Mdalwo4_-G z1#;I>4A1OruXG$;_*n+b$jOqdvZ=D4t*kc!Xr;0baw_90Pa89Y(DU}9N}!AZv-(yl zE!I|DU#K%C;Yv9P$Gy6VK=P1^A31TKZFl0Q(D2^mk zFA0#bBH3PU#bk?+(?HhQQv|i!8_2l*RoVV596_M0G0`{#&%SR@0&Rz^Vv)i)~i%AH2_8ePkm5WwO@G|-~rDLyf#SFtLTS#tGwNj?7SABEZyA}YtK$mS7O!A$*T z$zq9IZ|=13J1zr9jql|I))RQLGuJCR&GI^)Ec84CwO0WY2>Biy)0^d|^szum=@d#!-pmEbrjSsdzpQUe(lt@QWcm6J)bq zb~E-m+J&tCT@6O+Y{{(OUm+YU`(}{d#h%MU)q(XQvkg2d&t~Bj4{`;gYotOzFQSq% z<0)pyX)>y=-Wzi@L-ykI?*>M3lP!LA=b`cJ^J*MDpNGQpM;PY}5`>R}!Q%WaB7cg=cMe1C@~yedkz?VrV{XG0vhW6Wg1UOW!rupIuYtjL`Qc4m zu=DIs!Vx8^%~4j)`$m9FO%B@16lG=DnS@~Xdz7c}jLitrize%Z0ISZ`wS4sSZwmSaCx7^($d*0`KH&)}$-lKO)za zy-M`xTVF@-Tc2LM{6a{kEMg0c6h9KtVkC2!u-&5PKG8Zy@;vVw5!|!cO9;ri7Tr%X zQvV%UUTGaZ0d{??VZ4%%jc4nPJce$2%K>!l86DVUT-R!3-TSCMdhurv-6MgXkB!NY zz^)Cl*&6}8MB{3)w5m^b=b+@P>S3y$w0(|b^m+oM-?nY<-wUvD29W?My8_Y8%+MiP zH0?IgywS6Rs*a3&Om=l1+aG>AICCQ_-Fuc~BMXe`Yb)D}s~P=9^odBoWUxxX_HTID z28*aPMEyq)DQ|V?nXi)UqCc&xCvP-I&_JF#l8q8{3->`#)c~ke(GO&*aq;MC0s-kC zmB1P^i9BO1Y&Gyef@dXrh=H?7u+j^7`m?w^6ImN*gKFE^4QR)_l!3aV77$J9RSPV1 zdo%9vFl%geVa*HFTlIt*g2 zMDKDN7qc-xJGXtm5;IBMurjF?=|5NbssgO5@C2g7$ht)q0(y`o!I54-j}^(C)Qu#2 zC=_JY#U|;t4gORtiY2V1(Bp0L!EUaH7*`{~hz_G9Kw;MsqO~J}>V8k%nJIz{rYE$e zUz;e3lT{+17&g+cfi2o7yg1BIx$Y&1Sv{VSF+7%6fIP*>VrPsTxzcJj-=kl&fjoG9 zyb1-q(m8&N^NnB0G;JG-RgsJc%F`kl{G1*w%<88c9(3=GI5TJ*I$TN5$#9sIAH?tN^?*F8!@bg3) R;+6ma002ovPDHLkV1jF*qVE6z literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_20.png b/assets/dolphin/external/L1_Senpai_128x64/frame_20.png new file mode 100644 index 0000000000000000000000000000000000000000..d683b9f625f773ebfba1ae7b290f962e80c5a0f6 GIT binary patch literal 1281 zcmV+c1^)VpP)5Y=Xt`7%i<+kfE%&632<@T1h_bE0$dz70WOZ$$j*=BKtDgv6a6>u zgr$~&+IM+pm*nb*N&|b!?;(0ZIf`w^7A^Y-Axpq6%0G_dNmh#Nt-M$}S>R59U6fz? zu9u@Ci-ZK!_q^e@1V{#-&2>s#dxltMgrx+~Fuo_&aU2h;Y=cFQiEaCplAs`(499t% zr*-72b#)`=erb_;!u7p;eEOi3*UI2Hn+Mu$R@X(f<4pjn`q zA@IYXU6!yl-<#xVw}ab2D~sY=MYUHgtjAeW^CELAXE*_7Yya#SPhhUT{=3A2TQ6XZ zS}rV(TSfq@!B|_E1=>l>LQn}pJpi@dKYG>+*c#ot=fY!by*|R1fDxjVUO*{BXW2*L zA{_x{mKGro2hvDBitI}WV4<+c4IhB<##ZF!jWx^(V3GTe-dR1_J;jgC9as@Gg6L+a zf?c;T7t{rIwqgxTJb-1&H&d)+^JUg&Cgpp3ffoSzWJXP%1qre#UJC6PUIFO)+8HN| zx<}wCd$#iR2#*-R!m6y@GY1*TxdWbpKx%E8%5E%j-@!7jf?*cGP}DB~_^OuX1`8JF z5G=BQ?*QkaA;1~_KHN@&qv@M{9J7S5gy<- z{89b~bd(vqq&=IqhD$^jhv` z6I=iBK-=d?##&a6yy$lWSGl{DnLr&4bFMvEX2q+KoU7)LQ?6%w01vE=1FV$ae%^}- zyqAjFx-n3_M2#l`aFvwdxK@3&P=1X}!CwJJ0;$&t{h2rodIQGtr z)h?$^uO&kCQ?VX(08U%Is-A>Il6Sf7XR&?&VIc_MSwYsyuOV%hv(~Ou3U@r%#?>mQ r{?DM5f?~?cWI0rDl#eZCt7C>wS1=;$EP)zbW*x_N?8Gyj zPpVexP7){n{F`aZaU2KS_^^1{Ex-*Qlu}INZUUSfrIfDnC8YT5Ezpg4SgoDHO@I@@ zQ}Gq)2vACaJ1_kYsnV@ymW}{7Vl2Ifn*d3$Qt22s0XD%?;WHsKRT)_0|1N7-0j_Mb zpr?FkF7=^3z^a5ZiqIaipOyfbtA!OF%xmvy2!It@!@#V2Ta7a*z9J0)B&Y}MaR^={ zidMQsM$O(*5P%XGdjU_ydmu{&?}6!bS<<7 zvy=e%pfy6)XRYHY86uRP)yg@95}-Zg*#pAkz2~;dkquheLJ80cGi$+~73W#nTc`44*V@mg2qTdRZY@#RZtm2}o zhaHxx^_W{5L0aZg^}dJly~potNVSy^XG|KndVtvj!kxdJ%BSQ3$d&z5GFjxD_5dEW zxhK5iy>&iopFz4-n?qUGGZP@2Rd~yT4^~0~ue?AzPpf=w)uARpq~fja-_z=Q`{NQ- z>Uk@)d56XF@tk4;WV3jHIIG9N7KYc8uB}xNwSK#{c3#e_3<;1?@c?^c{pe+tSve~3 z8Y9JXeHG5DbgO_}ikCEo0DR@L#P|w}MS4RTu3D5*XaekfAFuzX9OO%me`OO(jZs z(mM!`#*U=Mm#jmDBda%faZvsb0QfUA0dBU`p9oNM*R^q}w@dk0MoTY*DpQB!40i#q z0Pr0EegVK&<=Dr?H|8ACu6!nGlqSKJlJIF>B>~<5;1j%C{3Y7RvqV@`22x$p(VCL; z)%Ld5u+p8r6J(akg1X$fow8TA&`?khcZ)mH49#q%C66}igoPwguyEEi9Jv=17 zvmPMQ>a~ZTDJzcxzL}&*s%bzShvJPOP8e@UUxRAD_i~SCvwBQ~-qkhryHbknzl7n1 zydh~6)$d2K+1gX@&6gdc7O+bd5bg5WrbXwXrgwSc6)LhRUvo~Q3PR-pRw=)|p{u{c zpB1eqz#c@J9CDr_oAM>Vdmrsf>;a-nJSw?q*X3mBZJ2BI_B;pm-PzGQACU*xrTi9> zya3PHuF-pz`u=L@?P*AW)yNG?;Zkc$)m(B24N5~GWv csIj~87qbzVAj79-wg3PC07*qoM6N<$f|ZkdMF0Q* literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_22.png b/assets/dolphin/external/L1_Senpai_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..dd241d24ae578441ddb0870387494b574909677c GIT binary patch literal 1102 zcmV-U1hM;xP)-TmB;NUUqxyH1B)8AVfAY!<; z+;zDqyd(WQ{~k9oP^t!yJZO~YA-;x(_>$Lh4yC%g0Ig1I zitZ8qBZyxUcIN)Udb8mrG(s|D-m| zEL+yIuGBu-hxGLd@vrL=MS!0G@ErhtA>z0EA=e9kY@gLnDjmfiXuE^wxURCBK8tc5&r@Jq_1h=T|f~@cmRGe zsfO^c_j3#|nw&%#U~AE}6Go2s9N}AS96krAokT?uJF#M>YbS3OvwrxjVI%@X4WHi8 zN9(Yu$LOp#xeoSBm?Gv9H8^IJgm0_u@X&SeDxxnhl)NxS`;rErFTTe36b;}}1q2XV za7x~8ZWb?n4n=FMJ{e%etdE*c^r*GFB>b!yS|@ngWA;h{SKN66>?L{)?Xs*>DkL{0w58fltrhnwVxG* zUfaFQC@u+;mQT^rt-OqDWGU!YxENR2NRjLmH0X_o$N*G0DLc<*3oNgzTP9%V05ihT z_j0b5N1iD;oVhzR%GQ`J2;snaf35iB&Uu0dZXXvxW909Zh$k(O( zO0^WikN9>h|9pUaT`EO@{AWjeJBAFPJp+4*+7W&oZl4h1b@_4ljQeK<9kG4+00gC3 UrPkqm<^TWy07*qoM6N<$f~Qyv(f|Me literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_23.png b/assets/dolphin/external/L1_Senpai_128x64/frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..944bdc74e99bc530de3bbf37368532a2dedaa377 GIT binary patch literal 1537 zcmV+c2LAbpP)Mu1*` zAAPD^?QR1S=z%19rX0s{;KbYFvYUYudvPkjo8we~H^->}Z;n#|-W;a_yg4oaP)b4X zS}|%j>YoKKAG7#bjp+R8wE#Gd<3K3|Gp;ii;hjquS@O9=g}PJtBTv=3e+6V2so4Wm zjP6gY!Ez5UVxr#D-)2lY((ctf!g3GLd)8wxbF%zd#fZ+Ud8#^hX-&3!fSHy?X&lFK z`^?_|ODT7s-H?>ox451M=y{On z0<_v^fqEH^=)ANT(YaYPQ3j0?T&&Umh)L<%Vp4obIFg@)dWbdJLsWR5Z)j*)yt40sH{)FMz+S1hsTV+ODyN-nqG#uMxD~&r+|~ zjAz9y3j=#Ce1M+-{=k>To8t#c92g&AM#r!sQvo#mTNHSP@B#cj3s5lei7(*;0pSDV z7fiHcWVK%%OY9bYz21(4dmzMr^qK)MOSARDNmk%k$86D-czJ}ta)$6*#ul<tzP53*|0%XKi2m(u`)?ih4*5U&nE{6yTpy%7?bGHP5XWO`b2%$ZDu!^cEz;3wY}r zvm$*CO2=FW0Pqnl;Ar|=f~eVxm<36>B7v}m-iX$<)p*oVi=H)F$Cm`H5k9y6Q9w8& z7U;dkwZigSG_KlQITPMDj(YuepZE9Z^nTMAh?kev_!M_rz%KXcp*vIfRxi*K*hrY! z9>6+h_i^w}XeKGOu`IS{D`=fpc$CR&6xM#%XA-_w@Lp(yH#)4+rhQR(gs&N+H!6=$NhN7Ei;+v%)LptnDrm08)N{^}+}6`+o#k1_Yi@@VwoO?amsHw1Ubk zCD4qcP6HoR@SdPED*Whn$-i^HRtX<4YyrSy34aGq8FdC)SCF1(1>~ku@My5g;7+Va zlSZ^7!l%OJ;6k4v{9zW*8Z63C1gmQbJszUMr&kIt{OA^AJ`3m#wi=pB+e%~@fY$Qs z)vE|z#Tou>;HnB5w|2T1JD@QD%|VgocPTT-I3tkgaD@IpKn7!(d;1KpmF145akvNI z?N)5T-xOfQzS|OflwM|aPt&GHuO)D446X!L!E3V<$qRh@+dwTr%CyDuW(YnLvkicI n1xX2{LXp+GQ5kXiRA#>bsTpnn;ED`h00000NkvXXu0mjfG)dy~ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_24.png b/assets/dolphin/external/L1_Senpai_128x64/frame_24.png new file mode 100644 index 0000000000000000000000000000000000000000..3f445593af79067b2b2ab416f92953a1ed9c4cd5 GIT binary patch literal 1414 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_29.png b/assets/dolphin/external/L1_Senpai_128x64/frame_29.png new file mode 100644 index 0000000000000000000000000000000000000000..e2767bd65540a0a0a456f6e0f8938ffd9ea77722 GIT binary patch literal 1179 zcmV;M1Z4Y(P)p2ltj}QY~!t} zc&DU`#o&kW!sJp)NgKDt!&`t2Uu**09Gd_)$0oqdu?cWG&ABZ=2iB)>CWGdjn*abTPvJ}k%{k4ZfXL>* zMwm!qX45=Om>nIN#vY& zCeRDDGBFCoD*tf)FxVELf~NV$KH8WmzrD*L5>aql0`xXpBhdssCWSCkKJ@zvd2e|I z)TXj^xFkc&(PPrqi|-fyQ;kA;2$8Ba4K}8Nj^?MwS5T!vTi?Kd=oYNN{H5uL4R_QwlyasHcKgNMNn} z=ni4qPC{OjS^!7TJD>a>CZ3)_p+D5-0iHe@CoLrIKx@a?rRA z;iwIk6pzC(8JQ(3C+h0N>h0gCCV&ZLsr}pE9?*wgr>iT-8kqfD4P|E}K!<@_`D>_^ tMmVB0n#qPMmOKNem@1cg7^AN<&OUCqFQYq1g0F)SU0 z0OSQmXdwFZ67reV_rdkpcn(1HpU|~@fQVEj@f7IEG46+*=-pC>Ff8lkf<$9Ev}c;) zp#G1dtiw*ETWVy(xso7wo<-8J`fFn!)Ul(+=!Q$-NM+E*@_xwr$Jj^U>j4#j$j${& zyC*P)NJ zuDuu@v23e!oZa{?0eW;Y3YIad=zB@)LjgEr(Sw}KxXfu|L?QURRjddj72$Qz$?O{u z(M#KW&ALZB-~4E)X!Ws5!<7|4R``RT$oYr1rYg{!NFJO|xo^?~?fi{40e>c6d*dae zvLDh`Nf#}!0~O&%tE16o@$Hq)vQI{-P+ov|{WuCnlgo)ve<@)3J`+}~%|Z%U!L0D* z+*)GX0nfN3j~1_z&TeT)Bn#IB>@keAI#L;+*IBHH`B95Q&m#P2%7*qwYe&YmYr;m1 zN3>jXQxT7VUPm`*B`TIY%XhY#@Dxvz?~XcqvBpXeEh~;_d6(PNuv(ccWsd?0OPj~D zdW$xZavP|1wirR}asd&yzbf1Bz*(LsDoivE!IJOCNucZyWz1u^vY>9PbjjL0kvS^~ z=|E*YZy_L(fun&E8BcL_OrJrN_f?@xDp$oz^6_6E3gyD#|FywcMDvKd0C~SIS~GM| zB3I3wj^n^(;H>bioIrX4i*{$&==3@kEoqT*t#|W$@Gbx$BYczqdII+5oE6|~HqP55 zZ;^Sd+^G`(y^=i&^%FrH+4!O{$)O!oa2jVNj$PitqLcA_7ObkN0{4p{l?kF*ueKQZ ze1-6}eia{}ovF%^+^Ymdiuc0Xj^%6J!>i@gR2)I!7o3RNMB9qK?yn(;5Q=rtAS1kvm17|jQ(mRaF2q)=KM}X zzY@_OMD)w*c&%MYcU;ayj%3d|!@uf=G^+rAZ`=0Gc-{)_EWYd5mPr4+ob1Q@uEG^c z5LSSU)pVlsT2`ZNd6kv(^EtXMcd~GQA7K1<7&_>?GPPrMIT*Q-Tpgpi0(u^)8y`tv zq$UR&a(z8BiKw8@stC}HXeS2kHwB1j-2eIGTz3ajf)yT!dz(-hh;F^^t_YlGt50MN z0T0*)B=D7C=eLDpb5%{W*uv>z)?(StZAaQy*V`R@~+%L=k z@u#&B&3}xH%kd)zh-jGhA&!dGbwK-sQ3V?BkrJ^Rl9$S@Df*C&nFDZF<6YQzcloO~ zDD!EYS$^dGLAN)t6b}hLdWYe8l@A55Cb*LutyqIrYI*%SJq)KAZJ$vBYyNH9KED?r z`QSkyHy{C2R%DAC&Z7f~`TOZGO!O0Fb* z&q`Msfu{skn|y}a2VSxv!iY6yRxwc0Vxi&^GyTw?1 zPRL*L1DQ^NY9(oR@`?D53aM*;4ozt)dC<1~dlbAX*+Uw*%Br)dT2NN$D*Q*oEjO^y z4<)(C_{Q4az8x$RkVp)+s)bBkvie1{^(?&c9{=3it>Qt06f{y&slX_a4ShAt+I*QM zcb1A3PmrCzYM~i7yvhq$YmFw6q01o6m2fMUaWNY6qjSsG8gHGU>nwDHIV+j8@OBx* zU4@sON+C70+RU?(}N zt4RQZ4}RvRoO6a7m&Lc;0_;Lc2~tYa-_DLp5+J1nIcLZ@zZBm3?FzUg0ZJSSkLn4f zWK|=;>RY3^US#DO4BLb_(;|$)<8>6RTr>=1X>$!$r?rZnz(cf&HmR32q5?6{c>qkVd|28q)R0SW1dxJ2 z?ZdP7mGxR4Oma<{UeGvuM+;2?j0DgUZlz*8DZiEha}g{%mmgWcNJz{E$O0^~1Xj?< zI4~E%;_Lyc7@#-dNH}Rg5=q%s;94k+U=SSx+=`-uQk=p(=N7w05S4;&v{6?LEk0i> z=ajgDn*fTyl&rN-$4y)kyu|<u3I_^H5_QU-riY?>Hr0PGv%7X3kj%7D1Gn& zN$Z?Dt`$p^(69hM0N{_pJYDQ2f%kxvinl&O{XM|(!XW?u0)Qs~`~raQhI01SU%4bZ z1Jbkr613uwQXWn-=n4OB{D<-}w^VWncPgrf<1B}TpbB3(=jZ=Jc_R*`3nu-fY!wNl zYt*^BN93I0QI{nLn|{_m6k{>~pmy0Sb#hW1D-xXf=GS;D1aT@&MNk7_XyCN#A^iJdY@`dG-blBZE>D0x6@l^mWM* zxV9Bsw}UaTBhkB4GQ8KO!7^!FT7yK{87VNqm%m4rY M07*qoM6N<$f`pe5KL7v# literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_31.png b/assets/dolphin/external/L1_Senpai_128x64/frame_31.png new file mode 100644 index 0000000000000000000000000000000000000000..037bdc8ed6eba76a6f3431eac40d0af8ee688ee7 GIT binary patch literal 1204 zcmV;l1WWsgP)PeYpey z0OpY(-%TF{d4KMfiYZRgjHD|SwL|jd9V;rdglg3)s*aK*(zNC(M(MHdJKW2%B&X97 z84Y=I9i9O?L+}&6mjHGR*~m#!0(z0aEBK?AF92F!p7sDeL%lk}C7_2bXBhcU)y39n z;H;BCNgEb6^>kSEdlboPhj;*$C75#@9C-(hZB8k$UiPSMd-$_<5@6K6q;;e^qwN+n8};x6 zh;R~@+Yl`q(%KWA05z<9Ge_EP4Q0MBv295L%)*gIe!@k71z|Z8Ek}T8Yw!qo?m?C# zz?spjL`rZGU?F&%&K{x^92vFIDYH2CH4>ub0Lw#?Mglb2p?3wtd*O7N}VSzOXwZ40*qRNkE=JZaV<6h7RM&Q;@AXO9Gd`(xV|J-;Fx!i&PcSKekr4+8sYrGWU32?VB9-zJF)>qp*UW4!i7!7P9mBJA~Dr65Upqb-N z%GbPCC<0i+pdLu(xRdfTq_R0YDuTF$q|^5Znaik2_hZex?a`7NeJM1`{X&N=e}?l z)w{1VNT6MZ>fby9M!ih01@PY2UF!`rpvcWe0V~GiXNQ+o^xhg~R0(kAmk=!;k4^$F zrx~C^fGf;kw0JyXy4e%?{ay$VC8v!Mm6RjLQ}C2SN-0(*5P%liogFhz5{!_;moPAr zpnK!-5qr6Ej@Ec*O3M*z8p|8q?Z zjY4}%wJe}4L2G`mFmQV1YhZQedO=x&);=r*_+=Qg46e?oViL6Abp%WgiBNl#?@b)( zd=G&6t6w}Zs$j)i2h#Z-`^(p3RKdy>B>0jF-d2DHB^Pb>PAc0`Wn{JI`!dJ~ZGneI zv0fy=cMnwCCV&uGkfp-Cowkv{cMkdAXB2=tZ+wDM%&77%uloURJdS3bDcv%Iw87&n z8THmh){c^w+rZXM)vDkwRx(CgfmVO*I{4Z40UF5llKBT9%&-Nuq}UfAGl7~HofS11 z;0qV35ALUFtF^c0D?#b9eE#LW02n85)uW*G0Z5B5Bu5Bdr`D#q=daxaQqT9jz5(7* z0C#mu)e&-Yac$HbtPikac2Il#aWcYfHHC46lIno7Djc4dLFErSJgbo4$|KLRZmSXq z0A2`)pOKeARQhms5@_E~^I?oA_$cSiA1r5aN}`ng-tc79=4W>bC1%@H#aMlv1L7 z06baGOGHIWI2vd@bxD6IIm<8+E1h!gF{=MZ5XGe03*8O)9ZP7^yi&z;=DOy&b4ed6 zyH^`m>zmsirTJ)`E#;3|;10m=X!U12-dmeOF2~{ZYtDKrJp2G33hP9U{CF1t0000< KMNUMnLSTX?97I_F literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_33.png b/assets/dolphin/external/L1_Senpai_128x64/frame_33.png new file mode 100644 index 0000000000000000000000000000000000000000..e3e7799db4dd44dccb311dab62120e0203c85f71 GIT binary patch literal 1669 zcmV;027394P)j%|4n`=#|$QL4&9nzu^v|~u0;=M zbhRv#G)ZxhTwTxeJj*v~t%dx#@r{vsiPCEGrTa8`o@+;~H6_?5p&4K`EozgD3?6$P zBOh+V4@>80LC&z%u~+_DG<|Ev)NC@^2@Wm4Z2ZOu#dAF)njTxw`!aG-9!KcTjWi^~ zQUgX7V%7;bkL>^pU>PWjTQ+$98~8SY234 zPGHHg9bf`R89!3kQG-Y1xb01#u^7GbxqUco$}iJR{E7hE5p;O;gH|D$LszVYwrON- z9<{mcx0n7-0RecbY|T)fITk-YCeiUa!LfG$&Wrb zf%HSiN|F0`&vi@ZD9?NCrSY$|ex-nYB4|U}$EV>OMTQZO=pz{=zEaexrIb6@*b(3n z)+$DGDIzP}D-VwdoDUl3k*wDGRRi?Abnx!T>VxVu%h%IQXgsKGdCF>k-?+dVeLy>M zQQb-_9PyDBuXXs;aN`VjG58zOs+o<5G5LK6lH9e{ubsecY#mpmM$INsS)`Xx&hxzA z2k3drDmmIG(}`vliP`opviCm| z{4Jf81@(gi@PbJ@5~V{PW}}NlRODshRr)N$aQ?Fk>PGwSFvuIudlWwEQ0eL-a|ZZ= zn{@z3>ABj#<}KFC5)ouQ+`H!Xj2Fe5xkK`q7-c?Ru z$xowQ7Fh@3hDJ`x9T*j|uCIn^tqHG^N4AR3-~`@#u80DfuDwO1>o7eoMG} zAp{AdffMHpxgeZZbfxlpr28!eQ(9=e2bpJz&~2Y7&@=HAj_y0p6W0Kims)~KcpLj4YO~m9oR*GG P00000NkvXXu0mjfIqw{B literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_34.png b/assets/dolphin/external/L1_Senpai_128x64/frame_34.png new file mode 100644 index 0000000000000000000000000000000000000000..a28aac4e0f1e76750594be887fe23ec7778094db GIT binary patch literal 1767 zcmVzK!>l%Rx%kk#J$3<$ z9d}D2B~oNj@=PwJl=Q*d;&xhr56l=ATGJ2*nlIlXfwtfqjvFq)CS!@}*JgSDO-R_mDS zK@ch?g<>R-ioL2MoYXridxwshodhz2)6T(mS3=b}YJezO=kFP}S5HjeUjO{n=5e=2 zc0Yc81qX=e+3VCT2J`f>(l54`V_1<|!(Ho$LM=y#2s(|KJC31c=UMB9pDlwk9a`*= zbN-3|Y@mp85f8lrBF698?J4Q=Wn(hukk7O}-ZP8>md#NAI}0^d?!)lJNFh%06r#Iv zBy4Lk(4aGRu4T;n&Wc2g=;u;O@k%~4tTt(@lGu&j1@+pe#OGz@bFEKQ50>X!oCYEL z+~iE>xL2jbESRd}qy_jcw_u*^=pgj~E_*mIAd<7l{}%(ONdG(1nrX zs7}$b<@g#ow@5pVbJvW`6c6VW!g8ZKvjR?*juVKo;s{Rd`WzSqjf8JL;7t+8-N z;#{v5!RTE8LyPel0rYr*C-Vep)XqLow}>2zJB@IlQOCObP1^H|rlf>N3i0G?^&!%% zvQ!(#ihM1=_M>{c-wNM{mEWbpl!!lvfb(UMvIe1&-}`&zhm`iGyj>R=TYsB%Vt zEW9OxT*2rPp%l;yudIti3U8biU0OrE)#qx4?8Wh44Ds^jiiKteTC3{zi2Re-?f`OE z$^YfMT{vlkt2n`t2!NrLae^H?KZ;d`x`GoNO97UFSLRoAu+s)$CGSqma)7@ljk67S zOL-C_$GM}J;Q)W6l>V78&NlSa<2#IV!U^1?V6r%Wrj&k9DgBjF`t8)c{3sX^Z8^eS zt=lentq$;S&iRM&{-xj@Qpx8PyKf{{%L}J~pG4(H4VkZO_=+pA@Cxk)bu~d$|2_cQ z8HUkc-y4EhT@vE>s|n(h14L?aqRch6(IB*i-WBaEW6zETb*u*mh_d1D5dKH7I&G|u z1~Q_DH~_o#y1FAsovk*#w|Fymq9zZGz2eCH*RO_IgT4~!la6}wfF%k~Kkcq$R!VsI z3n7uRh}Ebk^Cs$`$<^kfm3>}0SkW_2w$9#oR`m5EnPsyl4iNDu?Mob|c3q%KxfIxS zbcMF7ty@!Tz1f#+Ob#GJi+5q;-R19af@mHe)kiM=3`e(|K+4nlM4SNEhS}`N0W6|% zHR9K*Pju%n^3@t)S|e%w?2XYI37CF4=jY!Gz&yj8fGN8I-ehLz;4PXAn@p}23B3pz z)yEsVRSveNza1Qz$ja~@b+VBKMkBJZt-Ko1Z$ux@3A8cT&#ZmL4Idyh;TBsRfu}s` z&?B#f?6QASS8Kdcjv&DzwKp~+=m_&5sMP?hRFMZVt$A^GHNn92_ex-inT|YTEwCC; z|62Hr@WwoFwiB$30+#+-UY?1l4WvP(?QjEPpNBF~Hk1Q+Lak~+OC7J`1fKpf+KtGu z+E}Ro^F2B#zGFl-^s8Z3enXZ#vsAo-6SVYMm9i|p)$)!J&d_C$7E0tU$GjNL`BB;U z^GeKg;)<1|HYH9HBY3zBlA*#HkP#yci&_fkLDUI$MgiG3rPxi~2-#z)AhIq<((&0% z%X64Zw30&hNAjUfuE#O1<^&@;j5+~JyXX-0Idf3i?k*c^iZFvofmr%gL0+D0bp)2f zM%q@a(y^X7oB3NuI|pTY%$@I>iX-)555Sma>$waFi2a{qp&Y=uqF~ zlpep+-^vBNKG9s=6K$+i&`Sb)9H7SuS!A$M2)?)G8(E7w?tf9NempQLiU0rr002ov JPDHLkV1lcdZI%E4 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_35.png b/assets/dolphin/external/L1_Senpai_128x64/frame_35.png new file mode 100644 index 0000000000000000000000000000000000000000..04f8c1a7f30fbdc51cb1c9d7ba2d0a6b5e7f87e5 GIT binary patch literal 1832 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 LNkvXXu0mjfzS&Re literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_5.png b/assets/dolphin/external/L1_Senpai_128x64/frame_5.png new file mode 100644 index 0000000000000000000000000000000000000000..7a111afd03b4a20d23675dadfdf070cf0be65452 GIT binary patch literal 1869 zcmV-T2eSByP)q4TA|+B}Q9YjHJkRqu4mr_tw`xSrca#v!!aQB}Jed#Ag#>&<6N#i&nI8dg>Sy~c0)F!vwcnnrKN&2=XpjR!mx19z?s6xtC`9Gz3xRt&Sy^?`i#-Xb2hX;UOPH$ zPtVv$amLHNFvUa!^qSqEN{pQ3S-pF!iOlgl`#w?UEZ0N?F*?PJm#+$&9;%%wa&{KT zSe~5h)JJoRI8Ot;&XFRh-7bI$`&Ddz1$IRu)|hAP*KERT2>py;=1 zu9c+^4xD#`~;>n*S%I;qTY4qk7Pe~2$qC(HHi#V=2ZP7eEkn+B9USa&vyzDu3?` zu=Q~^&j?rpM9#P4HgSe9vU_*?cLTG$xoV@MlQ&vMRbrHP={9Cv+Z_Ofd`tdz;Vz?B ztP`arMpkJG@L)rn#|f(Ce>ZppHQLMDQb1PzRLMl_{SH(*VGI9E3SH#@e=nM63wp0n zD-CsbBwFDBe~8FG3+CB^-HblN-A<5R1%u7`Q$&6jk-tRbw|A@288G(~QQs<^?%2m( z2l)3mjvvA2zYNfIb@c-&MyK8TNvD9HCB_eyT)aD?=M)|)pYb=HP$^dl=m6Hh-rc<( zQlQ%fZF5#_663@>9Npf9?BoTP6yQCYL~T1m*35M$J1>(4X63QEXt|XS!(Ji)Bj_%& zGIQPetH7?MYJEDQQ#XO$v-{nS;BCezd1KdLdbINr;olo@7?x%XIO4HFa zQvsBEPZ;G<#r^Jg3Jk`Hv1WBZ+O@SMv-XDtYaY3;$bud_K;olV|8UB z2&+d{UG=O15p;|2VNkCD(6wqiNzsY&=Gu5KMQ3tVx zMowxS7!g_Ot6@=TVp;OaQt=&}fE{y36o~ekC6Ux0gm|GupK_ZQ*__YHwy!I((uoHdJKn)Ee1_MK1-6Aaeqw$LRbcV;Q*>QGhudmZaM@_^)D7F7ZkVONJt4 z+FT=!;VpKQXh!po_GgLZDj@qrRaw(f_j}sOkPu`rEzp*JO;E%g+Up)@d2G&QWO$pl z6DU9H_J)Y0_t>z_`^+{f+TF zEw=Q_7J?K;ij1H)3fOV8r!^v6sN7;<4eIsrQks389pL`}5S7jm`a^e200000NkvXX Hu0mjfzsZOo literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_6.png b/assets/dolphin/external/L1_Senpai_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..318c7eca0a6779c44ac6a7e40e50333a19f5dbb7 GIT binary patch literal 1893 zcmV-r2b%baP)Li{6-5le3coMyz)L{(k^m0R@u^ien z&2dow+fmlxNu;0D$cA$#LGU7rlwLc-UeR}=m11E z7eMWv!7N!CZvL21H3gROE=H(QdWcyM4SqJ?u5s0 zIL>rJZIAh9bu6)afHBZJ{TfrZq67aNyp+s24P@Oi7qI#9%bQGGL& zme!WtU#|Z{+kEYGk9NM8>;k;`Pv{&O98aJb>)`lV~+BfOEPyRuh zfIpKzd*h{|vLEtRDHkoU12ZRoz)C~fUZFmb=bBS#<2;>-#Oq*hapSW^;E)H(1;`VG zEmb+!~i?Q#K;u)m7!ci{*JiW(D*L$K`oaT6#zL>2QCt}Lh< zv&M@$q{p3R^_tzww-lh|32&f8CQ^K3PS0XhDwEDtiIRN$>z6`h3E`DvSwwlnRnVzl zmn@CQRk_o?@3;({HNM3OWF)X;cczWbsAI`O_aUhLDuC|-5HiL`1kf|EH)luwo?+v> zP3jhz$Kp;i7gDp1rT#_`M>fA`N^)oy63sotZUgfgZL@7LLRCP(9K~dX5~p1o{1n=Fgiyl1ytdcl#xi`$;s%F z4fWQTs~NJ2)4v<&<;@ivjV`o~EK84`&%)sjz)zL-qUb1`X@tsJB{A;6DG>m@$k3jx zm^X`6j*`(w@}n-DPD=rnfmh~N>0oDv;)+3MZvO_xoLvs^caxmMAm37+iHJcv(<6^2 z9S-mZ5&hF3=P*?2@gtn%NGBL@uY$qm{7FQ=6VYEp^xL<-*+WEh_4|m7Zq|GqX*E{WA8p_Jdj_$_=mK%^!I%A9(PsLxOcQk-yV3ZACDg4XZ#LSrL zI##ER)zKhb2BNoKS91gjuCr{ftRWBu%X(__Nn@|LGJpLmVPgX8&DT{3(}28A*4Vn$ z?`(Ki-~2iHdGx8`?OzCqltpOLv+`!NjMJW3WuI3L4pi2O^!DUgk;{u@md!pmK;(4r ze%vGZpVg-fE&6JwTZ0#^EE1@V*Vc_9hRFdq0r6hgL_*h9(DCwQ$mwu&%L({=+L(wF zNc$k0eK>$cG_Fqkh-^bgPYxnq*{UHMNyleTMl}*3{kCoU{9b^}Gsp>$vMVrqx*|Gy zo5sT?ALGrH(ZSNklbxMM_J`jNjx1zlc#k^S%mzy%BH316jp#R{kLLuM46Ra-{RbYh zLBcByQU4iu%1a%h;Xe9I_CtTTu9m!6j=+H;wI>@9bP4k!C~E*zs*IClohVJ_EOF}~ zqDX(OgsbWh)w3h&%4*<&1b2}=7?=*BC?^chFSSdvgFQE@hXuZI%Y)_7;+J; zy^dBUp_f5gDB-7E=EZ2vkIpTBj-aYTrxOo!asdQH5+it30Wwrr0ueEiVNpu~6-1rD z3xBg!fi`x`m(kBcG!;blMUwQ7w%xE2nk4#MJ4G|63so6fKcsJD914@3KH2qay7A|h z{u+=&AP438y)J?@!JZ*CqeSv#))At{iJU@dEV-@@Sb11O=D9@>qk23u9w`N6!23r| zaYSBqyvB$-2^>#5)ktXdu__eww+8bj(Ay9xT-#WSSIjW*nS3kB>q_>n@u|q+l6{at zIUhq2SmQtYzT5v?eD!o`?OkP3x13p7mO(vZS4km%Cdn>p*Y*s)4wmJe$1XEKQDVjb zv#cBCMh}^?6tQqQPlWaJc1w=M3t;3Rvj58%OUcNXNFJ(2n@m*@$+tu`2dJhpE=!v` fYHVvSEXVi{Ub^TjTGP(Q00000NkvXXu0mjfD1DE3 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_7.png b/assets/dolphin/external/L1_Senpai_128x64/frame_7.png new file mode 100644 index 0000000000000000000000000000000000000000..b56b995dc61244f395c0ab2713e32e00b8909c94 GIT binary patch literal 1835 zcmV+`2h{k9P)#P5NWd)xPY-?okReW&qvVB0n#qL*toel0wR2r7SEzwvA1akc z0#Fnfse$M{O2~Uw+Z*R&;~aqKr_wb(Kt!sMcm{NHjO*b^^y;ZY8J72QMWVhO+C9y1 zQ2X0Z`tT&uJvFl6+(;0-$RfwF+G~Ap)bT_YqYEyTBaK1p%iAIEAAN6y_X8RLk8r(2s4D0G9W9tY6FD6>@2X{yQzK$Cm-TTuhd3p5ocMy(T$u;zszo9#31;*Rdl40it<*BlUX++ zqKCHlns<-ueDR~DqSeMK4Ld7LvT(d2R>)LtrBe$S*8Yq5|)Rxqo4 zIk!fPSHN>FDWfH;9Or3SNQ8xJ3ibp>RvoDf(B~{Va(>j~(7lK}nz5nv(XJzBduqx? zf=8@e3saGZfIdeTs1p@Wo|QXWO?ZZ<>Gz2md%4CUh?W;etbA42)X;5Ama#{Hl%>t% zdA&6@k#ZWSHMRsn*X095!hRRqUxA|{QB;|zAA+Udx1B)QAS#$AaOHtkqcbFH^F-#X z5YmCl^Sq>h2m?nQB{GrX?3_M>sO(*-Od3}uO7i|+FNJdD@V{E;ETTN(DnQY1)mSs* zAR<@gPW!&&GH_P;7AKIAz#4mH+E^KNtg)m=%4fY=tRPZs5B95z~gEdYj@>#H|rV892hEyhqvR>_B z;7{sspr{awSL>o{1n=P&!8_1ytdcl#xi`jZ;C? zpsby|t0}UI(|;T2<;_(KZAFqRMwz2l>z*uM8AFF$sQ^%XJ2MnA=4S_m^FZZwr%@n-2WJ$?dr4NV>rVI!YSa#j@P%5H$PSJ z1J*f(hR!qoA(K1<=>{|asbFpIo_A@GtsM~~KElxDUdWNW;LQLcH8~LO{CZ{*(M3P1 zBIu3=W9%0Lh*` z%I4sPn)RSe+m#B5D7-5rJT1jqXZMmMN*rx4R=s}2`N9m4eA+c4`vu#`UIy3W8wZG3 znD!D!#p_leP^E;S$?okDbU@02PEEaNhhDNVIRFne-hoZ@9k@U4YCp+VSZThTuJ1XULZ_ zpgcRnEv&qc#^mj-{TWXd*%$sg*b{V2)`h5%RTkeOS8{C6OFNywGXi-H$rIr-4|yRX zh}CD-Fc4|cAc@WDau1PP$-bizBr1njU1@zaBi3XeFgJoS3g8p+&wN8BTVX^Ml`)zu zko-|4t(u=hGn$I=Z`+Tp;4ZRLcZ_#@3>sbU^fMFTW0! z1jI`Xd=fOcmO5sQKpTIyDnR!=xyHOzRHK$uG%`|Yz$j&eei~+#CXywuEEQLlKbj|0 zJv1{;R}`?;I!YqLKMvK(AU>H@GF73KKbb|95ACt|l|~$aR%vArscaBl4ukl@dhAqs zlw8zmZbq;oL6u|7Mif34QJyeo#vVXx9N|jxGrtD^RV>OST1kPd&U)CYrAHN3WyDzp zRoVE;ssZT-aV6eX>h>uILz5td@d;%r7!z6CVSLV@8Y-=R)-xOlO-4(p(Q3gO_n~>W z7s4w_km;d|J+JWQ4n)$0<}|X6X3XO+a!>?wComGuWD3y8Vh01y7=bre2VYqUi{NoS zh9WRp`_abyck$I+tzBQWI7fMG6j35LJC2o#5pRnSWOy7b!?njQGst>oc0BDKV!&D- z4_*ZFWQxdP)%{wTjhxmh$zM2t<@G#-%W&f5GmS0JqN6Ivz#?2xV?&S%)!N8=Su#GZ Z{R`QooeYqW*75)V002ovPDHLkV1hqFYH$Dm literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/frame_8.png b/assets/dolphin/external/L1_Senpai_128x64/frame_8.png new file mode 100644 index 0000000000000000000000000000000000000000..6c4b8757060ecd792db21380ce0c77ab5129d93c GIT binary patch literal 1772 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 RW^(`l002ovPDHLkV1i{(Q6&HX literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Senpai_128x64/meta.txt b/assets/dolphin/external/L1_Senpai_128x64/meta.txt new file mode 100644 index 000000000..f68f0b563 --- /dev/null +++ b/assets/dolphin/external/L1_Senpai_128x64/meta.txt @@ -0,0 +1,23 @@ +Filetype: Flipper Animation +Version: 1 + +Width: 128 +Height: 64 +Passive frames: 16 +Active frames: 22 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 0 12 13 14 0 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 +Active cycles: 1 +Frame rate: 2 +Duration: 3600 +Active cooldown: 7 + +Bubble slots: 1 + +Slot: 0 +X: 5 +Y: 29 +Text: SENPAI !!! +AlignH: Right +AlignV: Center +StartFrame: 28 +EndFrame: 31 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png deleted file mode 100644 index 340ceb47f99ee859720fa8cd89aa51f234799bca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1656 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 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png deleted file mode 100644 index 890ea5d70da8bab182cd7d25c234a2e2a1e9d610..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1494 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 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png deleted file mode 100644 index 6eda42b5d814555b5e795572face69b78fcfb0fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1585 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 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png deleted file mode 100644 index 554a177a93d8203134179860bc35ac3899407026..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1771 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 b4o3I~m)4F_96F#z00000NkvXXu0mjf;*=8M diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png deleted file mode 100644 index 971e8f55b5c36b7f5e24c68d4adcd62512986417..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1503 zcmV<51t9u~P)jd3cN7lpBDK~TTc%nL2xyv)oMky*1pDDh#Ld! zR{rk!BY4vG;xfQb!#6O@Nq83~QyCz`2v3TLD)bnChaUehz;#`*#NiD*`;#!jHh&m^ z4rG;CPu+dAd@@7f+IrV`wbojXQ2~4=(8l~SNG*mLAR76+%s34d@W~Om10w;I$^oqU ztbxBQa0XI2fK{*U^W4y1!=Hr<02;vGsO6FFJ$Fxjs589F8NLEP9|l>G?mg2Ik$pZ1 zt7JPN7rNyD0KbCGW`dKQ;K`r?{Nfc@Im!g1r-f2C2O@&zC9Y z;bm3QD9oG=vZo2@HfzSc0j`J6_vtJX{xlHao+C*0GfxvEOt9*1;@wUVCIfKr905R% zY_DE>rX^zJGYBd-A5VII^3hhq1?)?qsm{)+A@i3H}G--ZN45%iSV5a0B|?3wl`;@hAO^i(HWT| z^&{(AYU~{h5TV3~@o9dL%>}Xy;I;E|3@)Rzhg03`EdRNhpM+?{^T^`w@cm3B(K#jK z@n@}nWP5rX1MtOD1<-n}AGXInQrp=|LK%1&HLA4c6O7;5f*gQCiVXfgLaMjMrTsFI zEy<$N#{mYVz=M>0S(!nO7u8$_Id_KO&ux0m`$bD!W0 z+ZiBA%cEmUiqDJ@VUFFApDELF&vbyue%7-MSp73j5SeFpq|YvweHfJ80{A!@ysaUM z9{N17)bwC7Ky)wUii-4+q9!s=6jsE0b7^bYZN#3kSN;pw4?{c()jZ8o@(h~JjF*w& z9hm@S)z)J5zg+}zkBZcLeIkk5ccI4KoVU_MTph_T}DB6~o(RoZ=Kl;5w4t_c-f6I$#!buo3ha zpDSZFs1snh(&*9}dm{c;wgT-@C*TfY)zLP5QPy0Mc6?k3BTJ9&u|0cv<+xtC-2o&e zWJS^p9dV{g_UbiuMApjr(+m)aI?aF)29U;z&gIE9I#*_#462!98Ut_x;$#`cWF5e2 z%NNlrmp4x@X6GiA#&1oy2e8oE{Y#nI{!-uT(tiNL+i1zqh+LIGS$-nyivQ$2Qw}A5 z*&M*y*QIBxFI@;#npLm0iM;br%>ddQ05wS`j#HHghQ1ZdDVGao)obI8)bsgAQDGnp ze5=BDEB1J=41X=03ZzBeifGbgLpiuy(Z00(3P<2HjwPr0*=6c?0owkH0`CBilxPwr zIcr5y8G*$J{CRdc<@*3!fisw4bj(Qq*^VI1vnukt7$CC{X<>*R+e8m`F~UkGD06@a zrP8*v{&cL8Q_2yhmz7(9XIO2|>k(mO3)q#cX0akC_zyu3=q+V8lFR@A002ovPDHLk FV1g}Xz1si) diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png deleted file mode 100644 index 4026dc37364f9ff38c20c0ece6b94777fa4c8810..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1663 zcmV-_27vjAP)Fbt%F{{Jtl4+Xaj!(*t+$7X=QYg-a29wSO= zdJs`-tr6om4*KHv;3b_;MATX%{p^l-!W?$s#Toy!%zxT^dVnOs(UMlX4cS`zI&ULB z7~pK1s8$|YR!U)^E zF@PK>x>h6g?5*XK8H(rDJIAB7)_R;ulrw=h=9fX{VweHY%-7dS&`1TJoRMc>B%w+* zfcM^dj*Rui`6$%@-h0c@+*UjK>-cNoAfiU}PRrV<(S4NZcXjq%_V6?CM`kMjUAcO= z^Oka#PFD!Dj~al8-0ZgQWqYq3q{syW;i-6A%B`bd=~#pbxGneX*&SFx^u>CF-vyvs zH|?wn^=ECrfv^^@=%qZtL(T)x%;!GdmQsFZf@Mm5fBTsP{B0mnp0!9;duIlWRXd7Z z1?d$EI~1)o`cv&dl-CFz&;z3tneWNd3^pqJF3x9^7~rFGVHc1+iwuXfiTYJ>Hz)R@ zwHDU^$N>7p4>ca@e12c~F2SoU(EHy|7w{a10zeL?w}jH5*-2tmY5lj=M(d#4SMQ&S zDFMLHfq-N-I;I}i+I{FY(RUl34o_wRlNm>9fDG2LvBwe`l@rJ6F?G*8?hfnFj%YFi zL_&!^mx7GOWu{wgkrT>l=11oocY|ie5(A(obqG8ruf>j6Tk8bbP0g?03-9Yb^j;Kao)bconzR&VpQ{dklfFfozLp{zz?*)dZfr9*kx;ZE z7@x;G&3Hi#;MF9IUC!4w*E%gd36z)}XOd#kjGfIG)tUndmYvQgqW`}Q^bFjKY@M71 zgju}I)4FAuKa)|k%~v~L>i;&Ef9?FqA_n!^tPX(cm^+t^UZ+`@k@@nOY#S~CmQviA zKat`Hdio#7`%y3osdF`BcLV{;@oa=FZ+eW(PA~2#KiXKCQG0`+*ZHhlFM2X+HzTl~ z@+$S0^RR5`CCNunTl{aWPu?K9kER)~y_SPGG>hhpM!oSWPKo;UjytkkAkQQFb#DVJ zPK_e58U&s!6pge$tg)mXa_!=_DD6i&@pw{BM!#n}cZTzcDE?uHN8Oo0EdxhmWjc8BP=-NumE*~A z?{G!zU?Kb`VHq7~5ob}R1QHNd8{`=>hYT|KuHfXz`KYZlNFy zHA~6S=wXrGK4)aQoM&cdOLyI`YYE64X;D8@sct)xvAyGX0g3eAxhzDwS&5*7WoZo{ zm68V0?{)y0Xv8`ZqgYrG1X4aq`+8219o(9rX9@6tP!PjgE)W@BaTB3e9-@zw(L?7_ zzY+Oz9BX;7?w9>Iw3-XBERPs2P!jB0`R?<|Hn0*1S-TNA6^E{L{*0iv#^W&~=cCDx+Op>y0G;@#f$l=* zs{+9*2t8RI8+Z)aTJgV%jf6+-dJSatzapKF&NCaYXI!)n_zPhwl15tgi)R1;002ov JPDHLkV1gE38lnII diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png deleted file mode 100644 index b8cfa50ce63ac29e44091e6a3257d7fa093d7e81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1661 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 JPDHLkV1f!6*p~nR diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png deleted file mode 100644 index d0e44937c1dc542699af72070b551bd9e9017462..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1542 zcmV+h2Ko7kP)bG@H6lRj=2)NJ9B%jhyW{`3I8MgXhfa5rPjf1f{ZT=my z;jXB#%?AU}g{BU!9f)H(ndQYAgn9t$KJECA;|Mts$4BSAsN(>D0q{xPJacF7)x{l*!e!6K z?!aB1;XCj}vlg#B(`WDL6=4|=Gd*%nu(Olj<@_r=o&4$n0Q4FjWdiBGHs`No+!a6* z_?zDV_zZ-urvqG93J5COgEv@ep!PGr3y?Z{ZJfpoJTPYSX5c~kqJ}T)ChzREtJNcD z;6b7tD)GMzV@T`q{ry$AzYPSK)3wF~GlrN!V~z+5?0V9;(-XuP!)5?!0&cK_3A~1? zo)1`M##`pRxQp`~<#{OZ1*jxh#&SL&+wseyuLoc9Z*s9us>?<4J< zXgrG_-&yAXQCRb>GX`m%<$x9S-y`=)5CfnK+=$#^8tY^pJ3YtJl0Ke$+L*gSvip(% zpwNsbJq37G$$mG%$9fp=$<7C0CxKn4&~v7-)DD_7c@Y_W&Yj@u$ySwsYL_0FYn?Vm zVKLGjg1~b;|6x0C07M(a*@5Gnyyos~YX zbn6jlOaqYrk!n_MSDC0}xR?F`0t{eA(aH5ulkI6TD_1JFqY@n?9lNPpb7Ib zK*eYg$9v^WjTYFa=V_+f*^wY9_W;$b&&=n20sr&Ji)8lfzV#kJuHkq+)A;~R2SF>c zN*fcAHkIJ6~Q)C@)hFbHo>vjhN|D5YZkz#Cux&&y{vNJfiEN|JcYG|ZZpY3dKFj#2&MUZBo)yS3> z)UO&0SG{`C_I0piNHjElewDI;K*pjvUcq_Uo*?RcfF=g8>Wx;LvzfaJT;D4=HR61_ z2ZSdeS{Q)SDZeKL$61UpGs@i1mclJ_uYJDuvq_c61f6f{fzFMujRm7;<*$5(3_A1~=7$(`X0MzHkMSd{MB zW3T1^8AO!F>LA^@`TRzKif zOS(_dz5S@pne}JR=ek(-I6VLmQZtxs+GgxRv2y#F@{ph14Vke#N4usjBJV8R|W%Ry$03c>`-nor4 z8G*mgI6o2$w+R5mj!vG-+Q|sshLFznIA8s|D*$#G>kM42N3e8?K0UQFz>@=E7c@qo s@MO8wEaGRUd>i<}2Df3xmfk+#A2tH&C4KmzjQ{`u07*qoM6N<$f}r2zivR!s diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png deleted file mode 100644 index fd800b8b936fe6d4b2dad012c12f0ddb54903236..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1736 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 Tw_x!v00000NkvXXu0mjf>uC`M diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png deleted file mode 100644 index 05c5e969d7c10cb70d949d7d28c0fdf9b2c51c1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1628 zcmV-i2BZ0jP)e#l8e)t}|xbcaIT5BZB?uaMoumZ0h@lT8Vr|DBcBnYbg~R+*)g`VpJlX31nZg{~v?Q#V`XzBVS&T(`aQ}z$ZuK85jwu zR1RR(cOk6$9}8R$sT{zn_Z+8R``-`Oz(z!kh<4GLxwp#ixU;y+9)1S?UItl_?%ktu zUKT;$DO@2xSY`Z6=qhsnqW^-8FhR%NqW>Gf1NAK{h&~;GM|Sq=A(B7Hwm*#HgQcx^ z0ntd-n89O+ECv3YU_RbfC1-?L3=mm1j)Jy)eY0bQ-v$!Z_WEAVP+^0kUbM_`ggfmZ zT5CB6=orZnWIx%%p-R+dx47SevIBHP4}p*aL@3xhYw(O!enIjjPS9GTSLH!@n#WK0 z&}m=A_-LFibj9%U8SDNE`}3pGbu^Pegvk4XK7ePaXryKH1bH}xo<;ZH68Rh{2k@em z=yPIcXW6J9&naYNTWkJVyOUKwPW~p!t>vhF=ExTr1PL02H@3I*MDv%sgX{4zC&a!P zWbM&M4|FW2fr1&6E+e_(fDz`%o@`zXk=@vC7Z5$~_v*CvT(JJXte559xYd(IO+Kx! zJ2*k)aE|M?%K@w-+$bW+AIc9Z)X%S@he{(SoM$2+2iLi2q(M%tWBl8JMUQ5mqSo1TAs*$sd_~HZdKTD{L0=#ct=RvYgFGEy}l=Tc< zjeNvFNRA3dBiv&|I1kOJjYd#pN$}cQGIUruN7UmZ<42swVj7SAIF2xO#tB}YfHP5v zjtDF9zCGj1|09v@)m!&mmRW20Oa?HMz{+}tsQYNhXtNZqPM&2vuU}r@Wa30W3; z8;I6otxc5ry#6xJddq}tUzx;g4xrVK$Wf8oTB?YG=KxwXXda$%f{O7o2A9uNBG+qI zVHEDTD~ylkqqP?Mi5RrbO3%`OoCO)i6PUM+7(L=xE7#x(U03ClbA$r3w+($FNIHQW zKz6FJf%ObmHUn+qGR_oQVm{WSrm%SFW>V z11kXK{Dwagh6b}HziJrEvEd(MW&fdaFuHWX$R=a6V%|kROr#OI|IDYR5dR0h$u3&6L z_sGzhpeubf%tCAX{ke38Rr$dTbbW}EqUnt2}eC;Q?snPD{p z>;wRjuSJWcAVS38M>E1~Wah?W)on|>4n7)ESnWOoYf`K>vrt(cqRR)WFUd%>|EbtB z5%F47Z&AFq_O8--#*XI*;}a47W{}n4s_k3U5jbYFJrW}cqt$LmCbR@ZjC=)0SV`wI z5!ET;B>cIG{-IwAy_zFjC89@xj4kgg#?LrI_(P#xsF5}^@D$^7pu?Qm#-DN!lyL;F z-4ig|=`(A7G#PetsmeAms`x5aI2%Vff|d|!V`*%l?T}h)V;>2NwtKoG%+^!(SLrvJ a2mAw+lNpPBDxqfp0000(^U_4#5#o7iWbZG?H zbFNZKt+keh>$=LXp{@TH*Z!i;FXf*x_`4HZ{{ zf?v$SSwNEDivf0L&Pw@Gupt5Xq#(U2xSHL8F$3DtGuB#bJ;G)x14J0%#Eh_X&R9>6 z^LN_&3xV>#*=D^T^VV~)eLw7dyE8Ws0OgUTMm}St~eydrY>U4^$lu}EvjpRLh@3n8M3@l|e&w33{4}uppNNah< zde5|#K>N_2Rgh#M&Yz7t)E8ehKq=o@Lcd3uA!6j&LD79wdKe3QB(QW9OannGkdU^` zv+GukV?>Xq1ptNlZvp7hO=1FMoF_92cL~BfLM#G5IxuHOn82D>laqZFowd(CIso{Q z2RZ*Dr+~;!+YF}2F0xRWCk4yLRj`)5-o*%76MP9bj+3K6i_u8Xp5#~*Y7HPcxc$!i zAz2=|9gJkIcEm$bgbrhe#-bM*AV=ds?M zP$x*+FK@rL0OUxd(XKT{f_Bo-^g-s+obPQ5UeItEcLh?2)AoH4WC5v?dv5ZiIlVgv zZNbv-qtNb0?uMw-KiUEWK*NmlwB|IFZf7~4&M&dz?qC>lI%)sVMa_U`DD4yw(Px(P zz4=xJLYA||Qvgz-d+@e}$UhIA90A{>sSLys(pEs}moh>x+j!Y-ebbnrf!lUVruWtWodEcH+0QaE{Z93`L;J1)A{mVs%bTm$8Jt1rH%TV17kEo`v2>QcqfV99 zdk5#&D%=EWj^Pna`?v43?+$2A(%@yaHa}l$)aXt29T?%o7s#m=F&Z5s^^Y)177J-i z!TWNG_cGmL8r0~Lt{Ov44}$7g$;uLm)_7lTAnlos?`4pT@iZVbeI+?OKt@{>sSIEV zv1pIJr>({$7R|QrQM5TnwjU(Z+c!zi%*>>6_?kTCNMiskOFaW<2Gp{PE6)3S7zrq8 zoR{s=oDy5iY^hx2z1D`e0KQ-^TP3G^4&#Q5qK84OK=CKnAll*v-F5;Uw6y!tqi~+We9&iGPlM67Djcjf*W;qk6bRl&2dWx(3F-kR(g-l zIby_7Gw|T{kiAVD!2ldtfiUX8%;I!bjYH?pa(=Cy|8;N{YL)+(M0-C2na(fezwT83 zwua2M>3mcT;K5@A?OS9CTEW#jUqt38OEshxf_Dml2FYMr5f!c6t>E(J97T^4qy3L& z01m0bmYyR(P71S4Vll!Br{A6dI6MlqwhpVnw6cH3R=^=Sn?KKPj|hMUG!Q68`ey9i z2zXE~ZL=#!PMdY%@q-`H# zjK-k%-nd)6cdq+`o8l$i7-O)`7UJb{xQm{<@oG)a>bqwAUG=VnCkaFbzyhc-&&;kT zbL-$qf*0)dT4(P5S&q6x=6+L>4{f2;v_6VCtvWJ(({l^)N;) zGAwK!134$cgpsJD+s1=BWD?4K3Xs4BiFED3^}sTK_n6arKWr18cLxvsUkkmpc6c8Y zq4wxDz>U8me(7ZM;xbu-bOf|A{Z34q)C8Z{l>`UI=e`eM zu>qxi`lL{5XOz4$Q+xy10C?XF6oXbB;r)Wi^CgtE_y_>$U4ZAIj+Xfv<#T-`mdQYp z|H}b(coGPhdC;TDuw;235@j6aSxZ%r-6^1YYwci}s^Tp2>e$N0Of5m@wzjJ~Ij;im z=KxyqR&mz%27okv2GcJ{ekcgu3~=VLObdccfJI(Lc{2akm|%>dp8_~U_G=H5WNHo1 zL#uCmQdxobbDu^Y1y>^b+L)6U=}&d=99h4a3D_;ZofrVEeL4VNqkTMbq*#39NeRs<|!I`=U-ld9#l{0kH^sQcz#a5D*~@I;{5k1AbVEN z^v82Y*u|5L7E9dugfQi7jsd6xtO+9ua*!NBGmehQROVg=inO5j3Q#tya{d_O?<-+e zu`^o?(f-mFA<=deUKyNGqb=6ELU&2ODrc)TfK~C%R>T?;;Qd(I&$&4zncVi_Wt^Fo zt64I&!YZe`Y5-|L*<#2VFF6qJ{j?6??&13w-nt~Jqx9KImZi?-7=Rj(8cL$1ua8Hn82I4QZ{j8G=06nPRml%4}g=dvrx1RDKInWgoe zg%luJr*P}o!4%SZqU#iY8I*N~MAh1L&$SHj+^aKsC39fgS*`d75j2{j(8fd@o!y4$ zycM(#vUWRLVunb6@jrsR#;$15<&0jguZ3p}FFBmM=G_|32n1~#(&oR4^DR3`P0fQax9E*Z9NkJG~M0uW&V^DPMgFq9Sqcv19PM0Q3$FIEMCHmA0Z&^)UG zfGVdU!T?luL>F#OtH0DQ8w3=qwv(j+0EY4e0bs_d-fe?pCC$NJHUfB^?a?_ipVq2> z6?+CE&YzL}j%;QQxXu|HkLAXB9RzY<9M?iW+-m);7UbN50Wds)kWtT~dCsx)TDHEZ zs82X~70%zn02q)h*g*7im3&PmFatKK#uiZ>sQii+Gsr15fOkKwfMn?E?2+V0YJu#0 zv$Cr)-pv5kep(4>o-^wOZT@KC87XM-nmjFKqHU*uRgA#R!xc|ebItDeRL`h=G}Z%D zJDY1=?*gI=%}_Tt4^`Zne+C|%Ip;`vTaXGl`uXfO{VqTnUgan(r+TmE7(jz{V#w$L z@>e+h(9Q+`r~J(Q>^#}RYF4i^3e;OMfTa9g0wL?bGXen1Zf#KzR0RO1ymY^sHY4*_ i=y9g=={$h5g5W<8wiy()Hy@7x0000FbI@WbN?%AKBkl*grxz=a-4M4D?LXGh^2+? zG{Y|K_ z%?kt2g{(Fjsk?72@61qRZ`nN_z4xARYHL0dXlwo$WG>1KfMz~#BSAwIymLnGKqa9< zHGtJ0b?}b`u7OYuVD)R~Ja_cZ@z=u9TI;R7W-ZTj?>a5~qQUSf_wY0Dd>LeAx_3?6 zf#~&>aF%XYsD<8YfY$yiwwej990Yd;P2dOb7@bun7;Df!-fsoZ?q>&g22DKkT>x6y zQ)gOpv_9)|F9?1tQUmldh#n0z^LabQRMs2)(d>`Dv(SR}+W@}}Z0)^9&_1iKmwv2i zIP$D#6Q6bm5ek6kq{&b9X09h0KYEM%O-KbmYxAB3tXNNDrQh4mcRc(ddPRR#Xip@i zyB6AS&A%gh@9mG)=~eSr&3<%U>D$o!`s#sR!PuY30K;(Fz4&Zrt#CeC;eQho(C-cm zAHOlcs%+c^Z!bL4@5%()t@{fDtfb;y^~1|kFX7&wvK{}N&-pY+8P8j&2&{k~C+L&C zS%E^0z(1EBKJoF|{Hp?Sy2t=4B0YNBr9EBD1OxJ}g1`6B#zpqB15u~HwBtFz?#doj zc#M@`J?hogSn<^H45Xax)c`voMKQmYiZwot*BR2Lsq?Ljw^}NDHMG4>X=gD&CL=6) znm&;Ya#^ccPLrL%R41Rw0A3O0Wh^&9#V}M}Hq((Bf?sEYY)OWnSp#rtjkc0Ov@Fx) z@o}1*4Et#Ph>jXgb#{dTv`pojp$3SybPer%riPZ}A%>;}+Z`fjlDia`?>&eq}vexarzx`#HKngH4 zShg~Nq$ux`j7SaajhDvEU<5DwEV-KgtDHq`Gq5WNj(@VTuVi`7;D(GkCF20Zfav;G zKgB9%XjskwS~h8hkc^9FW)<0BK-NV1cLxJUnE`0}^No5zpczWqz|0_!kZWa5rafFD6KcUCP3s4pbp_c^~~C3%vn)Oq#3{qfQl1$1wn<%vkXGz zYsk{Kmrzh}NdbURJ6|}8NM=@mo~M-~LYiMWerDDLDF#3z9@*SZ%*Y~XJ?UEee_UIz zYL7|)BMhMaE&#!qG#-WJT(8xD1Pc!YZ5=*V8Xw7gKHk#VWPpf6&pykeSXFp>=PkXq z;IRZk_nwYkTVeo7P-Z@g==vF&=ei+dxpN|Wvc_BMRtiPhmc9|BQCVdzt38hRZxs1_ zi8`&h8qH>b9ye6p7*GGCwK734^W^gJ-X1guN^EHL(SC}I?djaD3{YVLG~;z6(?E@ zPyup0XtK~F0y%mz%*geg=5*3d{at{TCFor3OrN2Xl$nUG=srAs$8q4TCyiU>e3b!O zoAVxg#xXF93Ah|2SS@DAus}x7fvHYM7@(Sgnfcr)Gnqh=W5GKXq%~>5R{<{?S2@3T z=Pn>?{EG9=3IZ;Jt{=&K4jKpFvdNuS(B5itr?}a}N)|gvQ>C7B2BKZ>oP_M}=Xa!cM1fDq~ z#^2EgzdQhT|D|K96>>0YtTE$liZOtu%Wqbyf1<=m@loE64?fu2i6xIAZ46FwYG?AwD<1&4bT1_aDPwTzh~t?ngf`z)uw_OC zDjhfUq@6twW8`lKdqq+NwX(bzJtxNaI|w|HRa21H2WyY_01{w>p0KN!+HkxFkbrtd zf;vup&awyqep11gKGGJnl|&gAsxDUm6L=CqDUWgmu!tQZ$T!AHLF^U4B-ZA6%ILMN z{%t{^#M3@jXw{FK!k)Q5Wo*tfo=XKVx>sO5wcAP%^`@{naE)%l1I!w5M~!_g|c*Bn7bJu`Mgcvo3-XVtIfa#-By9&RY2Z>A(*k6Ili9G=8n) zYjyGxkRWnG%5I(NE!31h|iLPliR22qBR6!JhCn9w6aS1lVZU1Ek~B^@&n^GAsaZ zqd&)+2ukaE>UjDqi2(CD51@(Gpy&+%d+PC80eJ8NGnqgxuN8naD{>2xBR?@D0MGM( zj-(gZnfxi$fz!6@0gREBZ|OaL?J+(jN|n8U%RJolZAlFHI<7~$0x$sAU{or%mhvs# zC;!~*r-0q7fsuF$8?l!4YjHh*0=VN_BXR^P2B?)+I(G#i1ScAoUMaNp0!sJG#WExS zt$lBgaEy!RZv1`*&i;SxUe^Om&}&4^fm7Q2q0Z&!pPd1a1x5`r`ni?fGi?mj&OfY= z)Cz=L1yJclsu0guH>M_Cc>qpQ!((Um*4JYjYa|hLeK+v5PPlYz23_Ny$v=PX5Gx?n RwzmKP002ovPDHLkV1h+$`q%&f diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png deleted file mode 100644 index c6caf88d94b0d9536098767bffc4c66c4b9dd2a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 812 zcmV+{1JnG8P)?aEn`=V06RJ#06u%2);$!TqlGheHXR&I21tN1jb6=91hC-v zo+yBAPcvtl0$LG4iBuHWEC&0GB!1olSRogEyf)evfZhoDlLcrsVian7HICmc3$Vfr zXK9S2M;Z<_8Zg4lnW}9J4*@%Zi+@;UvBY>WKxa2IVJo>49hn{pIocVJq{DNH~d&cdy39L27_9Y z@YYT&sUlykKvvz0APp~AC>W{7Y*A{fDFBHxZf6n9F3HQsPF)0u>?nd6v%W++c2xmL zoc+7PHP&h)0Ki^RfFr#IP%Z%HJ&yv=68<&xQ8FF@jN<|@jtjszj{@+u)dt;%L0Z99 zHd$j^0n`ps{CN4xmI7#{u-1>4Q?}a#Vw}3uo5WE>M}V4hddi)8uvTM`BTfLlWlxk9 z#MI8m3YQZx0>Ey}BvlY&z7xUS*PQ0h#4YIrQ3zfg%HKFsiV)ZZpmqJ#D%u>@EP&hi zQ7HS(or$msK&$fs=)*Rr2)26<;NV~pfacjh3$7^O5x_Vu06}m>0T+OAmJ7hwe_410 zFpdjA7_0XIE&$`W01OjCZ~<7y1z?nzjky2}lBVM9^#IN~N<}DQO2V8rBUMDL+DR9H qB5J?()q3U$7l1-2S8d+}8s`u2Q7b?iK{5mY0000%G@RH6aA}XbjzIR33VGc9!;*7sr=HG2TEr=w+Rw}J#8?u!0IBz1p z7+|&WSC1dTotBp>1MCei;FvSvDoh3$VB5BjkB2*D#3-~le}x@C7+~ABJ#w`&^4=99 z`ND!4&8O5#wQi^I#*QLiFjdTK1ZP zfa9%pXMdacr2yTFKq4Z|1d-Z+Xl&v5OE^QIy;%{WHkw9z22u{{_v=_?1L@nhen9j| z=bnGK6^H!HG?WWn>1 z>gjy#%UFT5xA3V7t8Z&4|GeO-ct!S+$)) z>vskjCa{(VeYR1nF|6-*V*+c*4gV(40%C&Nz_OiJpjkk&&^C6Ye+_qF0(8*#_Burn ztpj9xuU)jiY->3ku?{-p@Pg>7g<)hKX$HtFH2~^>qJ1u04NJ8hVOJ+wie2rY!f9y!BQpQX z`CH;NFv0*{AM{*Lv=u13MrEuVV`VUCM4WH258pfVEx?o{iq90h*j1 zK;N?ruN9jOIj7l~yn;D+u827#K7JMKIovaB>vM?_)L(-P1itpr^J-^DgTccab+9u0 zXlMs9B6?(kO)~=j`wUK->3kxw6Uc@@owI_&3aDkrf;X;3eYHMF4)v@xo zBLZL-W*8PJ-y+L;F44Mv&kBSQ?8S0MIG<>60PI4IV$h=h-ar^t7@nCCBJI0$IwAme z;RETsR#=Xh#u*qP8ziz{i{6I@z&^-8IzP(!))*@oL4R*?ekM4k1b|e8=vX=*i4}|x zEeK`bnzOwC=>9KatVtlqnt;F0$mmvB`py_9;{2#i=yiL@K7co1rofXpi}P0kF@l8m x%B{b9tT2oLv|<@Xc<>2}5#;>{+;v|B#vk{1sswN{smTBU002ovPDHLkV1h3f3a9`8 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png deleted file mode 100644 index 8710e1a0a96e97627d7f0938515217a35defa493..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 536 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!3HERU8}DLQk(@Ik;M!Q+`=Ht$S`Y;1Oo%( zDNh&2kcv5PXWi~QtiZ#Xc=iAP)c3-nBBkcM7RL(G?yZ)pws;bD_Ka6j*4oY9H)hSh z$r^L1k$G|)3fnD0Scemq{ls_4=-(5G6>5ajkV|uSO zR9|r1$o$d!KksJV{qAxZ?YO@+F%7cq|2rp|%GFEP_I`XJxZ(GQ+qZ14-?(?F_Va!@ z8?^)P4(Ho^J@H*y(8lAxZG%<1;GU%wE(}xe-tVaR`1xPM;h*()SOcW%OzsI4uviFc z?p@BZj%ma1#~&s&utZ4kbKYS|_WHoUJvTVg<}8D!Pglo`ylFvNzjmy>`|2M*fb&SbRsVP^tleqWl& z5Vv&u=6$AiuS;`<8r*BEUtiz!Y}?Kw*BL9W#3%og+TQbUHG|AQ+igiJ<~o-$@cozk z#FOQ`g6qITd;eUHcIJW~=gUl5<}1&?^YFW}(z1>pKc-)M#2`~Q-_ErlgL#F&Wz$-L zE~XDJ-rv8cd61W3bKiqx2D!hFXSzRNYxr%>(EqRSsVAgBp9kDNL6Br*LY(r+3NZkcv5P=bX+vY{0`Z-Sz+f=~tZ3qOI3bHq_YK zGJe((W9VaTNM?Kx!C*luGeSja-n>J)Ae{+~9RXMGp9e7?G@QDsuPV0S)v`DHJ+&RO z-7X_r&4Qg+o&y}`a@-h_B>^2Rh9|=oT}B26!PR2 zN4hnf3v0P*xaEQM%kK>CC5qqH)t*&f`*-dprk=S8VxQi4Em^}bckhMm--R{x4+I{) zZ0%Ud7o diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png deleted file mode 100644 index 94b79d2ed9aac30fb2e73993906569b17f80079f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 897 zcmV-{1AhF8P)cfKZZMOCo}i?TbIa*qCpZXK-ys{c{8fAxT&PwBKpa{;3xIuUfjSa9;#ss3kyp#qCG0HA!nr2mYzGx=lVn za0kHe;j87dIf8_cB&-AAnM#SEcR%-n5`{5;?@B)L6s!aVz(gaFB&c#KoZo^4E^3`RRd^5;4rKt?gXHn zNmPM|*XA8H0yUNhxDtT0>TdMz#nri$Bj9n~unTY_0O?H91idE&%bD*ad_QyiTftNy zM^2zE4tqkFae~N&-zUSD7JgYCZeYn0Ag$Khy;R~QZF|qvw&^W5t*=`PK!8m(0GC*c^`5KtrR_&s z%GTd)H9)8$P}-ilheNxiOb4(YL}x3=F>;VqL_hh%xDY@?o6M6oe2WXX`ZO?7b=|g+ zQO4WDl>;=SHGr3)b0Y9E#vaFyf+>0*z~FmMBUuJKj{A#C0RZ&>8%S@qNvzl+_}U_n z+TmD@#R9hiXy8E292(68-|{%xvcFke)&O5dpY2=pDY<`Dm`>L+?i~QY{_sUQ3wO-Y z)2E6d1kkV!5+{V6Njd2N?w0_7eaX1(oL_>ggP_yAmVAH|z<=Cux~Cz80CvyFmqhRn X`cZE4+77BK00000NkvXXu0mjf+t!%6 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png deleted file mode 100644 index ce0e210f4f2202902eb5e63bb6ea0de06f4d7e92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1490 zcmV;@1ugoCP)(^Xn1&= zKJWX!<3sQ~&(Znj)@0}XgsuH|djVcVo$v{p-vR9j@H|iI1fEulu4}LWCqUL=Rd%vi z6=X+Ex|84sZIH&z$mVt5tcmx1Lu-AWr)@}r6JTODf$9v;Gf|RsB|-Zf;08EHMV-fy zq~l@uBfw#FBf${@yzhHe{vva0_oAMm6A6wgz%3$pH{7$fZzI7G0??AKV={CD9Y}CA z1ax>{2`eeBbs`CTyWep<1au^U=1^(9XcfCfglknmmoPw2{#vik@26uD(?M zYGNt^`p`U;9|)lvDQ&D3SF4pGy9FH-Y$E#%$EyJE^@?O`stW2=Zzp8Q*y3G)=jF?; zqYy86PT0)a_y`5?fHHY4!Lkpjm3nA&3JV^T5g@zC(#e-Ha?4iLDXp+|V-0N;K>9jH zESFgAa-@__tfWNM$Pj%8EeOEX+}6pLHfX({I!&wfEu45woWbtgB?Q=sv6cdGO29dN zZR2v~sJ2Gh0;cnR6Iv+1gva?(S5Er6+Kz^S2r9Dw6UqW_1xHVRq&qn8r}uJB*P2eA z)1O`YNu@v#1gZcT=g+U4#B*4iIg&z1*}ru9wS2+~1Jy5vs#V}>2w0sJ`9Q`xetlVD zy9`I?)5eSD%z@?^R$hnPG55b8YU2+s(^Bihdk&+GfjZXp1PFHOG%j3 z2bkB;Yn0ZVIn@#8UsV8l(yFl9mz=28|B~~o$~10%Z& z=WAr}K9d!Wq{@JEvZZQsT-AQ3KM_{8F!hu1kA9Yn|HIc8#D6 zqy)|zVl3x-a}_PKkB;HiBbA`sSjnb!PX=3`3EWYENOBgDKTl&uQiG@}WOEa3tcHrj z_V~xbwZ(fRQnG0^bgZ$KUF~E_G20?R6r8f`Vv7LL2eM)!Vq;dj*6H`Iy@CYN`aEdS z2iOIz-`5ko3y(d^`JPOiI#bK&5ehKpL1LQ#G|}Py%xjiRuZ}~2Bf^doJZt0fk6jX= zl#&DdWe}IaQxc$AY=IKNl5)>I(zP*ohgpfH_IFKPb^%KQBP9)osz%2OPmbWJ8a#1a z>Qh*@HPEoxh5(4zHtmd&>(E8!=H`j6-J=jz;^!?prNs3k%RmW~(O&0Bm4!FE_l(OB zTyuW(86aA!6-1Ew2=vu_Mk{wb^{&UZWN#PQ6Y3eqm8yvK zbLC)oU(p2M8X+944x0D=F3^bJsoS%p*d;+pA3$n&>_{LYhE~Pl)h4HUl{vtiQ)?Ef zV=EMZ`w#0L-;RFHJd&a1hb0j(up{i>NCBPW*xPIb9@8UKQDqH3d0??9Xj2IDC sbaVa zMNY9&gk-#G{ zYzvVABnR^vt78t{aej&ez1IgCp}eY&Mjk36sjSS4p(`u-tBk+H?&NEI;JbmS60G_G zIl&cg1b2?)yXHpq^8Y9pMQ7w}KU+9ThZvdNhSYv%C%7A5{6X|o$x^uG7-%1F$=2Fj z#V+3khZJBPs!(_fM)NQNqz})bk0p~Ib4!l%lNsV|HISUZGQM_=&`UTT}ALTHaYwrz;h7PMW^UJ1eM9#(y^lH z0F7a_{|PWtz=DSzyhp`|qR~qYDJ?{cJkR@rZYFpF=A3vJkclqOV7!l()1{pOy3fFp zx%(MH6>EKN2B4i5v@IO(x7yPpO_R@Sfhv~lt80Rtz;{Js1X2JO!k9xkE$kGGf70g6gb1@8f0INEj4`zJAh zE7FDS^I;dzy-;aCkG|(Da)!dK!jXi<5=!ozz17wvGiO4LyOEn0U?Uh64 zQMvM30_E5Ux*|Ny0F`K_i_mrgkujolt3_ml2`nR8I~BxS60V4J7XxHGm*;RQ96hIt z_Q>Y3X|!c`E}%DJ&B~T{VT(#yfr_Qas~W{kc=z75N7S zh(^EFKAQt6duX7nqNN-395IV0Vj@f+_0Krp=?w5!FR>G{OuDe-jZt%)NPphm8~L4) zC#9sT8Q@R^b{f`$t}#*0_4e6;juDg5^>mMOy90Ql)=~;;j~1!P_eM&j{h7{y^w7ot zROx3rf=01Xa5;!(EhoD>@}o{+IZK)WBt>?@%7a#4PkWT(t@a&E5S4ePGkDjN`aM7v z-1zJBoO)HiK$WaH3zz4S?^Sx``_AiHCx1@Y>ROM)0BbRpvUyv{AiNE71a1szoG3GS z7=b>kGQktBYpsXZ!4wizYi*I@k>NAQrX}udS_<01I33Kub(Ug)4&{60B4f}t{8?o@ zBrC!IJ6@^YBK=eb=v2H1C^EYpPf7#)`3^>n(m7wFwT>~sFM^PAJSm_G?#UVFi}c}X z0X^TrxZcR8Yl$$z$5Aj+NJhC4XybXuk{mYDUus*S*DL7WT9bL5^YecMSqk({zzd{& zkMTY8@G_TY9aJZGM|$Tx4zGiyMQe(!ID*AE5+n+bj7#N2`>noL89mBoAqL=`EoD_F zi6)#ur7x8+M&1#2k8n2wtVLOQg-@=c%&Gx99DvFi;~GXls02ZDe0mN2gNQlt{|!dp zd-HD=oyH%-{?VzY-&(VEUH#}a(qEESW`HF$62W{C=zG*AYQU~^lkFQFV+ j=ym|Kh+pJz1ta_c)_1ni5SD6$00000NkvXXu0mjfMD{rX diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png deleted file mode 100644 index 1aac855834c50765ba9674b304504ad9a2ed2d19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1538 zcmV+d2L1VoP)pZ zz;5I3Uf+W!9WPx5cn`dRW3Gg|Fqz5#$8ntgS$I-LR3YR19dA0nkJeh_*{unSMp>1dA8d>;z+W%(5#G$#HbUp$ zr9iq1Mr`>}0|0zxZie>xrX-m^?8gEBe+!^Y006EdK@?~N5#q-H*TDe*4dCpsUiOND z6vs>BuKqOfO#%8Y0s#Ox6L@L^fZD?Gn{b6d`@@X@^S&LsY(Rbc$2$Pd zbZ%|3_ri$Y%WN_`_Xiu)rthZ!DQm4v9(h}6jgc7R7XxTrWKudILmqLiUZL*U0G)BsX*72wptv5z-et?qu#T&EPax zaE|rG5ywicwT{(qWqwrtQ_x!TeGA}y8WUJg58f9_+WOXbt`zMbapsem0R8t-18@WK ztmWSfD5wLetlpbKx(1i!VFKxCf$y_dV@TIeV*+lc69Y(Z0eepbR!tx+Org~nUM09J z?KQS%eh*Jz0?j@jKL`Pc(KQP9qIb+>*Oi>^2^Q_XCz!5|o<45|hz5XEFpi9k$m8=o zPUqvZxI+-QoKD(TU*s84BhY$4dhF8*Mz3sMcUb$5)C5v{)tW#&+r6#C003)_0j>mh z(x|~_7IeGWAmwb00i-j|V=!Ln_rNor_hCzAMhz`JRalvyZBgzDXiqyOEs$RXjb`2m zO-kACWdu)QC>4IAunNghlsegB0L)@;_|>2r;~8tQjFstDHDA$M>t$`gZ85!VY`3FX zGn!>mR&fLM_S$Plb@QfF&5fMqmFu4oooc2#98U1Mi3aEz`K{uU5{r3^K0YyY18Bg$Lr@?afx#%e}DJ!nhym63cwZnO+$F@Q7<;2DfS_tUdWx0Q~7p0OmOWQT3Ik-%^%WUCiYwO$mt%lTAW&S* o`8%+b5oR3$tFq?FSiuPY0nQUmgswmI@Bjb+07*qoM6N<$f>dYDyZ`_I diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png deleted file mode 100644 index ef5e2fc53b5ac0d5110ccf4c1b96219f6c65516c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1668 zcmV-~27CF5P)e^kD*EFS~Lv9sEdDqpe=U;_ir zZSQKW_1=3MxUQ>xkw@?SQG579o!{EO1@L=M#3!ub4*ZO0eNxmS*s-3|vL8u;Uj{gZ z3BH+I2P7EZ7+`k<@79mtlY;c>;A)=Wn4K8gIQHqZa-czFfCwXeQbtrEb>`jQ(Kdxoy(DAe;R}A zUf(nWpqbA{>FB5#M-BY;47HdExKIyZeeQkssd)ODLp^}?xrXta_wS26xLRwywbw}I z+3a0M?zsjXkU3~g_11R6T0H!|3br!cyB^>_N1Q+UgcF#d>fMJo=LF_^>j7GOJ=12G zU?B2N;c3}~I`1^QTYF`=AN~RD+Otd$*&Hp?hYkD$aRMUWpEx1QIbN9B`dt9(REZg) z&!GBt2ZPn;Jn`Ck~>`s0`H!+ zNi*-;t(R62-PB&x))ULXz>h!WKX{MOn*W}_LnITk{eYE)k=}*&M*P0^PF{KwdJ^F8 z1Bed?YrS=fh-^^RIbB1fu|qQaROh!AWB?A25eAUVMs*?1*G?7awdGK<k<~0ljyu z2kk%Y}RWx3bEUPEc3sr~NuSf?jrrC)11Z?F zo+Qfzv(7&uc%H%n0M>m7BhgA+}ogeqD zcM04k=^P%-d^%Pi;qGPuZC@Y;Ksx*kBu1e3r3D~%X>w=mMBZUY_r1Vf3Deoa=$3#L zfsm1DJ!6#_boBIiPR*>P3Nz~t|G$9Bv$Z7)>AQexy&T2!JW4lMy-w z4u#bCNMn{kG&m|YgX-1nuP856Uu6D@G5jKkH+9}z3(+7MjTzS=WuZs+(#V@#K1Z@A z!Ytfw3s3`BFs9!GA`9Rf8I>_0Ii7wdWwMv)+DL8JbXn`AbEW4k+~xFC50FK)re@?1 zdC`W?v)Sq;Bw3a|OJ>ymBN!ly>UfhjcwF-lVE`*wc!`i~L4s^m50e?;Ca?;Tx{1Pb z4Ao6-@XCay%d>^sB|Wn{^S3d;3V6N70G;DGzbZ(15NHANF$|F1zzj2Fby-1>k%#Kz zh%nNs@ht!>`a>2(Hy1@@uB0mq&-p9Lm82K#U6zcUtuWfecpE0wI#iyeAF1^cyn3wZ z@UEk_dOZM&*&q;)fe|X?bFIzP0)#I*TM8o3?BG93*V;YHzDy8kLE#0<6az>GQo-mV zwQ{f`!y}Ak&DR27S{pJCou3)$T9Er82GHO!0}=!kS;N@`k)35VZB0>DSP=x$7?m43 z1$eYbqfpuiYvVk4ay6SY`z=V!<;k}KgUaQfS2{m4ssX^WK>Jw=0%=36x}^ogOkcp| zWuDKl*X|(Tc24yG7BbGsI>M4=1yi)_c>1U;UV@NvU_B!xRz*+XjZ?o3)Tr`qP+JBl z^Hl1%F6@8#NWsY=B~jM-bgk12AW=RyRGWVnq$ZFeVoSz;lmXGKr=M43zILCF<{yZ# z7LN1-(k4_vlNr$^Ie=bgY_OVK?fyW<>7H%NdH_wSW|-8h%<4imU$arOlh3g*8-P?7 z>U65RqH$dEg%B^3DoTdM02Zvq4b@BKL~OI>dQPv9Cyh(K51>)1R|6|`TbN;?NV!1t zP-oLo3Wkb|zl8xfyk4QA_D1cgTDDc+?^sWy;giN&z7W#8{Y_%}Ia+_SsBTC#+oQb* zeLgx;8Ambz2ihY@bFxjACa)UNE?v9!o~v+v`6(a@@6;e6+PQl*yP6Rsn=G59AV38cS7A*m7&a@-dN}xTC6^tvK|H1$f>OvNiLFK6-!Y+3OKYDT)bCx;X z>j9#MRs+pqgwC}M9oWHqXmGA_I@|+1B@if1qJ9rvAm1^D|5E78oGebH^GbL<&^87@ z4Atg%^D;P%&Z~o7>w&g2Kz8A)Sm6ZcQ>+YxqZpuKL7$9OfiPsQ2!g+^GYZtf-cH{D O00005Az&DLY{*`% zwcdMg*LAhtduy$=>$(~Sz7PM$zv;WU4F}r)5BK2#WC4+sK#5Eb(YP&!_q_^MjPVV4 z069QthH!8m+?IQPC4MU73xYQV@OEHDKXnN1p5{zHjDaTz-V^|?9KlkGpwFxM|Aff> zCMUf*Gn+_$S34&~;tAxA!YT7*6=0NyQ&sq;$gz+i|Bm%}Q-G1=vx71HpAaRxqoOt+ zy8yswVzk;}HojSTs_OdSiZ{y{yw0&pbnjxv4A2xF#nVH6*1J4A18@J|Okx$4wRxQb zXj?qbV44xyJ6`9~4yk^~;|b(&i>saedx6_WzZp-S9 zK0LLDT59m@8nD!iP=o51L@BlXXNOT0;G?lYW_4?)9YSTdp7@^Eo(fL+3J7>a8sj34 zn`CzKBmPDJY_rJMj z&_(BJ3F$p(Zygp{AGAg*!3pTSfBroHJjlTNzIMb+9Z0GJB8ODjd401Iz&bzZ&5HL^ zMTj2xO7e>d#5P*T)7%HH|EvT!4ZhY}+67eA!0N}U_&FtzCS;B0Eh4QsoD5E3uj`6! z11s=uA+r%!ElBCO++t~V?dT-ZJn&UL2pVlnYrfM(4?O$)NMS0e5%;@d_iT7b$Jb>J zfDLS|zGxXjyt|Xg3FZ#uM@hd$0W`xUWKR~kF(SJLcrP-}YE($a*U(AetO;2Ad<)u+ zgUjKWa}RD;v1*q<#~Veyx(mRVxiyluX`{x`>Htq+3h)${+EzA-p(Ixn07vPl$ypPh z(H|wp+G5g3$M0$VMbj2%>niel@9`8c+X*TQD1poWcnmro*N!8{`W>k?R+1i40BA_% zaEC%D0vhc!pHpxV4_7Jjx!-u~skwY+4L}!#POJhck6P`bV^CPcd*ocJ0koQf_J8N~ zNsZGcJX)H)$f%7~z-gGh=wRR7gTwk=tol{PrLsJ$Tt z$U>8EDN?lv=7^G%ghs_I+#UH*CxNx`v`;%gCYm(jX}ijEjBM1?K2}Eh%B#x(Bq{e` zW@LInYXN}QIqoJYlP2&SOA6qLSgyS#@v9tzrW9}5fOb2O>bZi=QKbr{04X;&R&4^{?fk+mWGmYamBQ%s$>>( zHR)Cg^YpjuPr_av$B+%Fz}tQ{Xb#1Sb$5=Z>x1u0wZIk%fFY$6DX&>6i0glrdaP6g zjOg6AQveKzDlNkCb5HwGu$*U>B0zuC25gHK@mYIrQ2+`Y;T!>yBH(+tZDdN>NaNnm zM0R%E%J^^B9UK5)Nj-HG4j$?Dw2vaHOfw3?V-j(f&CZm|)2s4&;B>CGZ0EOlV zbRw0$wC9zAaz?>8y1%U*0KkzWElQ>@?m0$viL5areLz3M3eXcA0KgT4ocvcEp;{PP zV?v*IMEsEoKrvFzt-^9v9+&!FLHe-@KrvFzSCKD`xhmqnLIEgd$@z%f9UW%{=~EP7 z)na*V7#Y#49RbEVQUP{y1dNk}ZmA;7N&!1K!s%F{2!G|LaRgS1$SeQ=002ovPDHLk FV1oG?)rtTB diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png deleted file mode 100644 index 789eebdfaa25f10335d19e85b2162518cd947a34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1521 zcmVGwk@m0LO8}3Wpm+_BUaMZ9W)) z4rI02NIm;#`DBK|wN=-6j4?*VsQ^9`Xk*fTT;|6vc}z1vH1m0z2^z`iQvWAs>nhA7 zO{xa4o?BTxGu97lk*Wc#=h{Bco#{Cj=kY#2^xy!10q~i%JUhMX(!|0Tm2p?F#a+(u z9eDp3WM#T{OiUhB7jXl0*e21!0UM^wLGFxYrm4{5e#P6yf3M1;<@f|2WJ zF#(tVohTB&1_Jn*nynck%;DX$GMvlos2lEff-o6?gVzWEbEgN1DLh1-kIyrwlMXhx zlk)+R4DhFOFten75f-r+B4cO_w5XnUbK(qA48Y+rg4TNP5h9r#)s4>;d2jJ8b-@^8 zjsZBbHG(w`&ssWmWZ$>+$>{M^pdF_eKpK=X0?)YUdjsxd?l8@H{4}6d`Bv@mWG0}e zpSPVm89;)Rh0*r}8WTu$fL1p|b&AN}8InEQ89x@jBvb;yI@3$zdPer@22Zw=)y@utDW}sKKsw876Gr4DCHZ%4T!6Y^X+0pZ zjaKbfTby$T=w6O(hx5f6AetqaJv1_AlwvE}K(&8ld~N%fMXQr729PF>7%5}O$e2_y zO*Rv7niFO-pPvq->uQiK$?z-#oDC_f0QxdF;vi2(%Y5lN!f4x^9btg0p*uc#9fRNL z2k&dGmif{(UBlA7{4NaOWwe)FyYz_2ST%sO9q^zy-TAv2fbQ`fg;Iwe7O1Y-8bEu_ zbvV2IZ-s#)pvI|Wc!e3N^U?jh!{|6xBzPXh0JvCc*a{m|*dv;@IA^*MsSuDGZ<)1g|}Lsef0w!YLcSca}pt zw*@*M|L*o5ovRL@nq{*NqWvWnsIW-2Pq*`>akcxrJ?-aU%c0sciQ;DG%Z$aHh^Y4O zaJskN(`x_?R&8K8c_s+7u{;?~H_0g;X7~8naW2CE6d6k8R6bK!(z!M8xmGwK5>(lB zUY-Fc_}*M`@a)_ccvX$}x}9wmjN1K2FaU?e2o(nCEFd*q0pg4xp{n03BVF>xpfU76 zL8pew?!Ogy!GpS@Y^240w9WMItO+j303048P;i-S_E+~_ss9Sow$~ze2mpi@2o?)O zs9Tb^=E@XgbllFuu&o9Fi2ZBWl^_+1cr6)8g6C*Hm(;haZv`uy|G@xJ>grfb7c`^r z=g~q@lbzibJbia&e5eM9819OJ;AL@y&NVhcbgVZTY^$6et^uAJ2ox)+--ESXZE=3| z6!4@#=%VvZaMsz{Mgl-m)RXb#c3?G~cL)7t8KAoGtFXdqI$sqCmt%mA1w9#eWQ6|# XYhRhvxX$)N00000NkvXXu0mjf-Ye1b diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png deleted file mode 100644 index cc4f5cbfff2c3c96ad0a63df6c9a20631c04b5ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1642 zcmV-w29^1VP)=Kfb^KBn|WR$3dY?YOH_4hAF8(nusn zDW%q03*k79^5Xa4!;N1`DYe!j-`x>U*ux5ZDB_qRAN3FHC7_}5XTD3XpJFovm4BZS6jePmcI1OK* z^&QRY{k-^(o`6Z^09Jq0z}8$XO!TrvA5uAh)vraSG!Spg-?NN@*y2SFM=7P2a*kSQ z46ofRTsTLj0`bQ7FvDGH_!0R2G02K^ug#x?Y&!xT@u-jg^k9|oufSM8a)46)FKjjw zWEeop8N6?QOC)E&1Nb95sI!*|MphA4!pIpxvcPM6j8pDq{VgC{x@qmmcukI1Em%g5 zjQQ>OSe-Juv}cwX(D*2##;1PiTzpS&<&gMmU@5U1yxwX;8dw{jiE47VnU#Z-bqCa1 zZ3aLe@EoC(zUl!nh5Q{MqF^5FT!vMR%qVTIj-!Zw3Qc_+tjW=LksJXRITva_jS1ilYI6b*1d6t74q)X3 z9NHBzlJ6lq{_5bt#C7;*g4>(glrYz zeV?sNqH<;D@TPYn!K$POF?<3TY zoS}s+D8gk96WxcFGiW)0)-U(B()dVk)31UNP*fp@;KuS2GfNiDA6b|6ek2{s<7ai` zf5)~S0j==PCUgX#wsky2)_{<4rddYQ)-@vXxgKfk6b6WRpw=gyLvBjjpy%xo*&ay# zD*Be|w~irCH=2VDzX_DIjg+s^IajXy#$pX?gVLF_b!9Ny_qy^Bd0<7lJif! z3f9hV`Hnk{HBO$N{6|7lfVHkDJfqEa3dsOmH}bvIrF=j92tZVXgh!JV&4D;j6wFL* zRxm)1laMYn=HM9W04Uj@w&ny8c;hn0?xB7ubGNah&R}7v185$nB{egn)PlFJ2zDEH zgz-z6d=H=*dY9ozw#+|xp9a2{-Nr>vT4PqXQyn0>81JaqwJ!AC6^xCH-@31*H-T1; zAls`Fx(u#h?5OdzAd>HwIsg}JWvN%o83?R_9Y%~H947onib^Zt4I8lnf&J?{$Q1V(7`BIeH_0M0tB_ z2Ox~3b1$(Rp{}U2MV!RiOK2@mZ~($cI-g~HO-3f-qa~i!KCR(N4nSBz=OcmcJw_Q{ zdq)^{6F4H#83mpbP diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png deleted file mode 100644 index 1a1fe5d2d4a034e644e717e621cc315e92baa085..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1694 zcmV;P24VS$P)4r{O-HzUS&p5R;KO>M# z+Ns(`ZLk{?OfvwQ`TQ9P8p-JuXYD`c0y0zsSmRb!&&WCp{5ss*!)`2)MW_a_#v z8_F}>SjYRRgctPDckYCvwbom+Gr;>hwXh%~BX>3vr7yzhmp~)vY4AVk%d;2V?0E7_ z_x`5O0VMP3XPKHpVv7i(K|R)}a{3M|@>K)0_MHKAypjpHAtQGX6=$@R)jaEGH38Y%#$(fD-{x+~RKU1SM1Hv5MXI2*SIn|wvw0=dASXpR#Fd2Y@ z*9fi6bq^9#ctD*$#-(m#qotd#! z7&=cefFzVM0%r#F-hdmFJ4$l~?*_Cx1JC)I4tIx?jHLlU7vk+%gb6g~NH=$MjII^A z#j9{`1!Ze{s(@4nl{0|0FlPnyp1?Z}tU#0AK{|lQu^_d8J3&e+{#0v#Y=!SB!-D`( zk_QLXPg*0g56j_PNA#W|15byD9LtXUsRn>`i82E&7g3VV<8m}udTUPeoGBeoS|Fc^ zsM9~5P56HR3Dtoh$+c+TTN|Nct1GbNYb?O^wb;o5VpC~4VdShEyfMRREIq44&Sg+%*wEDJOVs+|A906QR4-2V#9$ghOd-5~Xh_MX;#R0hHFdw^L~JBxP~){h2nj5C0ARM7P` z8IsP@I+roP3{6`yrD(4P!YtMtO)sfX<6|7iepht?=`VvUMKz;VSL0crLB(lY%3w*3 zXLLSKT2qsW$WL_vm9S{7s*A|HKPWdEe3H)E8IKrUJKIrRwx|K#Z3H_rQ3uZiLDu=y z_!(#!8x4Zl^-G^KH2{Z4XHvv-dbO%wX&pp;FVifiSjU{Fs}}?r(^uC5r?-I-r+P-^ z^CLP$;W<~*7df*t;BqBhFJXYp2CUCfBSkaZlZnok^yTxdAVA?Ur&VAss{s(Dd3`{Y z)6UKeth1FGkFJZ-zNHhF7by(6j@I9iOs6^_&yxQMcov>WmXlCS|d{&BJ~r(0H<~V9t~=d zEoDwrZL3I&%F8eXcLsky6M$CLuYGfDusiQ9G0z6Oy z@=cr>kBIXvs;xNFXTaHHi!LAoFO#Iqsbl~P(mCzvs#WRMdoz%Z(XAPPBSWdtqRCs^ zg4B^{x_ZT!Br^l6U5q?03jisrHG^cSmabKud1%mTZCh2V*?a8fZwm(S44|F6vqfA6 z$eqQQNB@q>%j}~f2rjDukp7s@jnoKqEYq%O@*?kBEv@uk#uZw72?xOrMIZ{xh+04) zK_kuA)|zb_z26a;T7rzVLN&n3>a-e20VWwED~pa->B}iwT6<=_@G&qL0KKHxDPXJ& zlg1;mq_KhQ-lMj&Dt$}#bPa$gx^p0);})H2>`}4bXkbbm1fu(N4e-=Jps1LeMYMB3 z+R11TKt3oMk oNVkHS0El9jKv<2P0s#$zUs0JT=-M&wV*mgE07*qoM6N<$f;eRor2qf` diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png deleted file mode 100644 index a19da7382eadaf0bc885f76da8dec3899034528c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1605 zcmV-L2DVtUtjJl073;Y!fgJw#SBa zU#+#?dvC3^_IkY<|C>YV!@uKL-bVt;&q9(vEAubn%@>jksTm&;F*NTRky(Y+@}Gh? z8HU>4`;fuOd3axGd<85Ws^_{hB0=!O0N7c(PVk~19YVS$jsF+9b%+GPF9zVP6)hbUmI%4KoKdpTq^YZW62|a^E z zY0QwpUt*AtRs^;-7NH|fK=M88%K5vDe}bchY5>ppS|zSs%NB;34&HkO>~k8f(v3Ub z2XD0Y?FgegfXLWP#UF9XlZ|~6zP8cYy8zxXQ`kv;v^0X_Glq_&j*5EV>G)VTz0?rd zHCQzU9}`8?_|koA2l7{8GQh9S$%v22jBX7_jTUjjtjx$@Nmb0V~=x7SPTIyj_4c2i}Vok(vId%tZ0OzT&r)SPgJMX=Lw?-8J)G} zN#H64Ta%B038Om_4O+1n-Kj(ml5nnF$Qpe~4Ul009ET5LVI&PmGNig<6t^=vYf~qI z5rsy^q2qY_d=K6ZM5_F=y3g!%vcN;DD&sHN1yr0R%oczd`FPKoOJXZ)7c+xWBky1U zu6U(SRqs-Nwm-sogSExv_i+*V{h5@qB*LAx+ySalk)sh@9?~oRjXI!_t?$tS6UXgr z%N<~6Zk&O{1d;S$MZWZ%=F-|LjJ~V}&~oAm88gfvIj3d#=sj(brFoAq{t5=*s49pM zBQiNN5k2!ybk0cRTQvfgceU|*@3jX(lGo3`lNFhZ43IIhcD-uRNmjMok#@<;AYAl} zQ{W=uI4j3y^+RNwX@xN~9A5)y#_)`($(kAC>4!RIvDyi;Y4h?L0COrj1E;!GuojIw z2iI{%rycZ6^YE6>kIy}tPL>=5p%c&t!TU)D(F{ulDmHJd-2LsGF39Ut>f5|QY4LLF%(Uxrk zwWyTtY2#Mc3OFaumznUb3`2#Q`KoL>8RBV%Nf9e zRv%y__CEHneFj61>DlJTW=ZS5ikuyQ#!E-H^vCOy!VIKkMG@g@BT-uUG|3)f&p zxt0cIRGnp&6O5e+jHtZJeh={DfeKUL!&&`TL(8#O&WemU}6&X;p;tIr-dH#E) zmLuSU&B(IuXHv?Fg0WiOtsH Date: Tue, 14 Mar 2023 20:00:53 +0300 Subject: [PATCH 09/34] Move plugins to external folder --- .gitmodules | 2 +- applications/{plugins => external}/doom/README.md | 0 .../{plugins => external}/doom/application.fam | 0 .../{plugins => external}/doom/assets/door2.png | Bin .../{plugins => external}/doom/assets/door_inv.png | Bin .../{plugins => external}/doom/assets/fire_inv.png | Bin .../doom/assets/fireball_inv.png | Bin .../doom/assets/fireball_mask_inv.png | Bin .../doom/assets/gradient_inv.png | Bin .../{plugins => external}/doom/assets/gun_inv.png | Bin .../doom/assets/gun_mask_inv.png | Bin .../{plugins => external}/doom/assets/imp_inv.png | Bin .../doom/assets/imp_mask_inv.png | Bin .../{plugins => external}/doom/assets/item_inv.png | Bin .../doom/assets/item_mask_inv.png | Bin .../{plugins => external}/doom/assets/logo_inv.png | Bin .../doom/assets/screenshot-imp2.jpg | Bin .../doom/assets/screenshot-intro2.jpg | Bin .../doom/assets/screenshot-medkit2.jpg | Bin .../doom/assets/screenshot-start2.jpg | Bin .../doom/assets/screenshot1.png | Bin .../doom/assets/screenshot2.png | Bin .../doom/assets/screenshot3.png | Bin .../doom/compiled/assets_icons.c | 0 .../doom/compiled/assets_icons.h | 0 applications/{plugins => external}/doom/constants.h | 0 applications/{plugins => external}/doom/display.h | 0 applications/{plugins => external}/doom/doom.c | 0 .../{plugins => external}/doom/doom_10px.png | Bin .../doom/doom_music_player_worker.c | 0 .../doom/doom_music_player_worker.h | 0 applications/{plugins => external}/doom/entities.c | 0 applications/{plugins => external}/doom/entities.h | 0 applications/{plugins => external}/doom/level.h | 0 applications/{plugins => external}/doom/sound.h | 0 applications/{plugins => external}/doom/types.c | 0 applications/{plugins => external}/doom/types.h | 0 .../{plugins => external}/dtmf_dolphin/LICENSE | 0 .../{plugins => external}/dtmf_dolphin/README.md | 0 .../dtmf_dolphin/application.fam | 0 .../dtmf_dolphin/dtmf_dolphin.c | 0 .../dtmf_dolphin/dtmf_dolphin_audio.c | 0 .../dtmf_dolphin/dtmf_dolphin_audio.h | 0 .../dtmf_dolphin/dtmf_dolphin_data.c | 0 .../dtmf_dolphin/dtmf_dolphin_data.h | 0 .../dtmf_dolphin/dtmf_dolphin_event.h | 0 .../dtmf_dolphin/dtmf_dolphin_hal.c | 0 .../dtmf_dolphin/dtmf_dolphin_hal.h | 0 .../dtmf_dolphin/dtmf_dolphin_i.h | 0 .../{plugins => external}/dtmf_dolphin/phone.png | Bin .../dtmf_dolphin/pics/dialer.jpg | Bin .../dtmf_dolphin/scenes/dtmf_dolphin_scene.c | 0 .../dtmf_dolphin/scenes/dtmf_dolphin_scene.h | 0 .../dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h | 0 .../dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c | 0 .../dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c | 0 .../dtmf_dolphin/views/dtmf_dolphin_common.h | 0 .../dtmf_dolphin/views/dtmf_dolphin_dialer.c | 0 .../dtmf_dolphin/views/dtmf_dolphin_dialer.h | 0 .../FlipperZeroWiFiDeauthModuleDefines.h | 0 .../esp8266_deauth/application.fam | 0 .../esp8266_deauth/esp8266_deauth.c | 0 .../esp8266_deauth/wifi_10px.png | Bin .../flappy_bird/application.fam | 0 .../flappy_bird/assets/bird/frame_01.png | Bin .../flappy_bird/assets/bird/frame_02.png | Bin .../flappy_bird/assets/bird/frame_03.png | Bin .../flappy_bird/assets/bird/frame_rate | 0 .../flappy_bird/flappy_10px.png | Bin .../{plugins => external}/flappy_bird/flappy_bird.c | 0 .../{plugins => external}/flipfrid/LICENSE.md | 0 .../{plugins => external}/flipfrid/README.md | 0 .../{plugins => external}/flipfrid/application.fam | 0 .../{plugins => external}/flipfrid/flipfrid.c | 0 .../{plugins => external}/flipfrid/flipfrid.h | 0 .../flipfrid/images/125_10px.png | Bin .../{plugins => external}/flipfrid/rfid_10px.png | Bin .../flipfrid/scene/flipfrid_scene_entrypoint.c | 0 .../flipfrid/scene/flipfrid_scene_entrypoint.h | 0 .../scene/flipfrid_scene_load_custom_uids.c | 0 .../scene/flipfrid_scene_load_custom_uids.h | 0 .../flipfrid/scene/flipfrid_scene_load_file.c | 0 .../flipfrid/scene/flipfrid_scene_load_file.h | 0 .../flipfrid/scene/flipfrid_scene_run_attack.c | 0 .../flipfrid/scene/flipfrid_scene_run_attack.h | 0 .../flipfrid/scene/flipfrid_scene_select_field.c | 0 .../flipfrid/scene/flipfrid_scene_select_field.h | 0 .../flipper_i2ctools/README.md | 0 .../flipper_i2ctools/application.fam | 0 .../flipper_i2ctools/i2cscanner.c | 0 .../flipper_i2ctools/i2cscanner.h | 0 .../flipper_i2ctools/i2csender.c | 0 .../flipper_i2ctools/i2csender.h | 0 .../flipper_i2ctools/i2csniffer.c | 0 .../flipper_i2ctools/i2csniffer.h | 0 .../flipper_i2ctools/i2ctools.c | 0 .../flipper_i2ctools/i2ctools.gif | Bin .../flipper_i2ctools/i2ctools.png | Bin .../flipper_i2ctools/i2ctools_i.h | 0 .../flipper_i2ctools/images/ButtonDown_7x4.png | Bin .../flipper_i2ctools/images/ButtonLeft_4x7.png | Bin .../flipper_i2ctools/images/ButtonRight_4x7.png | Bin .../flipper_i2ctools/images/ButtonUp_7x4.png | Bin .../flipper_i2ctools/images/Ok_btn_9x9.png | Bin .../flipper_i2ctools/images/i2ctools_main_76x59.png | Bin .../flipper_i2ctools/views/main_view.c | 0 .../flipper_i2ctools/views/main_view.h | 0 .../flipper_i2ctools/views/scanner_view.c | 0 .../flipper_i2ctools/views/scanner_view.h | 0 .../flipper_i2ctools/views/sender_view.c | 0 .../flipper_i2ctools/views/sender_view.h | 0 .../flipper_i2ctools/views/sniffer_view.c | 0 .../flipper_i2ctools/views/sniffer_view.h | 0 applications/{plugins => external}/game15/README.md | 0 .../{plugins => external}/game15/application.fam | 0 applications/{plugins => external}/game15/game15.c | 0 .../{plugins => external}/game15/game15_10px.png | Bin .../{plugins => external}/game15/images/Game15.png | Bin .../game15/images/Game15Popup.png | Bin .../game15/images/Game15Restore.png | Bin applications/{plugins => external}/game15/sandbox.c | 0 applications/{plugins => external}/game15/sandbox.h | 0 .../{plugins => external}/game_2048/LICENSE | 0 .../{plugins => external}/game_2048/README.md | 0 .../{plugins => external}/game_2048/application.fam | 0 .../{plugins => external}/game_2048/array_utils.c | 0 .../{plugins => external}/game_2048/array_utils.h | 0 .../{plugins => external}/game_2048/digits.h | 0 .../{plugins => external}/game_2048/game_2048.c | 0 .../{plugins => external}/game_2048/game_2048.png | Bin .../game_2048/images/screenshot1.png | Bin .../game_2048/images/screenshot2.png | Bin .../{plugins => external}/gps_nmea_uart/LICENSE | 0 .../{plugins => external}/gps_nmea_uart/README.md | 0 .../gps_nmea_uart/application.fam | 0 .../{plugins => external}/gps_nmea_uart/gps.c | 0 .../gps_nmea_uart/gps_10px.png | Bin .../{plugins => external}/gps_nmea_uart/gps_uart.c | 0 .../{plugins => external}/gps_nmea_uart/gps_uart.h | 0 .../{plugins => external}/gps_nmea_uart/minmea.c | 0 .../{plugins => external}/gps_nmea_uart/minmea.h | 0 .../{plugins => external}/gps_nmea_uart/ui.png | Bin .../{plugins => external}/gps_nmea_uart/wiring.png | Bin .../{plugins => external}/hc_sr04/application.fam | 0 .../hc_sr04/dist_sensor10px.png | Bin .../{plugins => external}/hc_sr04/hc_sr04.c | 0 .../heap_defence_game/application.fam | 0 .../assets_images/Background_128x64.png | Bin .../heap_defence_game/assets_images/Box1_10x10.png | Bin .../heap_defence_game/assets_images/Box2_10x10.png | Bin .../heap_defence_game/assets_images/Box3_10x10.png | Bin .../heap_defence_game/assets_images/Box4_10x10.png | Bin .../heap_defence_game/assets_images/Box5_10x10.png | Bin .../heap_defence_game/assets_images/Box6p_10x10.png | Bin .../heap_defence_game/assets_images/Box7p_10x10.png | Bin .../heap_defence_game/assets_images/Box8p_10x10.png | Bin .../assets_images/Game_over_128x64.png | Bin .../assets_images/HD_game_over_128x64/frame_01.png | Bin .../assets_images/HD_game_over_128x64/frame_02.png | Bin .../assets_images/HD_game_over_128x64/frame_03.png | Bin .../assets_images/HD_game_over_128x64/frame_04.png | Bin .../assets_images/HD_game_over_128x64/frame_05.png | Bin .../assets_images/HD_game_over_128x64/frame_06.png | Bin .../assets_images/HD_game_over_128x64/frame_07.png | Bin .../assets_images/HD_game_over_128x64/frame_rate | 0 .../HD_person_block_left_10x20/frame_01.png | Bin .../HD_person_block_left_10x20/frame_02.png | Bin .../HD_person_block_left_10x20/frame_rate | 0 .../HD_person_block_right_10x20/frame_01.png | Bin .../HD_person_block_right_10x20/frame_02.png | Bin .../HD_person_block_right_10x20/frame_rate | 0 .../assets_images/HD_person_left_10x20/frame_01.png | Bin .../assets_images/HD_person_left_10x20/frame_02.png | Bin .../assets_images/HD_person_left_10x20/frame_03.png | Bin .../assets_images/HD_person_left_10x20/frame_04.png | Bin .../assets_images/HD_person_left_10x20/frame_rate | 0 .../HD_person_right_10x20/frame_01.png | Bin .../HD_person_right_10x20/frame_02.png | Bin .../HD_person_right_10x20/frame_03.png | Bin .../HD_person_right_10x20/frame_04.png | Bin .../assets_images/HD_person_right_10x20/frame_rate | 0 .../assets_images/HD_start_128x64/frame_01.png | Bin .../assets_images/HD_start_128x64/frame_02.png | Bin .../assets_images/HD_start_128x64/frame_03.png | Bin .../assets_images/HD_start_128x64/frame_04.png | Bin .../assets_images/HD_start_128x64/frame_rate | 0 .../assets_images/Person4_1_10x20.png | Bin .../assets_images/Person4_2_10x20.png | Bin .../assets_images/Person5_1_10x20.png | Bin .../assets_images/Person5_2_10x20.png | Bin .../assets_images/Start_128x64.png | Bin .../{plugins => external}/heap_defence_game/box.png | Bin .../heap_defence_game/heap_defence.c | 0 .../heap_defence_game/hede_assets.c | 0 .../heap_defence_game/hede_assets.h | 0 .../{plugins => external}/hex_viewer/LICENSE | 0 .../hex_viewer/application.fam | 0 .../{plugins => external}/hex_viewer/hex_viewer.c | 0 .../hex_viewer/icons/hex_10px.png | Bin .../{plugins => external}/ibtn_fuzzer/LICENSE.md | 0 .../ibtn_fuzzer/application.fam | 0 .../{plugins => external}/ibtn_fuzzer/ibtnfuzzer.c | 0 .../{plugins => external}/ibtn_fuzzer/ibtnfuzzer.h | 0 .../ibtn_fuzzer/ibutt_10px.png | Bin .../ibtn_fuzzer/images/ibutt_10px.png | Bin .../ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c | 0 .../ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h | 0 .../scene/ibtnfuzzer_scene_load_custom_uids.c | 0 .../scene/ibtnfuzzer_scene_load_custom_uids.h | 0 .../ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c | 0 .../ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h | 0 .../ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c | 0 .../ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h | 0 .../scene/ibtnfuzzer_scene_select_field.c | 0 .../scene/ibtnfuzzer_scene_select_field.h | 0 .../{plugins => external}/lightmeter/LICENSE | 0 .../{plugins => external}/lightmeter/README.md | 0 .../lightmeter/application.fam | 0 .../lightmeter/gui/scenes/config/lightmeter_scene.c | 0 .../lightmeter/gui/scenes/config/lightmeter_scene.h | 0 .../gui/scenes/config/lightmeter_scene_config.h | 0 .../lightmeter/gui/scenes/lightmeter_scene_about.c | 0 .../lightmeter/gui/scenes/lightmeter_scene_config.c | 0 .../lightmeter/gui/scenes/lightmeter_scene_help.c | 0 .../lightmeter/gui/scenes/lightmeter_scene_main.c | 0 .../lightmeter/gui/views/main_view.c | 0 .../lightmeter/gui/views/main_view.h | 0 .../lightmeter/icons/T_10x14.png | Bin .../lightmeter/icons/f_10x14.png | Bin .../lightmeter/lib/BH1750/BH1750.c | 0 .../lightmeter/lib/BH1750/BH1750.h | 0 .../lightmeter/lib/BH1750/LICENSE | 0 .../lightmeter/lib/BH1750/README.md | 0 .../lightmeter/lib/BH1750/docs/BH1750.pdf | Bin .../{plugins => external}/lightmeter/lightmeter.c | 0 .../{plugins => external}/lightmeter/lightmeter.h | 0 .../{plugins => external}/lightmeter/lightmeter.png | Bin .../lightmeter/lightmeter_config.h | 0 .../lightmeter/lightmeter_helper.c | 0 .../lightmeter/lightmeter_helper.h | 0 .../{plugins => external}/metronome/README.md | 0 .../{plugins => external}/metronome/application.fam | 0 .../metronome/gui_extensions.c | 0 .../metronome/gui_extensions.h | 0 .../metronome/images/ButtonUp_7x4.png | Bin .../metronome/img/screenshot.png | Bin .../metronome/img/wave_left_4x14.png | Bin .../metronome/img/wave_right_4x14.png | Bin .../{plugins => external}/metronome/metronome.c | 0 .../metronome/metronome_icon.png | Bin .../{plugins => external}/minesweeper/LICENSE | 0 .../{plugins => external}/minesweeper/README.md | 0 .../minesweeper/application.fam | 0 .../{plugins => external}/minesweeper/assets.h | 0 .../{plugins => external}/minesweeper/assets/asset | 0 .../minesweeper/assets/mockup.png | Bin .../minesweeper/assets/tile_0.png | Bin .../minesweeper/assets/tile_0.xbm | 0 .../minesweeper/assets/tile_1.png | Bin .../minesweeper/assets/tile_1.xbm | 0 .../minesweeper/assets/tile_2.png | Bin .../minesweeper/assets/tile_2.xbm | 0 .../minesweeper/assets/tile_3.png | Bin .../minesweeper/assets/tile_3.xbm | 0 .../minesweeper/assets/tile_4.png | Bin .../minesweeper/assets/tile_4.xbm | 0 .../minesweeper/assets/tile_5.png | Bin .../minesweeper/assets/tile_5.xbm | 0 .../minesweeper/assets/tile_6.png | Bin .../minesweeper/assets/tile_6.xbm | 0 .../minesweeper/assets/tile_7.png | Bin .../minesweeper/assets/tile_7.xbm | 0 .../minesweeper/assets/tile_8.png | Bin .../minesweeper/assets/tile_8.xbm | 0 .../minesweeper/assets/tile_empty.png | Bin .../minesweeper/assets/tile_flag.png | Bin .../minesweeper/assets/tile_flag.xbm | 0 .../minesweeper/assets/tile_mine.png | Bin .../minesweeper/assets/tile_mine.xbm | 0 .../minesweeper/assets/tile_uncleared.png | Bin .../minesweeper/assets/tile_uncleared.xbm | 0 .../minesweeper/img/screenshot.png | Bin .../{plugins => external}/minesweeper/minesweeper.c | 0 .../minesweeper/minesweeper_icon.png | Bin .../morse_code/application.fam | 0 .../{plugins => external}/morse_code/morse_code.c | 0 .../morse_code/morse_code_10px.png | Bin .../morse_code/morse_code_worker.c | 0 .../morse_code/morse_code_worker.h | 0 .../mousejacker/application.fam | 0 .../mousejacker/images/badusb_10px.png | Bin .../mousejacker/images/sub1_10px.png | Bin .../mousejacker/lib/nrf24/nrf24.c | 0 .../mousejacker/lib/nrf24/nrf24.h | 0 .../mousejacker/mouse_10px.png | Bin .../{plugins => external}/mousejacker/mousejacker.c | 0 .../mousejacker/mousejacker_ducky.c | 0 .../mousejacker/mousejacker_ducky.h | 0 .../multi_converter/application.fam | 0 .../multi_converter/converter_10px.png | Bin .../multi_converter/multi_converter.c | 0 .../multi_converter/multi_converter_definitions.h | 0 .../multi_converter/multi_converter_mode_display.c | 0 .../multi_converter/multi_converter_mode_display.h | 0 .../multi_converter/multi_converter_mode_select.c | 0 .../multi_converter/multi_converter_mode_select.h | 0 .../multi_converter/multi_converter_units.c | 0 .../multi_converter/multi_converter_units.h | 0 .../{plugins => external}/nrfsniff/application.fam | 0 .../nrfsniff/lib/nrf24/nrf24.c | 0 .../nrfsniff/lib/nrf24/nrf24.h | 0 .../{plugins => external}/nrfsniff/nrfsniff.c | 0 .../nrfsniff/nrfsniff_10px.png | Bin .../{plugins => external}/playlist/application.fam | 0 .../{plugins => external}/playlist/canvas_helper.c | 0 .../{plugins => external}/playlist/canvas_helper.h | 0 .../playlist/images/ButtonRight_4x7.png | Bin .../playlist/images/sub1_10px.png | Bin .../{plugins => external}/playlist/playlist.c | 0 .../playlist/playlist_10px.png | Bin .../{plugins => external}/playlist/playlist_file.c | 0 .../{plugins => external}/playlist/playlist_file.h | 0 .../pocsag_pager/application.fam | 0 .../pocsag_pager/helpers/pocsag_pager_event.h | 0 .../pocsag_pager/helpers/pocsag_pager_types.h | 0 .../pocsag_pager/images/Lock_7x8.png | Bin .../pocsag_pager/images/Message_8x7.png | Bin .../pocsag_pager/images/Pin_back_arrow_10x8.png | Bin .../pocsag_pager/images/Quest_7x8.png | Bin .../pocsag_pager/images/Scanning_123x52.png | Bin .../pocsag_pager/images/Unlock_7x8.png | Bin .../pocsag_pager/images/WarningDolphin_45x42.png | Bin .../pocsag_pager/pocsag_pager_10px.png | Bin .../pocsag_pager/pocsag_pager_app.c | 0 .../pocsag_pager/pocsag_pager_app_i.c | 0 .../pocsag_pager/pocsag_pager_app_i.h | 0 .../pocsag_pager/pocsag_pager_history.c | 0 .../pocsag_pager/pocsag_pager_history.h | 0 .../pocsag_pager/protocols/pcsg_generic.c | 0 .../pocsag_pager/protocols/pcsg_generic.h | 0 .../pocsag_pager/protocols/pocsag.c | 0 .../pocsag_pager/protocols/pocsag.h | 0 .../pocsag_pager/protocols/protocol_items.c | 0 .../pocsag_pager/protocols/protocol_items.h | 0 .../pocsag_pager/scenes/pocsag_pager_receiver.c | 0 .../pocsag_pager/scenes/pocsag_pager_scene.c | 0 .../pocsag_pager/scenes/pocsag_pager_scene.h | 0 .../pocsag_pager/scenes/pocsag_pager_scene_about.c | 0 .../pocsag_pager/scenes/pocsag_pager_scene_config.h | 0 .../scenes/pocsag_pager_scene_receiver_config.c | 0 .../scenes/pocsag_pager_scene_receiver_info.c | 0 .../pocsag_pager/scenes/pocsag_pager_scene_start.c | 0 .../pocsag_pager/views/pocsag_pager_receiver.c | 0 .../pocsag_pager/views/pocsag_pager_receiver.h | 0 .../pocsag_pager/views/pocsag_pager_receiver_info.c | 0 .../pocsag_pager/views/pocsag_pager_receiver_info.h | 0 .../{plugins => external}/protoview/LICENSE | 0 .../{plugins => external}/protoview/README.md | 0 applications/{plugins => external}/protoview/app.c | 0 applications/{plugins => external}/protoview/app.h | 0 .../{plugins => external}/protoview/app_subghz.c | 0 .../{plugins => external}/protoview/appicon.png | Bin .../{plugins => external}/protoview/application.fam | 0 applications/{plugins => external}/protoview/crc.c | 0 .../protoview/custom_presets.h | 0 .../{plugins => external}/protoview/fields.c | 0 .../protoview/images/ProtoViewSignal.jpg | Bin .../protoview/images/protoview_1.jpg | Bin .../protoview/images/protoview_2.jpg | Bin .../protoview/protocols/b4b1.c | 0 .../protoview/protocols/keeloq.c | 0 .../protoview/protocols/oregon2.c | 0 .../protoview/protocols/pvchat.c | 0 .../protoview/protocols/tpms/citroen.c | 0 .../protoview/protocols/tpms/ford.c | 0 .../protoview/protocols/tpms/renault.c | 0 .../protoview/protocols/tpms/schrader.c | 0 .../protoview/protocols/tpms/schrader_eg53ma4.c | 0 .../protoview/protocols/tpms/toyota.c | 0 .../protoview/protocols/unknown.c | 0 .../{plugins => external}/protoview/raw_samples.c | 0 .../{plugins => external}/protoview/raw_samples.h | 0 .../{plugins => external}/protoview/signal.c | 0 .../{plugins => external}/protoview/signal_file.c | 0 applications/{plugins => external}/protoview/ui.c | 0 .../{plugins => external}/protoview/view_build.c | 0 .../protoview/view_direct_sampling.c | 0 .../{plugins => external}/protoview/view_info.c | 0 .../protoview/view_raw_signal.c | 0 .../{plugins => external}/protoview/view_settings.c | 0 .../sentry_safe/application.fam | 0 .../{plugins => external}/sentry_safe/safe_10px.png | Bin .../{plugins => external}/sentry_safe/sentry_safe.c | 0 .../{plugins => external}/solitaire/application.fam | 0 .../solitaire/assets/card_graphics.png | Bin .../solitaire/assets/solitaire_main.png | Bin .../{plugins => external}/solitaire/common/card.c | 0 .../{plugins => external}/solitaire/common/card.h | 0 .../{plugins => external}/solitaire/common/dml.c | 0 .../{plugins => external}/solitaire/common/dml.h | 0 .../{plugins => external}/solitaire/common/menu.c | 0 .../{plugins => external}/solitaire/common/menu.h | 0 .../{plugins => external}/solitaire/common/queue.c | 0 .../{plugins => external}/solitaire/common/queue.h | 0 .../{plugins => external}/solitaire/common/ui.c | 0 .../{plugins => external}/solitaire/common/ui.h | 0 .../{plugins => external}/solitaire/defines.h | 0 .../{plugins => external}/solitaire/solitaire.c | 0 .../solitaire/solitaire_10px.png | Bin .../spectrum_analyzer/application.fam | 0 .../spectrum_analyzer/spectrum_10px.png | Bin .../spectrum_analyzer/spectrum_analyzer.c | 0 .../spectrum_analyzer/spectrum_analyzer.h | 0 .../spectrum_analyzer/spectrum_analyzer_worker.c | 0 .../spectrum_analyzer/spectrum_analyzer_worker.h | 0 applications/{plugins => external}/subbrute | 0 .../{plugins => external}/swd_probe/.gitignore | 0 .../{plugins => external}/swd_probe/LICENSE.txt | 0 .../{plugins => external}/swd_probe/README.md | 0 applications/{plugins => external}/swd_probe/adi.c | 0 applications/{plugins => external}/swd_probe/adi.h | 0 .../{plugins => external}/swd_probe/application.fam | 0 .../swd_probe/icons/ButtonDown_7x4.png | Bin .../swd_probe/icons/ButtonUp_7x4.png | Bin .../{plugins => external}/swd_probe/icons/app.png | Bin .../{plugins => external}/swd_probe/icons/swd.png | Bin .../{plugins => external}/swd_probe/jep106.c | 0 .../{plugins => external}/swd_probe/jep106.h | 0 .../{plugins => external}/swd_probe/jep106.inc | 0 .../{plugins => external}/swd_probe/model/chip.ply | 0 .../swd_probe/model/convert.py | 0 .../swd_probe/model/model_chip.h | 0 .../{plugins => external}/swd_probe/swd_probe_app.c | 0 .../{plugins => external}/swd_probe/swd_probe_app.h | 0 .../{plugins => external}/swd_probe/usb_uart.c | 0 .../{plugins => external}/swd_probe/usb_uart.h | 0 .../tetris_game/application.fam | 0 .../tetris_game/tetris_10px.png | Bin .../{plugins => external}/tetris_game/tetris_game.c | 0 .../{plugins => external}/text_viewer/LICENSE | 0 .../{plugins => external}/text_viewer/README.md | 0 .../text_viewer/application.fam | 0 .../text_viewer/icons/text_10px.png | Bin .../{plugins => external}/text_viewer/text_viewer.c | 0 .../text_viewer/textviewerflipper.PNG | Bin .../tictactoe_game/application.fam | 0 .../tictactoe_game/tictactoe_10px.png | Bin .../tictactoe_game/tictactoe_game.c | 0 applications/{plugins => external}/totp/LICENSE | 0 .../{plugins => external}/totp/application.fam | 0 applications/{plugins => external}/totp/cli/cli.c | 0 applications/{plugins => external}/totp/cli/cli.h | 0 .../{plugins => external}/totp/cli/cli_helpers.c | 0 .../{plugins => external}/totp/cli/cli_helpers.h | 0 .../totp/cli/commands/add/add.c | 0 .../totp/cli/commands/add/add.h | 0 .../totp/cli/commands/delete/delete.c | 0 .../totp/cli/commands/delete/delete.h | 0 .../totp/cli/commands/help/help.c | 0 .../totp/cli/commands/help/help.h | 0 .../totp/cli/commands/list/list.c | 0 .../totp/cli/commands/list/list.h | 0 .../totp/cli/commands/move/move.c | 0 .../totp/cli/commands/move/move.h | 0 .../totp/cli/commands/notification/notification.c | 0 .../totp/cli/commands/notification/notification.h | 0 .../totp/cli/commands/pin/pin.c | 0 .../totp/cli/commands/pin/pin.h | 0 .../totp/cli/commands/reset/reset.c | 0 .../totp/cli/commands/reset/reset.h | 0 .../totp/cli/commands/timezone/timezone.c | 0 .../totp/cli/commands/timezone/timezone.h | 0 .../totp/images/DolphinCommon_56x48.png | Bin .../totp/images/totp_arrow_bottom_10x5.png | Bin .../totp/images/totp_arrow_left_8x9.png | Bin .../totp/images/totp_arrow_right_8x9.png | Bin .../{plugins => external}/totp/lib/base32/base32.c | 0 .../{plugins => external}/totp/lib/base32/base32.h | 0 .../{plugins => external}/totp/lib/list/list.c | 0 .../{plugins => external}/totp/lib/list/list.h | 0 .../totp/lib/polyfills/memset_s.c | 0 .../totp/lib/polyfills/memset_s.h | 0 .../totp/lib/polyfills/strnlen.c | 0 .../totp/lib/polyfills/strnlen.h | 0 .../totp/lib/roll_value/roll_value.c | 0 .../totp/lib/roll_value/roll_value.h | 0 .../totp/lib/timezone_utils/timezone_utils.c | 0 .../totp/lib/timezone_utils/timezone_utils.h | 0 .../totp/services/config/config.c | 0 .../totp/services/config/config.h | 0 .../totp/services/config/constants.h | 0 .../config/migrations/config_migration_v1_to_v2.c | 0 .../config/migrations/config_migration_v1_to_v2.h | 0 .../config/migrations/config_migration_v2_to_v3.c | 0 .../config/migrations/config_migration_v2_to_v3.h | 0 .../totp/services/convert/convert.h | 0 .../totp/services/crypto/crypto.c | 0 .../totp/services/crypto/crypto.h | 0 .../totp/services/hmac/byteswap.c | 0 .../totp/services/hmac/byteswap.h | 0 .../totp/services/hmac/hmac_common.h | 0 .../totp/services/hmac/hmac_sha1.c | 0 .../totp/services/hmac/hmac_sha1.h | 0 .../totp/services/hmac/hmac_sha256.c | 0 .../totp/services/hmac/hmac_sha256.h | 0 .../totp/services/hmac/hmac_sha512.c | 0 .../totp/services/hmac/hmac_sha512.h | 0 .../totp/services/hmac/memxor.c | 0 .../totp/services/hmac/memxor.h | 0 .../{plugins => external}/totp/services/hmac/sha1.c | 0 .../{plugins => external}/totp/services/hmac/sha1.h | 0 .../totp/services/hmac/sha256.c | 0 .../totp/services/hmac/sha256.h | 0 .../totp/services/hmac/sha512.c | 0 .../totp/services/hmac/sha512.h | 0 .../{plugins => external}/totp/services/hmac/u64.h | 0 .../{plugins => external}/totp/services/totp/totp.c | 0 .../{plugins => external}/totp/services/totp/totp.h | 0 .../{plugins => external}/totp/totp_10px.png | Bin applications/{plugins => external}/totp/totp_app.c | 0 .../{plugins => external}/totp/types/common.h | 0 .../{plugins => external}/totp/types/event_type.h | 0 .../totp/types/notification_method.h | 0 .../{plugins => external}/totp/types/nullable.h | 0 .../{plugins => external}/totp/types/plugin_event.h | 0 .../{plugins => external}/totp/types/plugin_state.h | 0 .../{plugins => external}/totp/types/token_info.c | 0 .../{plugins => external}/totp/types/token_info.h | 0 .../totp/types/user_pin_codes.h | 0 .../{plugins => external}/totp/ui/common_dialogs.c | 0 .../{plugins => external}/totp/ui/common_dialogs.h | 0 .../{plugins => external}/totp/ui/constants.h | 0 .../{plugins => external}/totp/ui/scene_director.c | 0 .../{plugins => external}/totp/ui/scene_director.h | 0 .../totp/ui/scenes/add_new_token/totp_input_text.c | 0 .../totp/ui/scenes/add_new_token/totp_input_text.h | 0 .../scenes/add_new_token/totp_scene_add_new_token.c | 0 .../scenes/add_new_token/totp_scene_add_new_token.h | 0 .../totp/ui/scenes/app_settings/totp_app_settings.c | 0 .../totp/ui/scenes/app_settings/totp_app_settings.h | 0 .../scenes/authenticate/totp_scene_authenticate.c | 0 .../scenes/authenticate/totp_scene_authenticate.h | 0 .../generate_token/totp_scene_generate_token.c | 0 .../generate_token/totp_scene_generate_token.h | 0 .../ui/scenes/token_menu/totp_scene_token_menu.c | 0 .../ui/scenes/token_menu/totp_scene_token_menu.h | 0 .../totp/ui/totp_scenes_enum.h | 0 .../{plugins => external}/totp/ui/ui_controls.c | 0 .../{plugins => external}/totp/ui/ui_controls.h | 0 .../totp/workers/type_code/type_code.c | 0 .../totp/workers/type_code/type_code.h | 0 .../{plugins => external}/uart_terminal/LICENSE | 0 .../{plugins => external}/uart_terminal/README.md | 0 .../uart_terminal/application.fam | 0 .../assets/KeyBackspaceSelected_16x9.png | Bin .../uart_terminal/assets/KeyBackspace_16x9.png | Bin .../uart_terminal/assets/KeySaveSelected_24x11.png | Bin .../uart_terminal/assets/KeySave_24x11.png | Bin .../uart_terminal/assets/WarningDolphin_45x42.png | Bin .../uart_terminal/scenes/uart_terminal_scene.c | 0 .../uart_terminal/scenes/uart_terminal_scene.h | 0 .../scenes/uart_terminal_scene_config.h | 0 .../scenes/uart_terminal_scene_console_output.c | 0 .../scenes/uart_terminal_scene_start.c | 0 .../scenes/uart_terminal_scene_text_input.c | 0 .../uart_terminal/uart_terminal.png | Bin .../uart_terminal/uart_terminal_app.c | 0 .../uart_terminal/uart_terminal_app.h | 0 .../uart_terminal/uart_terminal_app_i.h | 0 .../uart_terminal/uart_terminal_custom_event.h | 0 .../uart_terminal/uart_terminal_uart.c | 0 .../uart_terminal/uart_terminal_uart.h | 0 .../uart_terminal/uart_text_input.c | 0 .../uart_terminal/uart_text_input.h | 0 .../uart_terminal/uart_validators.c | 0 .../uart_terminal/uart_validators.h | 0 .../{plugins => external}/unitemp/LICENSE.md | 0 .../{plugins => external}/unitemp/README.md | 0 .../{plugins => external}/unitemp/Sensors.c | 0 .../{plugins => external}/unitemp/Sensors.h | 0 .../{plugins => external}/unitemp/application.fam | 0 .../{plugins => external}/unitemp/assets/README.MD | 0 .../unitemp/assets/flipper_happy_2_60x38.png | Bin .../unitemp/assets/flipper_happy_60x38.png | Bin .../unitemp/assets/flipper_sad_60x38.png | Bin .../unitemp/assets/hum_9x15.png | Bin .../unitemp/assets/in_hg_15x15.png | Bin .../unitemp/assets/mm_hg_15x15.png | Bin .../unitemp/assets/pressure_7x13.png | Bin .../unitemp/assets/repo_qr_50x50.png | Bin .../unitemp/assets/sherlok_53x45.png | Bin .../unitemp/assets/temp_C_11x14.png | Bin .../unitemp/assets/temp_F_11x14.png | Bin applications/{plugins => external}/unitemp/icon.png | Bin .../unitemp/interfaces/I2CSensor.c | 0 .../unitemp/interfaces/I2CSensor.h | 0 .../unitemp/interfaces/OneWireSensor.c | 0 .../unitemp/interfaces/OneWireSensor.h | 0 .../unitemp/interfaces/SPISensor.c | 0 .../unitemp/interfaces/SPISensor.h | 0 .../unitemp/interfaces/SingleWireSensor.c | 0 .../unitemp/interfaces/SingleWireSensor.h | 0 .../{plugins => external}/unitemp/sensors/AM2320.c | 0 .../{plugins => external}/unitemp/sensors/AM2320.h | 0 .../{plugins => external}/unitemp/sensors/BME680.c | 0 .../{plugins => external}/unitemp/sensors/BME680.h | 0 .../{plugins => external}/unitemp/sensors/BMP180.c | 0 .../{plugins => external}/unitemp/sensors/BMP180.h | 0 .../{plugins => external}/unitemp/sensors/BMx280.c | 0 .../{plugins => external}/unitemp/sensors/BMx280.h | 0 .../{plugins => external}/unitemp/sensors/DHT20.c | 0 .../{plugins => external}/unitemp/sensors/DHT20.h | 0 .../{plugins => external}/unitemp/sensors/HDC1080.c | 0 .../{plugins => external}/unitemp/sensors/HDC1080.h | 0 .../{plugins => external}/unitemp/sensors/HTU21x.c | 0 .../{plugins => external}/unitemp/sensors/HTU21x.h | 0 .../{plugins => external}/unitemp/sensors/LM75.c | 0 .../{plugins => external}/unitemp/sensors/LM75.h | 0 .../unitemp/sensors/MAX31855.c | 0 .../unitemp/sensors/MAX31855.h | 0 .../{plugins => external}/unitemp/sensors/MAX6675.c | 0 .../{plugins => external}/unitemp/sensors/MAX6675.h | 0 .../{plugins => external}/unitemp/sensors/SHT30.c | 0 .../{plugins => external}/unitemp/sensors/SHT30.h | 0 .../unitemp/sensors/Sensors.xlsx | Bin .../{plugins => external}/unitemp/unitemp.c | 0 .../{plugins => external}/unitemp/unitemp.h | 0 .../unitemp/views/General_view.c | 0 .../unitemp/views/MainMenu_view.c | 0 .../unitemp/views/Popup_view.c | 0 .../unitemp/views/SensorActions_view.c | 0 .../unitemp/views/SensorEdit_view.c | 0 .../unitemp/views/SensorNameEdit_view.c | 0 .../unitemp/views/SensorsList_view.c | 0 .../unitemp/views/Settings_view.c | 0 .../unitemp/views/UnitempViews.h | 0 .../unitemp/views/Widgets_view.c | 0 .../{plugins => external}/wav_player/README.md | 0 .../wav_player/application.fam | 0 .../wav_player/images/music_10px.png | Bin .../{plugins => external}/wav_player/wav_10px.png | Bin .../{plugins => external}/wav_player/wav_parser.c | 0 .../{plugins => external}/wav_player/wav_parser.h | 0 .../{plugins => external}/wav_player/wav_player.c | 0 .../wav_player/wav_player_hal.c | 0 .../wav_player/wav_player_hal.h | 0 .../wav_player/wav_player_view.c | 0 .../wav_player/wav_player_view.h | 0 .../wifi_marauder_companion/application.fam | 0 .../scenes/wifi_marauder_scene.c | 0 .../scenes/wifi_marauder_scene.h | 0 .../scenes/wifi_marauder_scene_config.h | 0 .../scenes/wifi_marauder_scene_console_output.c | 0 .../scenes/wifi_marauder_scene_start.c | 0 .../scenes/wifi_marauder_scene_text_input.c | 0 .../wifi_marauder_companion/wifi_10px.png | Bin .../wifi_marauder_companion/wifi_marauder_app.c | 0 .../wifi_marauder_companion/wifi_marauder_app.h | 0 .../wifi_marauder_companion/wifi_marauder_app_i.h | 0 .../wifi_marauder_custom_event.h | 0 .../wifi_marauder_companion/wifi_marauder_uart.c | 0 .../wifi_marauder_companion/wifi_marauder_uart.h | 0 .../wifi_scanner/FlipperZeroWiFiModuleDefines.h | 0 .../wifi_scanner/application.fam | 0 .../wifi_scanner/wifi_10px.png | Bin .../wifi_scanner/wifi_scanner.c | 0 .../{plugins => external}/zombiez/application.fam | 0 .../{plugins => external}/zombiez/zombie_10px.png | Bin .../{plugins => external}/zombiez/zombiez.c | 0 .../{plugins => external}/zombiez/zombiez.h | 0 applications/plugins/dap_link/lib/free-dap | 1 - 671 files changed, 1 insertion(+), 2 deletions(-) rename applications/{plugins => external}/doom/README.md (100%) rename applications/{plugins => external}/doom/application.fam (100%) rename applications/{plugins => external}/doom/assets/door2.png (100%) rename applications/{plugins => external}/doom/assets/door_inv.png (100%) rename applications/{plugins => external}/doom/assets/fire_inv.png (100%) rename applications/{plugins => external}/doom/assets/fireball_inv.png (100%) rename applications/{plugins => external}/doom/assets/fireball_mask_inv.png (100%) rename applications/{plugins => external}/doom/assets/gradient_inv.png (100%) rename applications/{plugins => external}/doom/assets/gun_inv.png (100%) rename applications/{plugins => external}/doom/assets/gun_mask_inv.png (100%) rename applications/{plugins => external}/doom/assets/imp_inv.png (100%) rename applications/{plugins => external}/doom/assets/imp_mask_inv.png (100%) rename applications/{plugins => external}/doom/assets/item_inv.png (100%) rename applications/{plugins => external}/doom/assets/item_mask_inv.png (100%) rename applications/{plugins => external}/doom/assets/logo_inv.png (100%) rename applications/{plugins => external}/doom/assets/screenshot-imp2.jpg (100%) rename applications/{plugins => external}/doom/assets/screenshot-intro2.jpg (100%) rename applications/{plugins => external}/doom/assets/screenshot-medkit2.jpg (100%) rename applications/{plugins => external}/doom/assets/screenshot-start2.jpg (100%) rename applications/{plugins => external}/doom/assets/screenshot1.png (100%) rename applications/{plugins => external}/doom/assets/screenshot2.png (100%) rename applications/{plugins => external}/doom/assets/screenshot3.png (100%) rename applications/{plugins => external}/doom/compiled/assets_icons.c (100%) rename applications/{plugins => external}/doom/compiled/assets_icons.h (100%) rename applications/{plugins => external}/doom/constants.h (100%) rename applications/{plugins => external}/doom/display.h (100%) rename applications/{plugins => external}/doom/doom.c (100%) rename applications/{plugins => external}/doom/doom_10px.png (100%) rename applications/{plugins => external}/doom/doom_music_player_worker.c (100%) rename applications/{plugins => external}/doom/doom_music_player_worker.h (100%) rename applications/{plugins => external}/doom/entities.c (100%) rename applications/{plugins => external}/doom/entities.h (100%) rename applications/{plugins => external}/doom/level.h (100%) rename applications/{plugins => external}/doom/sound.h (100%) rename applications/{plugins => external}/doom/types.c (100%) rename applications/{plugins => external}/doom/types.h (100%) rename applications/{plugins => external}/dtmf_dolphin/LICENSE (100%) rename applications/{plugins => external}/dtmf_dolphin/README.md (100%) rename applications/{plugins => external}/dtmf_dolphin/application.fam (100%) rename applications/{plugins => external}/dtmf_dolphin/dtmf_dolphin.c (100%) rename applications/{plugins => external}/dtmf_dolphin/dtmf_dolphin_audio.c (100%) rename applications/{plugins => external}/dtmf_dolphin/dtmf_dolphin_audio.h (100%) rename applications/{plugins => external}/dtmf_dolphin/dtmf_dolphin_data.c (100%) rename applications/{plugins => external}/dtmf_dolphin/dtmf_dolphin_data.h (100%) rename applications/{plugins => external}/dtmf_dolphin/dtmf_dolphin_event.h (100%) rename applications/{plugins => external}/dtmf_dolphin/dtmf_dolphin_hal.c (100%) rename applications/{plugins => external}/dtmf_dolphin/dtmf_dolphin_hal.h (100%) rename applications/{plugins => external}/dtmf_dolphin/dtmf_dolphin_i.h (100%) rename applications/{plugins => external}/dtmf_dolphin/phone.png (100%) rename applications/{plugins => external}/dtmf_dolphin/pics/dialer.jpg (100%) rename applications/{plugins => external}/dtmf_dolphin/scenes/dtmf_dolphin_scene.c (100%) rename applications/{plugins => external}/dtmf_dolphin/scenes/dtmf_dolphin_scene.h (100%) rename applications/{plugins => external}/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h (100%) rename applications/{plugins => external}/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c (100%) rename applications/{plugins => external}/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c (100%) rename applications/{plugins => external}/dtmf_dolphin/views/dtmf_dolphin_common.h (100%) rename applications/{plugins => external}/dtmf_dolphin/views/dtmf_dolphin_dialer.c (100%) rename applications/{plugins => external}/dtmf_dolphin/views/dtmf_dolphin_dialer.h (100%) rename applications/{plugins => external}/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h (100%) rename applications/{plugins => external}/esp8266_deauth/application.fam (100%) rename applications/{plugins => external}/esp8266_deauth/esp8266_deauth.c (100%) rename applications/{plugins => external}/esp8266_deauth/wifi_10px.png (100%) rename applications/{plugins => external}/flappy_bird/application.fam (100%) rename applications/{plugins => external}/flappy_bird/assets/bird/frame_01.png (100%) rename applications/{plugins => external}/flappy_bird/assets/bird/frame_02.png (100%) rename applications/{plugins => external}/flappy_bird/assets/bird/frame_03.png (100%) rename applications/{plugins => external}/flappy_bird/assets/bird/frame_rate (100%) rename applications/{plugins => external}/flappy_bird/flappy_10px.png (100%) rename applications/{plugins => external}/flappy_bird/flappy_bird.c (100%) rename applications/{plugins => external}/flipfrid/LICENSE.md (100%) rename applications/{plugins => external}/flipfrid/README.md (100%) rename applications/{plugins => external}/flipfrid/application.fam (100%) rename applications/{plugins => external}/flipfrid/flipfrid.c (100%) rename applications/{plugins => external}/flipfrid/flipfrid.h (100%) rename applications/{plugins => external}/flipfrid/images/125_10px.png (100%) rename applications/{plugins => external}/flipfrid/rfid_10px.png (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_entrypoint.c (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_entrypoint.h (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_load_custom_uids.c (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_load_custom_uids.h (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_load_file.c (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_load_file.h (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_run_attack.c (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_run_attack.h (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_select_field.c (100%) rename applications/{plugins => external}/flipfrid/scene/flipfrid_scene_select_field.h (100%) rename applications/{plugins => external}/flipper_i2ctools/README.md (100%) rename applications/{plugins => external}/flipper_i2ctools/application.fam (100%) rename applications/{plugins => external}/flipper_i2ctools/i2cscanner.c (100%) rename applications/{plugins => external}/flipper_i2ctools/i2cscanner.h (100%) rename applications/{plugins => external}/flipper_i2ctools/i2csender.c (100%) rename applications/{plugins => external}/flipper_i2ctools/i2csender.h (100%) rename applications/{plugins => external}/flipper_i2ctools/i2csniffer.c (100%) rename applications/{plugins => external}/flipper_i2ctools/i2csniffer.h (100%) rename applications/{plugins => external}/flipper_i2ctools/i2ctools.c (100%) rename applications/{plugins => external}/flipper_i2ctools/i2ctools.gif (100%) rename applications/{plugins => external}/flipper_i2ctools/i2ctools.png (100%) rename applications/{plugins => external}/flipper_i2ctools/i2ctools_i.h (100%) rename applications/{plugins => external}/flipper_i2ctools/images/ButtonDown_7x4.png (100%) rename applications/{plugins => external}/flipper_i2ctools/images/ButtonLeft_4x7.png (100%) rename applications/{plugins => external}/flipper_i2ctools/images/ButtonRight_4x7.png (100%) rename applications/{plugins => external}/flipper_i2ctools/images/ButtonUp_7x4.png (100%) rename applications/{plugins => external}/flipper_i2ctools/images/Ok_btn_9x9.png (100%) rename applications/{plugins => external}/flipper_i2ctools/images/i2ctools_main_76x59.png (100%) rename applications/{plugins => external}/flipper_i2ctools/views/main_view.c (100%) rename applications/{plugins => external}/flipper_i2ctools/views/main_view.h (100%) rename applications/{plugins => external}/flipper_i2ctools/views/scanner_view.c (100%) rename applications/{plugins => external}/flipper_i2ctools/views/scanner_view.h (100%) rename applications/{plugins => external}/flipper_i2ctools/views/sender_view.c (100%) rename applications/{plugins => external}/flipper_i2ctools/views/sender_view.h (100%) rename applications/{plugins => external}/flipper_i2ctools/views/sniffer_view.c (100%) rename applications/{plugins => external}/flipper_i2ctools/views/sniffer_view.h (100%) rename applications/{plugins => external}/game15/README.md (100%) rename applications/{plugins => external}/game15/application.fam (100%) rename applications/{plugins => external}/game15/game15.c (100%) rename applications/{plugins => external}/game15/game15_10px.png (100%) rename applications/{plugins => external}/game15/images/Game15.png (100%) rename applications/{plugins => external}/game15/images/Game15Popup.png (100%) rename applications/{plugins => external}/game15/images/Game15Restore.png (100%) rename applications/{plugins => external}/game15/sandbox.c (100%) rename applications/{plugins => external}/game15/sandbox.h (100%) rename applications/{plugins => external}/game_2048/LICENSE (100%) rename applications/{plugins => external}/game_2048/README.md (100%) rename applications/{plugins => external}/game_2048/application.fam (100%) rename applications/{plugins => external}/game_2048/array_utils.c (100%) rename applications/{plugins => external}/game_2048/array_utils.h (100%) rename applications/{plugins => external}/game_2048/digits.h (100%) rename applications/{plugins => external}/game_2048/game_2048.c (100%) rename applications/{plugins => external}/game_2048/game_2048.png (100%) rename applications/{plugins => external}/game_2048/images/screenshot1.png (100%) rename applications/{plugins => external}/game_2048/images/screenshot2.png (100%) rename applications/{plugins => external}/gps_nmea_uart/LICENSE (100%) rename applications/{plugins => external}/gps_nmea_uart/README.md (100%) rename applications/{plugins => external}/gps_nmea_uart/application.fam (100%) rename applications/{plugins => external}/gps_nmea_uart/gps.c (100%) rename applications/{plugins => external}/gps_nmea_uart/gps_10px.png (100%) rename applications/{plugins => external}/gps_nmea_uart/gps_uart.c (100%) rename applications/{plugins => external}/gps_nmea_uart/gps_uart.h (100%) rename applications/{plugins => external}/gps_nmea_uart/minmea.c (100%) rename applications/{plugins => external}/gps_nmea_uart/minmea.h (100%) rename applications/{plugins => external}/gps_nmea_uart/ui.png (100%) rename applications/{plugins => external}/gps_nmea_uart/wiring.png (100%) rename applications/{plugins => external}/hc_sr04/application.fam (100%) rename applications/{plugins => external}/hc_sr04/dist_sensor10px.png (100%) rename applications/{plugins => external}/hc_sr04/hc_sr04.c (100%) rename applications/{plugins => external}/heap_defence_game/application.fam (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Background_128x64.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Box1_10x10.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Box2_10x10.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Box3_10x10.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Box4_10x10.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Box5_10x10.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Box6p_10x10.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Box7p_10x10.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Box8p_10x10.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Game_over_128x64.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_start_128x64/frame_01.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_start_128x64/frame_02.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_start_128x64/frame_03.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_start_128x64/frame_04.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/HD_start_128x64/frame_rate (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Person4_1_10x20.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Person4_2_10x20.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Person5_1_10x20.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Person5_2_10x20.png (100%) rename applications/{plugins => external}/heap_defence_game/assets_images/Start_128x64.png (100%) rename applications/{plugins => external}/heap_defence_game/box.png (100%) rename applications/{plugins => external}/heap_defence_game/heap_defence.c (100%) rename applications/{plugins => external}/heap_defence_game/hede_assets.c (100%) rename applications/{plugins => external}/heap_defence_game/hede_assets.h (100%) rename applications/{plugins => external}/hex_viewer/LICENSE (100%) rename applications/{plugins => external}/hex_viewer/application.fam (100%) rename applications/{plugins => external}/hex_viewer/hex_viewer.c (100%) rename applications/{plugins => external}/hex_viewer/icons/hex_10px.png (100%) rename applications/{plugins => external}/ibtn_fuzzer/LICENSE.md (100%) rename applications/{plugins => external}/ibtn_fuzzer/application.fam (100%) rename applications/{plugins => external}/ibtn_fuzzer/ibtnfuzzer.c (100%) rename applications/{plugins => external}/ibtn_fuzzer/ibtnfuzzer.h (100%) rename applications/{plugins => external}/ibtn_fuzzer/ibutt_10px.png (100%) rename applications/{plugins => external}/ibtn_fuzzer/images/ibutt_10px.png (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c (100%) rename applications/{plugins => external}/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h (100%) rename applications/{plugins => external}/lightmeter/LICENSE (100%) rename applications/{plugins => external}/lightmeter/README.md (100%) rename applications/{plugins => external}/lightmeter/application.fam (100%) rename applications/{plugins => external}/lightmeter/gui/scenes/config/lightmeter_scene.c (100%) rename applications/{plugins => external}/lightmeter/gui/scenes/config/lightmeter_scene.h (100%) rename applications/{plugins => external}/lightmeter/gui/scenes/config/lightmeter_scene_config.h (100%) rename applications/{plugins => external}/lightmeter/gui/scenes/lightmeter_scene_about.c (100%) rename applications/{plugins => external}/lightmeter/gui/scenes/lightmeter_scene_config.c (100%) rename applications/{plugins => external}/lightmeter/gui/scenes/lightmeter_scene_help.c (100%) rename applications/{plugins => external}/lightmeter/gui/scenes/lightmeter_scene_main.c (100%) rename applications/{plugins => external}/lightmeter/gui/views/main_view.c (100%) rename applications/{plugins => external}/lightmeter/gui/views/main_view.h (100%) rename applications/{plugins => external}/lightmeter/icons/T_10x14.png (100%) rename applications/{plugins => external}/lightmeter/icons/f_10x14.png (100%) rename applications/{plugins => external}/lightmeter/lib/BH1750/BH1750.c (100%) rename applications/{plugins => external}/lightmeter/lib/BH1750/BH1750.h (100%) rename applications/{plugins => external}/lightmeter/lib/BH1750/LICENSE (100%) rename applications/{plugins => external}/lightmeter/lib/BH1750/README.md (100%) rename applications/{plugins => external}/lightmeter/lib/BH1750/docs/BH1750.pdf (100%) rename applications/{plugins => external}/lightmeter/lightmeter.c (100%) rename applications/{plugins => external}/lightmeter/lightmeter.h (100%) rename applications/{plugins => external}/lightmeter/lightmeter.png (100%) rename applications/{plugins => external}/lightmeter/lightmeter_config.h (100%) rename applications/{plugins => external}/lightmeter/lightmeter_helper.c (100%) rename applications/{plugins => external}/lightmeter/lightmeter_helper.h (100%) rename applications/{plugins => external}/metronome/README.md (100%) rename applications/{plugins => external}/metronome/application.fam (100%) rename applications/{plugins => external}/metronome/gui_extensions.c (100%) rename applications/{plugins => external}/metronome/gui_extensions.h (100%) rename applications/{plugins => external}/metronome/images/ButtonUp_7x4.png (100%) rename applications/{plugins => external}/metronome/img/screenshot.png (100%) rename applications/{plugins => external}/metronome/img/wave_left_4x14.png (100%) rename applications/{plugins => external}/metronome/img/wave_right_4x14.png (100%) rename applications/{plugins => external}/metronome/metronome.c (100%) rename applications/{plugins => external}/metronome/metronome_icon.png (100%) rename applications/{plugins => external}/minesweeper/LICENSE (100%) rename applications/{plugins => external}/minesweeper/README.md (100%) rename applications/{plugins => external}/minesweeper/application.fam (100%) rename applications/{plugins => external}/minesweeper/assets.h (100%) rename applications/{plugins => external}/minesweeper/assets/asset (100%) rename applications/{plugins => external}/minesweeper/assets/mockup.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_0.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_0.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_1.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_1.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_2.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_2.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_3.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_3.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_4.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_4.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_5.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_5.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_6.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_6.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_7.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_7.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_8.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_8.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_empty.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_flag.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_flag.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_mine.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_mine.xbm (100%) rename applications/{plugins => external}/minesweeper/assets/tile_uncleared.png (100%) rename applications/{plugins => external}/minesweeper/assets/tile_uncleared.xbm (100%) rename applications/{plugins => external}/minesweeper/img/screenshot.png (100%) rename applications/{plugins => external}/minesweeper/minesweeper.c (100%) rename applications/{plugins => external}/minesweeper/minesweeper_icon.png (100%) rename applications/{plugins => external}/morse_code/application.fam (100%) rename applications/{plugins => external}/morse_code/morse_code.c (100%) rename applications/{plugins => external}/morse_code/morse_code_10px.png (100%) rename applications/{plugins => external}/morse_code/morse_code_worker.c (100%) rename applications/{plugins => external}/morse_code/morse_code_worker.h (100%) rename applications/{plugins => external}/mousejacker/application.fam (100%) rename applications/{plugins => external}/mousejacker/images/badusb_10px.png (100%) rename applications/{plugins => external}/mousejacker/images/sub1_10px.png (100%) rename applications/{plugins => external}/mousejacker/lib/nrf24/nrf24.c (100%) rename applications/{plugins => external}/mousejacker/lib/nrf24/nrf24.h (100%) rename applications/{plugins => external}/mousejacker/mouse_10px.png (100%) rename applications/{plugins => external}/mousejacker/mousejacker.c (100%) rename applications/{plugins => external}/mousejacker/mousejacker_ducky.c (100%) rename applications/{plugins => external}/mousejacker/mousejacker_ducky.h (100%) rename applications/{plugins => external}/multi_converter/application.fam (100%) rename applications/{plugins => external}/multi_converter/converter_10px.png (100%) rename applications/{plugins => external}/multi_converter/multi_converter.c (100%) rename applications/{plugins => external}/multi_converter/multi_converter_definitions.h (100%) rename applications/{plugins => external}/multi_converter/multi_converter_mode_display.c (100%) rename applications/{plugins => external}/multi_converter/multi_converter_mode_display.h (100%) rename applications/{plugins => external}/multi_converter/multi_converter_mode_select.c (100%) rename applications/{plugins => external}/multi_converter/multi_converter_mode_select.h (100%) rename applications/{plugins => external}/multi_converter/multi_converter_units.c (100%) rename applications/{plugins => external}/multi_converter/multi_converter_units.h (100%) rename applications/{plugins => external}/nrfsniff/application.fam (100%) rename applications/{plugins => external}/nrfsniff/lib/nrf24/nrf24.c (100%) rename applications/{plugins => external}/nrfsniff/lib/nrf24/nrf24.h (100%) rename applications/{plugins => external}/nrfsniff/nrfsniff.c (100%) rename applications/{plugins => external}/nrfsniff/nrfsniff_10px.png (100%) rename applications/{plugins => external}/playlist/application.fam (100%) rename applications/{plugins => external}/playlist/canvas_helper.c (100%) rename applications/{plugins => external}/playlist/canvas_helper.h (100%) rename applications/{plugins => external}/playlist/images/ButtonRight_4x7.png (100%) rename applications/{plugins => external}/playlist/images/sub1_10px.png (100%) rename applications/{plugins => external}/playlist/playlist.c (100%) rename applications/{plugins => external}/playlist/playlist_10px.png (100%) rename applications/{plugins => external}/playlist/playlist_file.c (100%) rename applications/{plugins => external}/playlist/playlist_file.h (100%) rename applications/{plugins => external}/pocsag_pager/application.fam (100%) rename applications/{plugins => external}/pocsag_pager/helpers/pocsag_pager_event.h (100%) rename applications/{plugins => external}/pocsag_pager/helpers/pocsag_pager_types.h (100%) rename applications/{plugins => external}/pocsag_pager/images/Lock_7x8.png (100%) rename applications/{plugins => external}/pocsag_pager/images/Message_8x7.png (100%) rename applications/{plugins => external}/pocsag_pager/images/Pin_back_arrow_10x8.png (100%) rename applications/{plugins => external}/pocsag_pager/images/Quest_7x8.png (100%) rename applications/{plugins => external}/pocsag_pager/images/Scanning_123x52.png (100%) rename applications/{plugins => external}/pocsag_pager/images/Unlock_7x8.png (100%) rename applications/{plugins => external}/pocsag_pager/images/WarningDolphin_45x42.png (100%) rename applications/{plugins => external}/pocsag_pager/pocsag_pager_10px.png (100%) rename applications/{plugins => external}/pocsag_pager/pocsag_pager_app.c (100%) rename applications/{plugins => external}/pocsag_pager/pocsag_pager_app_i.c (100%) rename applications/{plugins => external}/pocsag_pager/pocsag_pager_app_i.h (100%) rename applications/{plugins => external}/pocsag_pager/pocsag_pager_history.c (100%) rename applications/{plugins => external}/pocsag_pager/pocsag_pager_history.h (100%) rename applications/{plugins => external}/pocsag_pager/protocols/pcsg_generic.c (100%) rename applications/{plugins => external}/pocsag_pager/protocols/pcsg_generic.h (100%) rename applications/{plugins => external}/pocsag_pager/protocols/pocsag.c (100%) rename applications/{plugins => external}/pocsag_pager/protocols/pocsag.h (100%) rename applications/{plugins => external}/pocsag_pager/protocols/protocol_items.c (100%) rename applications/{plugins => external}/pocsag_pager/protocols/protocol_items.h (100%) rename applications/{plugins => external}/pocsag_pager/scenes/pocsag_pager_receiver.c (100%) rename applications/{plugins => external}/pocsag_pager/scenes/pocsag_pager_scene.c (100%) rename applications/{plugins => external}/pocsag_pager/scenes/pocsag_pager_scene.h (100%) rename applications/{plugins => external}/pocsag_pager/scenes/pocsag_pager_scene_about.c (100%) rename applications/{plugins => external}/pocsag_pager/scenes/pocsag_pager_scene_config.h (100%) rename applications/{plugins => external}/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c (100%) rename applications/{plugins => external}/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c (100%) rename applications/{plugins => external}/pocsag_pager/scenes/pocsag_pager_scene_start.c (100%) rename applications/{plugins => external}/pocsag_pager/views/pocsag_pager_receiver.c (100%) rename applications/{plugins => external}/pocsag_pager/views/pocsag_pager_receiver.h (100%) rename applications/{plugins => external}/pocsag_pager/views/pocsag_pager_receiver_info.c (100%) rename applications/{plugins => external}/pocsag_pager/views/pocsag_pager_receiver_info.h (100%) rename applications/{plugins => external}/protoview/LICENSE (100%) rename applications/{plugins => external}/protoview/README.md (100%) rename applications/{plugins => external}/protoview/app.c (100%) rename applications/{plugins => external}/protoview/app.h (100%) rename applications/{plugins => external}/protoview/app_subghz.c (100%) rename applications/{plugins => external}/protoview/appicon.png (100%) rename applications/{plugins => external}/protoview/application.fam (100%) rename applications/{plugins => external}/protoview/crc.c (100%) rename applications/{plugins => external}/protoview/custom_presets.h (100%) rename applications/{plugins => external}/protoview/fields.c (100%) rename applications/{plugins => external}/protoview/images/ProtoViewSignal.jpg (100%) rename applications/{plugins => external}/protoview/images/protoview_1.jpg (100%) rename applications/{plugins => external}/protoview/images/protoview_2.jpg (100%) rename applications/{plugins => external}/protoview/protocols/b4b1.c (100%) rename applications/{plugins => external}/protoview/protocols/keeloq.c (100%) rename applications/{plugins => external}/protoview/protocols/oregon2.c (100%) rename applications/{plugins => external}/protoview/protocols/pvchat.c (100%) rename applications/{plugins => external}/protoview/protocols/tpms/citroen.c (100%) rename applications/{plugins => external}/protoview/protocols/tpms/ford.c (100%) rename applications/{plugins => external}/protoview/protocols/tpms/renault.c (100%) rename applications/{plugins => external}/protoview/protocols/tpms/schrader.c (100%) rename applications/{plugins => external}/protoview/protocols/tpms/schrader_eg53ma4.c (100%) rename applications/{plugins => external}/protoview/protocols/tpms/toyota.c (100%) rename applications/{plugins => external}/protoview/protocols/unknown.c (100%) rename applications/{plugins => external}/protoview/raw_samples.c (100%) rename applications/{plugins => external}/protoview/raw_samples.h (100%) rename applications/{plugins => external}/protoview/signal.c (100%) rename applications/{plugins => external}/protoview/signal_file.c (100%) rename applications/{plugins => external}/protoview/ui.c (100%) rename applications/{plugins => external}/protoview/view_build.c (100%) rename applications/{plugins => external}/protoview/view_direct_sampling.c (100%) rename applications/{plugins => external}/protoview/view_info.c (100%) rename applications/{plugins => external}/protoview/view_raw_signal.c (100%) rename applications/{plugins => external}/protoview/view_settings.c (100%) rename applications/{plugins => external}/sentry_safe/application.fam (100%) rename applications/{plugins => external}/sentry_safe/safe_10px.png (100%) rename applications/{plugins => external}/sentry_safe/sentry_safe.c (100%) rename applications/{plugins => external}/solitaire/application.fam (100%) rename applications/{plugins => external}/solitaire/assets/card_graphics.png (100%) rename applications/{plugins => external}/solitaire/assets/solitaire_main.png (100%) rename applications/{plugins => external}/solitaire/common/card.c (100%) rename applications/{plugins => external}/solitaire/common/card.h (100%) rename applications/{plugins => external}/solitaire/common/dml.c (100%) rename applications/{plugins => external}/solitaire/common/dml.h (100%) rename applications/{plugins => external}/solitaire/common/menu.c (100%) rename applications/{plugins => external}/solitaire/common/menu.h (100%) rename applications/{plugins => external}/solitaire/common/queue.c (100%) rename applications/{plugins => external}/solitaire/common/queue.h (100%) rename applications/{plugins => external}/solitaire/common/ui.c (100%) rename applications/{plugins => external}/solitaire/common/ui.h (100%) rename applications/{plugins => external}/solitaire/defines.h (100%) rename applications/{plugins => external}/solitaire/solitaire.c (100%) rename applications/{plugins => external}/solitaire/solitaire_10px.png (100%) rename applications/{plugins => external}/spectrum_analyzer/application.fam (100%) rename applications/{plugins => external}/spectrum_analyzer/spectrum_10px.png (100%) rename applications/{plugins => external}/spectrum_analyzer/spectrum_analyzer.c (100%) rename applications/{plugins => external}/spectrum_analyzer/spectrum_analyzer.h (100%) rename applications/{plugins => external}/spectrum_analyzer/spectrum_analyzer_worker.c (100%) rename applications/{plugins => external}/spectrum_analyzer/spectrum_analyzer_worker.h (100%) rename applications/{plugins => external}/subbrute (100%) rename applications/{plugins => external}/swd_probe/.gitignore (100%) rename applications/{plugins => external}/swd_probe/LICENSE.txt (100%) rename applications/{plugins => external}/swd_probe/README.md (100%) rename applications/{plugins => external}/swd_probe/adi.c (100%) rename applications/{plugins => external}/swd_probe/adi.h (100%) rename applications/{plugins => external}/swd_probe/application.fam (100%) rename applications/{plugins => external}/swd_probe/icons/ButtonDown_7x4.png (100%) rename applications/{plugins => external}/swd_probe/icons/ButtonUp_7x4.png (100%) rename applications/{plugins => external}/swd_probe/icons/app.png (100%) rename applications/{plugins => external}/swd_probe/icons/swd.png (100%) rename applications/{plugins => external}/swd_probe/jep106.c (100%) rename applications/{plugins => external}/swd_probe/jep106.h (100%) rename applications/{plugins => external}/swd_probe/jep106.inc (100%) rename applications/{plugins => external}/swd_probe/model/chip.ply (100%) rename applications/{plugins => external}/swd_probe/model/convert.py (100%) rename applications/{plugins => external}/swd_probe/model/model_chip.h (100%) rename applications/{plugins => external}/swd_probe/swd_probe_app.c (100%) rename applications/{plugins => external}/swd_probe/swd_probe_app.h (100%) rename applications/{plugins => external}/swd_probe/usb_uart.c (100%) rename applications/{plugins => external}/swd_probe/usb_uart.h (100%) rename applications/{plugins => external}/tetris_game/application.fam (100%) rename applications/{plugins => external}/tetris_game/tetris_10px.png (100%) rename applications/{plugins => external}/tetris_game/tetris_game.c (100%) rename applications/{plugins => external}/text_viewer/LICENSE (100%) rename applications/{plugins => external}/text_viewer/README.md (100%) rename applications/{plugins => external}/text_viewer/application.fam (100%) rename applications/{plugins => external}/text_viewer/icons/text_10px.png (100%) rename applications/{plugins => external}/text_viewer/text_viewer.c (100%) rename applications/{plugins => external}/text_viewer/textviewerflipper.PNG (100%) rename applications/{plugins => external}/tictactoe_game/application.fam (100%) rename applications/{plugins => external}/tictactoe_game/tictactoe_10px.png (100%) rename applications/{plugins => external}/tictactoe_game/tictactoe_game.c (100%) rename applications/{plugins => external}/totp/LICENSE (100%) rename applications/{plugins => external}/totp/application.fam (100%) rename applications/{plugins => external}/totp/cli/cli.c (100%) rename applications/{plugins => external}/totp/cli/cli.h (100%) rename applications/{plugins => external}/totp/cli/cli_helpers.c (100%) rename applications/{plugins => external}/totp/cli/cli_helpers.h (100%) rename applications/{plugins => external}/totp/cli/commands/add/add.c (100%) rename applications/{plugins => external}/totp/cli/commands/add/add.h (100%) rename applications/{plugins => external}/totp/cli/commands/delete/delete.c (100%) rename applications/{plugins => external}/totp/cli/commands/delete/delete.h (100%) rename applications/{plugins => external}/totp/cli/commands/help/help.c (100%) rename applications/{plugins => external}/totp/cli/commands/help/help.h (100%) rename applications/{plugins => external}/totp/cli/commands/list/list.c (100%) rename applications/{plugins => external}/totp/cli/commands/list/list.h (100%) rename applications/{plugins => external}/totp/cli/commands/move/move.c (100%) rename applications/{plugins => external}/totp/cli/commands/move/move.h (100%) rename applications/{plugins => external}/totp/cli/commands/notification/notification.c (100%) rename applications/{plugins => external}/totp/cli/commands/notification/notification.h (100%) rename applications/{plugins => external}/totp/cli/commands/pin/pin.c (100%) rename applications/{plugins => external}/totp/cli/commands/pin/pin.h (100%) rename applications/{plugins => external}/totp/cli/commands/reset/reset.c (100%) rename applications/{plugins => external}/totp/cli/commands/reset/reset.h (100%) rename applications/{plugins => external}/totp/cli/commands/timezone/timezone.c (100%) rename applications/{plugins => external}/totp/cli/commands/timezone/timezone.h (100%) rename applications/{plugins => external}/totp/images/DolphinCommon_56x48.png (100%) rename applications/{plugins => external}/totp/images/totp_arrow_bottom_10x5.png (100%) rename applications/{plugins => external}/totp/images/totp_arrow_left_8x9.png (100%) rename applications/{plugins => external}/totp/images/totp_arrow_right_8x9.png (100%) rename applications/{plugins => external}/totp/lib/base32/base32.c (100%) rename applications/{plugins => external}/totp/lib/base32/base32.h (100%) rename applications/{plugins => external}/totp/lib/list/list.c (100%) rename applications/{plugins => external}/totp/lib/list/list.h (100%) rename applications/{plugins => external}/totp/lib/polyfills/memset_s.c (100%) rename applications/{plugins => external}/totp/lib/polyfills/memset_s.h (100%) rename applications/{plugins => external}/totp/lib/polyfills/strnlen.c (100%) rename applications/{plugins => external}/totp/lib/polyfills/strnlen.h (100%) rename applications/{plugins => external}/totp/lib/roll_value/roll_value.c (100%) rename applications/{plugins => external}/totp/lib/roll_value/roll_value.h (100%) rename applications/{plugins => external}/totp/lib/timezone_utils/timezone_utils.c (100%) rename applications/{plugins => external}/totp/lib/timezone_utils/timezone_utils.h (100%) rename applications/{plugins => external}/totp/services/config/config.c (100%) rename applications/{plugins => external}/totp/services/config/config.h (100%) rename applications/{plugins => external}/totp/services/config/constants.h (100%) rename applications/{plugins => external}/totp/services/config/migrations/config_migration_v1_to_v2.c (100%) rename applications/{plugins => external}/totp/services/config/migrations/config_migration_v1_to_v2.h (100%) rename applications/{plugins => external}/totp/services/config/migrations/config_migration_v2_to_v3.c (100%) rename applications/{plugins => external}/totp/services/config/migrations/config_migration_v2_to_v3.h (100%) rename applications/{plugins => external}/totp/services/convert/convert.h (100%) rename applications/{plugins => external}/totp/services/crypto/crypto.c (100%) rename applications/{plugins => external}/totp/services/crypto/crypto.h (100%) rename applications/{plugins => external}/totp/services/hmac/byteswap.c (100%) rename applications/{plugins => external}/totp/services/hmac/byteswap.h (100%) rename applications/{plugins => external}/totp/services/hmac/hmac_common.h (100%) rename applications/{plugins => external}/totp/services/hmac/hmac_sha1.c (100%) rename applications/{plugins => external}/totp/services/hmac/hmac_sha1.h (100%) rename applications/{plugins => external}/totp/services/hmac/hmac_sha256.c (100%) rename applications/{plugins => external}/totp/services/hmac/hmac_sha256.h (100%) rename applications/{plugins => external}/totp/services/hmac/hmac_sha512.c (100%) rename applications/{plugins => external}/totp/services/hmac/hmac_sha512.h (100%) rename applications/{plugins => external}/totp/services/hmac/memxor.c (100%) rename applications/{plugins => external}/totp/services/hmac/memxor.h (100%) rename applications/{plugins => external}/totp/services/hmac/sha1.c (100%) rename applications/{plugins => external}/totp/services/hmac/sha1.h (100%) rename applications/{plugins => external}/totp/services/hmac/sha256.c (100%) rename applications/{plugins => external}/totp/services/hmac/sha256.h (100%) rename applications/{plugins => external}/totp/services/hmac/sha512.c (100%) rename applications/{plugins => external}/totp/services/hmac/sha512.h (100%) rename applications/{plugins => external}/totp/services/hmac/u64.h (100%) rename applications/{plugins => external}/totp/services/totp/totp.c (100%) rename applications/{plugins => external}/totp/services/totp/totp.h (100%) rename applications/{plugins => external}/totp/totp_10px.png (100%) rename applications/{plugins => external}/totp/totp_app.c (100%) rename applications/{plugins => external}/totp/types/common.h (100%) rename applications/{plugins => external}/totp/types/event_type.h (100%) rename applications/{plugins => external}/totp/types/notification_method.h (100%) rename applications/{plugins => external}/totp/types/nullable.h (100%) rename applications/{plugins => external}/totp/types/plugin_event.h (100%) rename applications/{plugins => external}/totp/types/plugin_state.h (100%) rename applications/{plugins => external}/totp/types/token_info.c (100%) rename applications/{plugins => external}/totp/types/token_info.h (100%) rename applications/{plugins => external}/totp/types/user_pin_codes.h (100%) rename applications/{plugins => external}/totp/ui/common_dialogs.c (100%) rename applications/{plugins => external}/totp/ui/common_dialogs.h (100%) rename applications/{plugins => external}/totp/ui/constants.h (100%) rename applications/{plugins => external}/totp/ui/scene_director.c (100%) rename applications/{plugins => external}/totp/ui/scene_director.h (100%) rename applications/{plugins => external}/totp/ui/scenes/add_new_token/totp_input_text.c (100%) rename applications/{plugins => external}/totp/ui/scenes/add_new_token/totp_input_text.h (100%) rename applications/{plugins => external}/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c (100%) rename applications/{plugins => external}/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h (100%) rename applications/{plugins => external}/totp/ui/scenes/app_settings/totp_app_settings.c (100%) rename applications/{plugins => external}/totp/ui/scenes/app_settings/totp_app_settings.h (100%) rename applications/{plugins => external}/totp/ui/scenes/authenticate/totp_scene_authenticate.c (100%) rename applications/{plugins => external}/totp/ui/scenes/authenticate/totp_scene_authenticate.h (100%) rename applications/{plugins => external}/totp/ui/scenes/generate_token/totp_scene_generate_token.c (100%) rename applications/{plugins => external}/totp/ui/scenes/generate_token/totp_scene_generate_token.h (100%) rename applications/{plugins => external}/totp/ui/scenes/token_menu/totp_scene_token_menu.c (100%) rename applications/{plugins => external}/totp/ui/scenes/token_menu/totp_scene_token_menu.h (100%) rename applications/{plugins => external}/totp/ui/totp_scenes_enum.h (100%) rename applications/{plugins => external}/totp/ui/ui_controls.c (100%) rename applications/{plugins => external}/totp/ui/ui_controls.h (100%) rename applications/{plugins => external}/totp/workers/type_code/type_code.c (100%) rename applications/{plugins => external}/totp/workers/type_code/type_code.h (100%) rename applications/{plugins => external}/uart_terminal/LICENSE (100%) rename applications/{plugins => external}/uart_terminal/README.md (100%) rename applications/{plugins => external}/uart_terminal/application.fam (100%) rename applications/{plugins => external}/uart_terminal/assets/KeyBackspaceSelected_16x9.png (100%) rename applications/{plugins => external}/uart_terminal/assets/KeyBackspace_16x9.png (100%) rename applications/{plugins => external}/uart_terminal/assets/KeySaveSelected_24x11.png (100%) rename applications/{plugins => external}/uart_terminal/assets/KeySave_24x11.png (100%) rename applications/{plugins => external}/uart_terminal/assets/WarningDolphin_45x42.png (100%) rename applications/{plugins => external}/uart_terminal/scenes/uart_terminal_scene.c (100%) rename applications/{plugins => external}/uart_terminal/scenes/uart_terminal_scene.h (100%) rename applications/{plugins => external}/uart_terminal/scenes/uart_terminal_scene_config.h (100%) rename applications/{plugins => external}/uart_terminal/scenes/uart_terminal_scene_console_output.c (100%) rename applications/{plugins => external}/uart_terminal/scenes/uart_terminal_scene_start.c (100%) rename applications/{plugins => external}/uart_terminal/scenes/uart_terminal_scene_text_input.c (100%) rename applications/{plugins => external}/uart_terminal/uart_terminal.png (100%) rename applications/{plugins => external}/uart_terminal/uart_terminal_app.c (100%) rename applications/{plugins => external}/uart_terminal/uart_terminal_app.h (100%) rename applications/{plugins => external}/uart_terminal/uart_terminal_app_i.h (100%) rename applications/{plugins => external}/uart_terminal/uart_terminal_custom_event.h (100%) rename applications/{plugins => external}/uart_terminal/uart_terminal_uart.c (100%) rename applications/{plugins => external}/uart_terminal/uart_terminal_uart.h (100%) rename applications/{plugins => external}/uart_terminal/uart_text_input.c (100%) rename applications/{plugins => external}/uart_terminal/uart_text_input.h (100%) rename applications/{plugins => external}/uart_terminal/uart_validators.c (100%) rename applications/{plugins => external}/uart_terminal/uart_validators.h (100%) rename applications/{plugins => external}/unitemp/LICENSE.md (100%) rename applications/{plugins => external}/unitemp/README.md (100%) rename applications/{plugins => external}/unitemp/Sensors.c (100%) rename applications/{plugins => external}/unitemp/Sensors.h (100%) rename applications/{plugins => external}/unitemp/application.fam (100%) rename applications/{plugins => external}/unitemp/assets/README.MD (100%) rename applications/{plugins => external}/unitemp/assets/flipper_happy_2_60x38.png (100%) rename applications/{plugins => external}/unitemp/assets/flipper_happy_60x38.png (100%) rename applications/{plugins => external}/unitemp/assets/flipper_sad_60x38.png (100%) rename applications/{plugins => external}/unitemp/assets/hum_9x15.png (100%) rename applications/{plugins => external}/unitemp/assets/in_hg_15x15.png (100%) rename applications/{plugins => external}/unitemp/assets/mm_hg_15x15.png (100%) rename applications/{plugins => external}/unitemp/assets/pressure_7x13.png (100%) rename applications/{plugins => external}/unitemp/assets/repo_qr_50x50.png (100%) rename applications/{plugins => external}/unitemp/assets/sherlok_53x45.png (100%) rename applications/{plugins => external}/unitemp/assets/temp_C_11x14.png (100%) rename applications/{plugins => external}/unitemp/assets/temp_F_11x14.png (100%) rename applications/{plugins => external}/unitemp/icon.png (100%) rename applications/{plugins => external}/unitemp/interfaces/I2CSensor.c (100%) rename applications/{plugins => external}/unitemp/interfaces/I2CSensor.h (100%) rename applications/{plugins => external}/unitemp/interfaces/OneWireSensor.c (100%) rename applications/{plugins => external}/unitemp/interfaces/OneWireSensor.h (100%) rename applications/{plugins => external}/unitemp/interfaces/SPISensor.c (100%) rename applications/{plugins => external}/unitemp/interfaces/SPISensor.h (100%) rename applications/{plugins => external}/unitemp/interfaces/SingleWireSensor.c (100%) rename applications/{plugins => external}/unitemp/interfaces/SingleWireSensor.h (100%) rename applications/{plugins => external}/unitemp/sensors/AM2320.c (100%) rename applications/{plugins => external}/unitemp/sensors/AM2320.h (100%) rename applications/{plugins => external}/unitemp/sensors/BME680.c (100%) rename applications/{plugins => external}/unitemp/sensors/BME680.h (100%) rename applications/{plugins => external}/unitemp/sensors/BMP180.c (100%) rename applications/{plugins => external}/unitemp/sensors/BMP180.h (100%) rename applications/{plugins => external}/unitemp/sensors/BMx280.c (100%) rename applications/{plugins => external}/unitemp/sensors/BMx280.h (100%) rename applications/{plugins => external}/unitemp/sensors/DHT20.c (100%) rename applications/{plugins => external}/unitemp/sensors/DHT20.h (100%) rename applications/{plugins => external}/unitemp/sensors/HDC1080.c (100%) rename applications/{plugins => external}/unitemp/sensors/HDC1080.h (100%) rename applications/{plugins => external}/unitemp/sensors/HTU21x.c (100%) rename applications/{plugins => external}/unitemp/sensors/HTU21x.h (100%) rename applications/{plugins => external}/unitemp/sensors/LM75.c (100%) rename applications/{plugins => external}/unitemp/sensors/LM75.h (100%) rename applications/{plugins => external}/unitemp/sensors/MAX31855.c (100%) rename applications/{plugins => external}/unitemp/sensors/MAX31855.h (100%) rename applications/{plugins => external}/unitemp/sensors/MAX6675.c (100%) rename applications/{plugins => external}/unitemp/sensors/MAX6675.h (100%) rename applications/{plugins => external}/unitemp/sensors/SHT30.c (100%) rename applications/{plugins => external}/unitemp/sensors/SHT30.h (100%) rename applications/{plugins => external}/unitemp/sensors/Sensors.xlsx (100%) rename applications/{plugins => external}/unitemp/unitemp.c (100%) rename applications/{plugins => external}/unitemp/unitemp.h (100%) rename applications/{plugins => external}/unitemp/views/General_view.c (100%) rename applications/{plugins => external}/unitemp/views/MainMenu_view.c (100%) rename applications/{plugins => external}/unitemp/views/Popup_view.c (100%) rename applications/{plugins => external}/unitemp/views/SensorActions_view.c (100%) rename applications/{plugins => external}/unitemp/views/SensorEdit_view.c (100%) rename applications/{plugins => external}/unitemp/views/SensorNameEdit_view.c (100%) rename applications/{plugins => external}/unitemp/views/SensorsList_view.c (100%) rename applications/{plugins => external}/unitemp/views/Settings_view.c (100%) rename applications/{plugins => external}/unitemp/views/UnitempViews.h (100%) rename applications/{plugins => external}/unitemp/views/Widgets_view.c (100%) rename applications/{plugins => external}/wav_player/README.md (100%) rename applications/{plugins => external}/wav_player/application.fam (100%) rename applications/{plugins => external}/wav_player/images/music_10px.png (100%) rename applications/{plugins => external}/wav_player/wav_10px.png (100%) rename applications/{plugins => external}/wav_player/wav_parser.c (100%) rename applications/{plugins => external}/wav_player/wav_parser.h (100%) rename applications/{plugins => external}/wav_player/wav_player.c (100%) rename applications/{plugins => external}/wav_player/wav_player_hal.c (100%) rename applications/{plugins => external}/wav_player/wav_player_hal.h (100%) rename applications/{plugins => external}/wav_player/wav_player_view.c (100%) rename applications/{plugins => external}/wav_player/wav_player_view.h (100%) rename applications/{plugins => external}/wifi_marauder_companion/application.fam (100%) rename applications/{plugins => external}/wifi_marauder_companion/scenes/wifi_marauder_scene.c (100%) rename applications/{plugins => external}/wifi_marauder_companion/scenes/wifi_marauder_scene.h (100%) rename applications/{plugins => external}/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h (100%) rename applications/{plugins => external}/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c (100%) rename applications/{plugins => external}/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c (100%) rename applications/{plugins => external}/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c (100%) rename applications/{plugins => external}/wifi_marauder_companion/wifi_10px.png (100%) rename applications/{plugins => external}/wifi_marauder_companion/wifi_marauder_app.c (100%) rename applications/{plugins => external}/wifi_marauder_companion/wifi_marauder_app.h (100%) rename applications/{plugins => external}/wifi_marauder_companion/wifi_marauder_app_i.h (100%) rename applications/{plugins => external}/wifi_marauder_companion/wifi_marauder_custom_event.h (100%) rename applications/{plugins => external}/wifi_marauder_companion/wifi_marauder_uart.c (100%) rename applications/{plugins => external}/wifi_marauder_companion/wifi_marauder_uart.h (100%) rename applications/{plugins => external}/wifi_scanner/FlipperZeroWiFiModuleDefines.h (100%) rename applications/{plugins => external}/wifi_scanner/application.fam (100%) rename applications/{plugins => external}/wifi_scanner/wifi_10px.png (100%) rename applications/{plugins => external}/wifi_scanner/wifi_scanner.c (100%) rename applications/{plugins => external}/zombiez/application.fam (100%) rename applications/{plugins => external}/zombiez/zombie_10px.png (100%) rename applications/{plugins => external}/zombiez/zombiez.c (100%) rename applications/{plugins => external}/zombiez/zombiez.h (100%) delete mode 160000 applications/plugins/dap_link/lib/free-dap diff --git a/.gitmodules b/.gitmodules index 6435cfb1e..5f97063b3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,4 +33,4 @@ url = https://github.com/ataradov/free-dap.git [submodule "applications/external/subbrute"] path = applications/external/subbrute - url = https://github.com/derskythe/flipperzero-subbrute.git + url = https://github.com/derskythe/flipperzero-subbrute.git \ No newline at end of file diff --git a/applications/plugins/doom/README.md b/applications/external/doom/README.md similarity index 100% rename from applications/plugins/doom/README.md rename to applications/external/doom/README.md diff --git a/applications/plugins/doom/application.fam b/applications/external/doom/application.fam similarity index 100% rename from applications/plugins/doom/application.fam rename to applications/external/doom/application.fam diff --git a/applications/plugins/doom/assets/door2.png b/applications/external/doom/assets/door2.png similarity index 100% rename from applications/plugins/doom/assets/door2.png rename to applications/external/doom/assets/door2.png diff --git a/applications/plugins/doom/assets/door_inv.png b/applications/external/doom/assets/door_inv.png similarity index 100% rename from applications/plugins/doom/assets/door_inv.png rename to applications/external/doom/assets/door_inv.png diff --git a/applications/plugins/doom/assets/fire_inv.png b/applications/external/doom/assets/fire_inv.png similarity index 100% rename from applications/plugins/doom/assets/fire_inv.png rename to applications/external/doom/assets/fire_inv.png diff --git a/applications/plugins/doom/assets/fireball_inv.png b/applications/external/doom/assets/fireball_inv.png similarity index 100% rename from applications/plugins/doom/assets/fireball_inv.png rename to applications/external/doom/assets/fireball_inv.png diff --git a/applications/plugins/doom/assets/fireball_mask_inv.png b/applications/external/doom/assets/fireball_mask_inv.png similarity index 100% rename from applications/plugins/doom/assets/fireball_mask_inv.png rename to applications/external/doom/assets/fireball_mask_inv.png diff --git a/applications/plugins/doom/assets/gradient_inv.png b/applications/external/doom/assets/gradient_inv.png similarity index 100% rename from applications/plugins/doom/assets/gradient_inv.png rename to applications/external/doom/assets/gradient_inv.png diff --git a/applications/plugins/doom/assets/gun_inv.png b/applications/external/doom/assets/gun_inv.png similarity index 100% rename from applications/plugins/doom/assets/gun_inv.png rename to applications/external/doom/assets/gun_inv.png diff --git a/applications/plugins/doom/assets/gun_mask_inv.png b/applications/external/doom/assets/gun_mask_inv.png similarity index 100% rename from applications/plugins/doom/assets/gun_mask_inv.png rename to applications/external/doom/assets/gun_mask_inv.png diff --git a/applications/plugins/doom/assets/imp_inv.png b/applications/external/doom/assets/imp_inv.png similarity index 100% rename from applications/plugins/doom/assets/imp_inv.png rename to applications/external/doom/assets/imp_inv.png diff --git a/applications/plugins/doom/assets/imp_mask_inv.png b/applications/external/doom/assets/imp_mask_inv.png similarity index 100% rename from applications/plugins/doom/assets/imp_mask_inv.png rename to applications/external/doom/assets/imp_mask_inv.png diff --git a/applications/plugins/doom/assets/item_inv.png b/applications/external/doom/assets/item_inv.png similarity index 100% rename from applications/plugins/doom/assets/item_inv.png rename to applications/external/doom/assets/item_inv.png diff --git a/applications/plugins/doom/assets/item_mask_inv.png b/applications/external/doom/assets/item_mask_inv.png similarity index 100% rename from applications/plugins/doom/assets/item_mask_inv.png rename to applications/external/doom/assets/item_mask_inv.png diff --git a/applications/plugins/doom/assets/logo_inv.png b/applications/external/doom/assets/logo_inv.png similarity index 100% rename from applications/plugins/doom/assets/logo_inv.png rename to applications/external/doom/assets/logo_inv.png diff --git a/applications/plugins/doom/assets/screenshot-imp2.jpg b/applications/external/doom/assets/screenshot-imp2.jpg similarity index 100% rename from applications/plugins/doom/assets/screenshot-imp2.jpg rename to applications/external/doom/assets/screenshot-imp2.jpg diff --git a/applications/plugins/doom/assets/screenshot-intro2.jpg b/applications/external/doom/assets/screenshot-intro2.jpg similarity index 100% rename from applications/plugins/doom/assets/screenshot-intro2.jpg rename to applications/external/doom/assets/screenshot-intro2.jpg diff --git a/applications/plugins/doom/assets/screenshot-medkit2.jpg b/applications/external/doom/assets/screenshot-medkit2.jpg similarity index 100% rename from applications/plugins/doom/assets/screenshot-medkit2.jpg rename to applications/external/doom/assets/screenshot-medkit2.jpg diff --git a/applications/plugins/doom/assets/screenshot-start2.jpg b/applications/external/doom/assets/screenshot-start2.jpg similarity index 100% rename from applications/plugins/doom/assets/screenshot-start2.jpg rename to applications/external/doom/assets/screenshot-start2.jpg diff --git a/applications/plugins/doom/assets/screenshot1.png b/applications/external/doom/assets/screenshot1.png similarity index 100% rename from applications/plugins/doom/assets/screenshot1.png rename to applications/external/doom/assets/screenshot1.png diff --git a/applications/plugins/doom/assets/screenshot2.png b/applications/external/doom/assets/screenshot2.png similarity index 100% rename from applications/plugins/doom/assets/screenshot2.png rename to applications/external/doom/assets/screenshot2.png diff --git a/applications/plugins/doom/assets/screenshot3.png b/applications/external/doom/assets/screenshot3.png similarity index 100% rename from applications/plugins/doom/assets/screenshot3.png rename to applications/external/doom/assets/screenshot3.png diff --git a/applications/plugins/doom/compiled/assets_icons.c b/applications/external/doom/compiled/assets_icons.c similarity index 100% rename from applications/plugins/doom/compiled/assets_icons.c rename to applications/external/doom/compiled/assets_icons.c diff --git a/applications/plugins/doom/compiled/assets_icons.h b/applications/external/doom/compiled/assets_icons.h similarity index 100% rename from applications/plugins/doom/compiled/assets_icons.h rename to applications/external/doom/compiled/assets_icons.h diff --git a/applications/plugins/doom/constants.h b/applications/external/doom/constants.h similarity index 100% rename from applications/plugins/doom/constants.h rename to applications/external/doom/constants.h diff --git a/applications/plugins/doom/display.h b/applications/external/doom/display.h similarity index 100% rename from applications/plugins/doom/display.h rename to applications/external/doom/display.h diff --git a/applications/plugins/doom/doom.c b/applications/external/doom/doom.c similarity index 100% rename from applications/plugins/doom/doom.c rename to applications/external/doom/doom.c diff --git a/applications/plugins/doom/doom_10px.png b/applications/external/doom/doom_10px.png similarity index 100% rename from applications/plugins/doom/doom_10px.png rename to applications/external/doom/doom_10px.png diff --git a/applications/plugins/doom/doom_music_player_worker.c b/applications/external/doom/doom_music_player_worker.c similarity index 100% rename from applications/plugins/doom/doom_music_player_worker.c rename to applications/external/doom/doom_music_player_worker.c diff --git a/applications/plugins/doom/doom_music_player_worker.h b/applications/external/doom/doom_music_player_worker.h similarity index 100% rename from applications/plugins/doom/doom_music_player_worker.h rename to applications/external/doom/doom_music_player_worker.h diff --git a/applications/plugins/doom/entities.c b/applications/external/doom/entities.c similarity index 100% rename from applications/plugins/doom/entities.c rename to applications/external/doom/entities.c diff --git a/applications/plugins/doom/entities.h b/applications/external/doom/entities.h similarity index 100% rename from applications/plugins/doom/entities.h rename to applications/external/doom/entities.h diff --git a/applications/plugins/doom/level.h b/applications/external/doom/level.h similarity index 100% rename from applications/plugins/doom/level.h rename to applications/external/doom/level.h diff --git a/applications/plugins/doom/sound.h b/applications/external/doom/sound.h similarity index 100% rename from applications/plugins/doom/sound.h rename to applications/external/doom/sound.h diff --git a/applications/plugins/doom/types.c b/applications/external/doom/types.c similarity index 100% rename from applications/plugins/doom/types.c rename to applications/external/doom/types.c diff --git a/applications/plugins/doom/types.h b/applications/external/doom/types.h similarity index 100% rename from applications/plugins/doom/types.h rename to applications/external/doom/types.h diff --git a/applications/plugins/dtmf_dolphin/LICENSE b/applications/external/dtmf_dolphin/LICENSE similarity index 100% rename from applications/plugins/dtmf_dolphin/LICENSE rename to applications/external/dtmf_dolphin/LICENSE diff --git a/applications/plugins/dtmf_dolphin/README.md b/applications/external/dtmf_dolphin/README.md similarity index 100% rename from applications/plugins/dtmf_dolphin/README.md rename to applications/external/dtmf_dolphin/README.md diff --git a/applications/plugins/dtmf_dolphin/application.fam b/applications/external/dtmf_dolphin/application.fam similarity index 100% rename from applications/plugins/dtmf_dolphin/application.fam rename to applications/external/dtmf_dolphin/application.fam diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin.c b/applications/external/dtmf_dolphin/dtmf_dolphin.c similarity index 100% rename from applications/plugins/dtmf_dolphin/dtmf_dolphin.c rename to applications/external/dtmf_dolphin/dtmf_dolphin.c diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.c b/applications/external/dtmf_dolphin/dtmf_dolphin_audio.c similarity index 100% rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.c rename to applications/external/dtmf_dolphin/dtmf_dolphin_audio.c diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.h b/applications/external/dtmf_dolphin/dtmf_dolphin_audio.h similarity index 100% rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_audio.h rename to applications/external/dtmf_dolphin/dtmf_dolphin_audio.h diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.c b/applications/external/dtmf_dolphin/dtmf_dolphin_data.c similarity index 100% rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_data.c rename to applications/external/dtmf_dolphin/dtmf_dolphin_data.c diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_data.h b/applications/external/dtmf_dolphin/dtmf_dolphin_data.h similarity index 100% rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_data.h rename to applications/external/dtmf_dolphin/dtmf_dolphin_data.h diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_event.h b/applications/external/dtmf_dolphin/dtmf_dolphin_event.h similarity index 100% rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_event.h rename to applications/external/dtmf_dolphin/dtmf_dolphin_event.h diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_hal.c b/applications/external/dtmf_dolphin/dtmf_dolphin_hal.c similarity index 100% rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_hal.c rename to applications/external/dtmf_dolphin/dtmf_dolphin_hal.c diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_hal.h b/applications/external/dtmf_dolphin/dtmf_dolphin_hal.h similarity index 100% rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_hal.h rename to applications/external/dtmf_dolphin/dtmf_dolphin_hal.h diff --git a/applications/plugins/dtmf_dolphin/dtmf_dolphin_i.h b/applications/external/dtmf_dolphin/dtmf_dolphin_i.h similarity index 100% rename from applications/plugins/dtmf_dolphin/dtmf_dolphin_i.h rename to applications/external/dtmf_dolphin/dtmf_dolphin_i.h diff --git a/applications/plugins/dtmf_dolphin/phone.png b/applications/external/dtmf_dolphin/phone.png similarity index 100% rename from applications/plugins/dtmf_dolphin/phone.png rename to applications/external/dtmf_dolphin/phone.png diff --git a/applications/plugins/dtmf_dolphin/pics/dialer.jpg b/applications/external/dtmf_dolphin/pics/dialer.jpg similarity index 100% rename from applications/plugins/dtmf_dolphin/pics/dialer.jpg rename to applications/external/dtmf_dolphin/pics/dialer.jpg diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene.c b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.c similarity index 100% rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene.c rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.c diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene.h b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.h similarity index 100% rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene.h rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.h diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h similarity index 100% rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c similarity index 100% rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c diff --git a/applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c b/applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c similarity index 100% rename from applications/plugins/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c rename to applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c diff --git a/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_common.h b/applications/external/dtmf_dolphin/views/dtmf_dolphin_common.h similarity index 100% rename from applications/plugins/dtmf_dolphin/views/dtmf_dolphin_common.h rename to applications/external/dtmf_dolphin/views/dtmf_dolphin_common.h diff --git a/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.c b/applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.c similarity index 100% rename from applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.c rename to applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.c diff --git a/applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.h b/applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.h similarity index 100% rename from applications/plugins/dtmf_dolphin/views/dtmf_dolphin_dialer.h rename to applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.h diff --git a/applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h b/applications/external/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h similarity index 100% rename from applications/plugins/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h rename to applications/external/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h diff --git a/applications/plugins/esp8266_deauth/application.fam b/applications/external/esp8266_deauth/application.fam similarity index 100% rename from applications/plugins/esp8266_deauth/application.fam rename to applications/external/esp8266_deauth/application.fam diff --git a/applications/plugins/esp8266_deauth/esp8266_deauth.c b/applications/external/esp8266_deauth/esp8266_deauth.c similarity index 100% rename from applications/plugins/esp8266_deauth/esp8266_deauth.c rename to applications/external/esp8266_deauth/esp8266_deauth.c diff --git a/applications/plugins/esp8266_deauth/wifi_10px.png b/applications/external/esp8266_deauth/wifi_10px.png similarity index 100% rename from applications/plugins/esp8266_deauth/wifi_10px.png rename to applications/external/esp8266_deauth/wifi_10px.png diff --git a/applications/plugins/flappy_bird/application.fam b/applications/external/flappy_bird/application.fam similarity index 100% rename from applications/plugins/flappy_bird/application.fam rename to applications/external/flappy_bird/application.fam diff --git a/applications/plugins/flappy_bird/assets/bird/frame_01.png b/applications/external/flappy_bird/assets/bird/frame_01.png similarity index 100% rename from applications/plugins/flappy_bird/assets/bird/frame_01.png rename to applications/external/flappy_bird/assets/bird/frame_01.png diff --git a/applications/plugins/flappy_bird/assets/bird/frame_02.png b/applications/external/flappy_bird/assets/bird/frame_02.png similarity index 100% rename from applications/plugins/flappy_bird/assets/bird/frame_02.png rename to applications/external/flappy_bird/assets/bird/frame_02.png diff --git a/applications/plugins/flappy_bird/assets/bird/frame_03.png b/applications/external/flappy_bird/assets/bird/frame_03.png similarity index 100% rename from applications/plugins/flappy_bird/assets/bird/frame_03.png rename to applications/external/flappy_bird/assets/bird/frame_03.png diff --git a/applications/plugins/flappy_bird/assets/bird/frame_rate b/applications/external/flappy_bird/assets/bird/frame_rate similarity index 100% rename from applications/plugins/flappy_bird/assets/bird/frame_rate rename to applications/external/flappy_bird/assets/bird/frame_rate diff --git a/applications/plugins/flappy_bird/flappy_10px.png b/applications/external/flappy_bird/flappy_10px.png similarity index 100% rename from applications/plugins/flappy_bird/flappy_10px.png rename to applications/external/flappy_bird/flappy_10px.png diff --git a/applications/plugins/flappy_bird/flappy_bird.c b/applications/external/flappy_bird/flappy_bird.c similarity index 100% rename from applications/plugins/flappy_bird/flappy_bird.c rename to applications/external/flappy_bird/flappy_bird.c diff --git a/applications/plugins/flipfrid/LICENSE.md b/applications/external/flipfrid/LICENSE.md similarity index 100% rename from applications/plugins/flipfrid/LICENSE.md rename to applications/external/flipfrid/LICENSE.md diff --git a/applications/plugins/flipfrid/README.md b/applications/external/flipfrid/README.md similarity index 100% rename from applications/plugins/flipfrid/README.md rename to applications/external/flipfrid/README.md diff --git a/applications/plugins/flipfrid/application.fam b/applications/external/flipfrid/application.fam similarity index 100% rename from applications/plugins/flipfrid/application.fam rename to applications/external/flipfrid/application.fam diff --git a/applications/plugins/flipfrid/flipfrid.c b/applications/external/flipfrid/flipfrid.c similarity index 100% rename from applications/plugins/flipfrid/flipfrid.c rename to applications/external/flipfrid/flipfrid.c diff --git a/applications/plugins/flipfrid/flipfrid.h b/applications/external/flipfrid/flipfrid.h similarity index 100% rename from applications/plugins/flipfrid/flipfrid.h rename to applications/external/flipfrid/flipfrid.h diff --git a/applications/plugins/flipfrid/images/125_10px.png b/applications/external/flipfrid/images/125_10px.png similarity index 100% rename from applications/plugins/flipfrid/images/125_10px.png rename to applications/external/flipfrid/images/125_10px.png diff --git a/applications/plugins/flipfrid/rfid_10px.png b/applications/external/flipfrid/rfid_10px.png similarity index 100% rename from applications/plugins/flipfrid/rfid_10px.png rename to applications/external/flipfrid/rfid_10px.png diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.c rename to applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.h b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_entrypoint.h rename to applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.c rename to applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.h b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_load_custom_uids.h rename to applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c b/applications/external/flipfrid/scene/flipfrid_scene_load_file.c similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_load_file.c rename to applications/external/flipfrid/scene/flipfrid_scene_load_file.c diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_load_file.h b/applications/external/flipfrid/scene/flipfrid_scene_load_file.h similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_load_file.h rename to applications/external/flipfrid/scene/flipfrid_scene_load_file.h diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.c rename to applications/external/flipfrid/scene/flipfrid_scene_run_attack.c diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.h b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_run_attack.h rename to applications/external/flipfrid/scene/flipfrid_scene_run_attack.h diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_select_field.c b/applications/external/flipfrid/scene/flipfrid_scene_select_field.c similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_select_field.c rename to applications/external/flipfrid/scene/flipfrid_scene_select_field.c diff --git a/applications/plugins/flipfrid/scene/flipfrid_scene_select_field.h b/applications/external/flipfrid/scene/flipfrid_scene_select_field.h similarity index 100% rename from applications/plugins/flipfrid/scene/flipfrid_scene_select_field.h rename to applications/external/flipfrid/scene/flipfrid_scene_select_field.h diff --git a/applications/plugins/flipper_i2ctools/README.md b/applications/external/flipper_i2ctools/README.md similarity index 100% rename from applications/plugins/flipper_i2ctools/README.md rename to applications/external/flipper_i2ctools/README.md diff --git a/applications/plugins/flipper_i2ctools/application.fam b/applications/external/flipper_i2ctools/application.fam similarity index 100% rename from applications/plugins/flipper_i2ctools/application.fam rename to applications/external/flipper_i2ctools/application.fam diff --git a/applications/plugins/flipper_i2ctools/i2cscanner.c b/applications/external/flipper_i2ctools/i2cscanner.c similarity index 100% rename from applications/plugins/flipper_i2ctools/i2cscanner.c rename to applications/external/flipper_i2ctools/i2cscanner.c diff --git a/applications/plugins/flipper_i2ctools/i2cscanner.h b/applications/external/flipper_i2ctools/i2cscanner.h similarity index 100% rename from applications/plugins/flipper_i2ctools/i2cscanner.h rename to applications/external/flipper_i2ctools/i2cscanner.h diff --git a/applications/plugins/flipper_i2ctools/i2csender.c b/applications/external/flipper_i2ctools/i2csender.c similarity index 100% rename from applications/plugins/flipper_i2ctools/i2csender.c rename to applications/external/flipper_i2ctools/i2csender.c diff --git a/applications/plugins/flipper_i2ctools/i2csender.h b/applications/external/flipper_i2ctools/i2csender.h similarity index 100% rename from applications/plugins/flipper_i2ctools/i2csender.h rename to applications/external/flipper_i2ctools/i2csender.h diff --git a/applications/plugins/flipper_i2ctools/i2csniffer.c b/applications/external/flipper_i2ctools/i2csniffer.c similarity index 100% rename from applications/plugins/flipper_i2ctools/i2csniffer.c rename to applications/external/flipper_i2ctools/i2csniffer.c diff --git a/applications/plugins/flipper_i2ctools/i2csniffer.h b/applications/external/flipper_i2ctools/i2csniffer.h similarity index 100% rename from applications/plugins/flipper_i2ctools/i2csniffer.h rename to applications/external/flipper_i2ctools/i2csniffer.h diff --git a/applications/plugins/flipper_i2ctools/i2ctools.c b/applications/external/flipper_i2ctools/i2ctools.c similarity index 100% rename from applications/plugins/flipper_i2ctools/i2ctools.c rename to applications/external/flipper_i2ctools/i2ctools.c diff --git a/applications/plugins/flipper_i2ctools/i2ctools.gif b/applications/external/flipper_i2ctools/i2ctools.gif similarity index 100% rename from applications/plugins/flipper_i2ctools/i2ctools.gif rename to applications/external/flipper_i2ctools/i2ctools.gif diff --git a/applications/plugins/flipper_i2ctools/i2ctools.png b/applications/external/flipper_i2ctools/i2ctools.png similarity index 100% rename from applications/plugins/flipper_i2ctools/i2ctools.png rename to applications/external/flipper_i2ctools/i2ctools.png diff --git a/applications/plugins/flipper_i2ctools/i2ctools_i.h b/applications/external/flipper_i2ctools/i2ctools_i.h similarity index 100% rename from applications/plugins/flipper_i2ctools/i2ctools_i.h rename to applications/external/flipper_i2ctools/i2ctools_i.h diff --git a/applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png b/applications/external/flipper_i2ctools/images/ButtonDown_7x4.png similarity index 100% rename from applications/plugins/flipper_i2ctools/images/ButtonDown_7x4.png rename to applications/external/flipper_i2ctools/images/ButtonDown_7x4.png diff --git a/applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png b/applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png similarity index 100% rename from applications/plugins/flipper_i2ctools/images/ButtonLeft_4x7.png rename to applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png diff --git a/applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png b/applications/external/flipper_i2ctools/images/ButtonRight_4x7.png similarity index 100% rename from applications/plugins/flipper_i2ctools/images/ButtonRight_4x7.png rename to applications/external/flipper_i2ctools/images/ButtonRight_4x7.png diff --git a/applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png b/applications/external/flipper_i2ctools/images/ButtonUp_7x4.png similarity index 100% rename from applications/plugins/flipper_i2ctools/images/ButtonUp_7x4.png rename to applications/external/flipper_i2ctools/images/ButtonUp_7x4.png diff --git a/applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png b/applications/external/flipper_i2ctools/images/Ok_btn_9x9.png similarity index 100% rename from applications/plugins/flipper_i2ctools/images/Ok_btn_9x9.png rename to applications/external/flipper_i2ctools/images/Ok_btn_9x9.png diff --git a/applications/plugins/flipper_i2ctools/images/i2ctools_main_76x59.png b/applications/external/flipper_i2ctools/images/i2ctools_main_76x59.png similarity index 100% rename from applications/plugins/flipper_i2ctools/images/i2ctools_main_76x59.png rename to applications/external/flipper_i2ctools/images/i2ctools_main_76x59.png diff --git a/applications/plugins/flipper_i2ctools/views/main_view.c b/applications/external/flipper_i2ctools/views/main_view.c similarity index 100% rename from applications/plugins/flipper_i2ctools/views/main_view.c rename to applications/external/flipper_i2ctools/views/main_view.c diff --git a/applications/plugins/flipper_i2ctools/views/main_view.h b/applications/external/flipper_i2ctools/views/main_view.h similarity index 100% rename from applications/plugins/flipper_i2ctools/views/main_view.h rename to applications/external/flipper_i2ctools/views/main_view.h diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.c b/applications/external/flipper_i2ctools/views/scanner_view.c similarity index 100% rename from applications/plugins/flipper_i2ctools/views/scanner_view.c rename to applications/external/flipper_i2ctools/views/scanner_view.c diff --git a/applications/plugins/flipper_i2ctools/views/scanner_view.h b/applications/external/flipper_i2ctools/views/scanner_view.h similarity index 100% rename from applications/plugins/flipper_i2ctools/views/scanner_view.h rename to applications/external/flipper_i2ctools/views/scanner_view.h diff --git a/applications/plugins/flipper_i2ctools/views/sender_view.c b/applications/external/flipper_i2ctools/views/sender_view.c similarity index 100% rename from applications/plugins/flipper_i2ctools/views/sender_view.c rename to applications/external/flipper_i2ctools/views/sender_view.c diff --git a/applications/plugins/flipper_i2ctools/views/sender_view.h b/applications/external/flipper_i2ctools/views/sender_view.h similarity index 100% rename from applications/plugins/flipper_i2ctools/views/sender_view.h rename to applications/external/flipper_i2ctools/views/sender_view.h diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.c b/applications/external/flipper_i2ctools/views/sniffer_view.c similarity index 100% rename from applications/plugins/flipper_i2ctools/views/sniffer_view.c rename to applications/external/flipper_i2ctools/views/sniffer_view.c diff --git a/applications/plugins/flipper_i2ctools/views/sniffer_view.h b/applications/external/flipper_i2ctools/views/sniffer_view.h similarity index 100% rename from applications/plugins/flipper_i2ctools/views/sniffer_view.h rename to applications/external/flipper_i2ctools/views/sniffer_view.h diff --git a/applications/plugins/game15/README.md b/applications/external/game15/README.md similarity index 100% rename from applications/plugins/game15/README.md rename to applications/external/game15/README.md diff --git a/applications/plugins/game15/application.fam b/applications/external/game15/application.fam similarity index 100% rename from applications/plugins/game15/application.fam rename to applications/external/game15/application.fam diff --git a/applications/plugins/game15/game15.c b/applications/external/game15/game15.c similarity index 100% rename from applications/plugins/game15/game15.c rename to applications/external/game15/game15.c diff --git a/applications/plugins/game15/game15_10px.png b/applications/external/game15/game15_10px.png similarity index 100% rename from applications/plugins/game15/game15_10px.png rename to applications/external/game15/game15_10px.png diff --git a/applications/plugins/game15/images/Game15.png b/applications/external/game15/images/Game15.png similarity index 100% rename from applications/plugins/game15/images/Game15.png rename to applications/external/game15/images/Game15.png diff --git a/applications/plugins/game15/images/Game15Popup.png b/applications/external/game15/images/Game15Popup.png similarity index 100% rename from applications/plugins/game15/images/Game15Popup.png rename to applications/external/game15/images/Game15Popup.png diff --git a/applications/plugins/game15/images/Game15Restore.png b/applications/external/game15/images/Game15Restore.png similarity index 100% rename from applications/plugins/game15/images/Game15Restore.png rename to applications/external/game15/images/Game15Restore.png diff --git a/applications/plugins/game15/sandbox.c b/applications/external/game15/sandbox.c similarity index 100% rename from applications/plugins/game15/sandbox.c rename to applications/external/game15/sandbox.c diff --git a/applications/plugins/game15/sandbox.h b/applications/external/game15/sandbox.h similarity index 100% rename from applications/plugins/game15/sandbox.h rename to applications/external/game15/sandbox.h diff --git a/applications/plugins/game_2048/LICENSE b/applications/external/game_2048/LICENSE similarity index 100% rename from applications/plugins/game_2048/LICENSE rename to applications/external/game_2048/LICENSE diff --git a/applications/plugins/game_2048/README.md b/applications/external/game_2048/README.md similarity index 100% rename from applications/plugins/game_2048/README.md rename to applications/external/game_2048/README.md diff --git a/applications/plugins/game_2048/application.fam b/applications/external/game_2048/application.fam similarity index 100% rename from applications/plugins/game_2048/application.fam rename to applications/external/game_2048/application.fam diff --git a/applications/plugins/game_2048/array_utils.c b/applications/external/game_2048/array_utils.c similarity index 100% rename from applications/plugins/game_2048/array_utils.c rename to applications/external/game_2048/array_utils.c diff --git a/applications/plugins/game_2048/array_utils.h b/applications/external/game_2048/array_utils.h similarity index 100% rename from applications/plugins/game_2048/array_utils.h rename to applications/external/game_2048/array_utils.h diff --git a/applications/plugins/game_2048/digits.h b/applications/external/game_2048/digits.h similarity index 100% rename from applications/plugins/game_2048/digits.h rename to applications/external/game_2048/digits.h diff --git a/applications/plugins/game_2048/game_2048.c b/applications/external/game_2048/game_2048.c similarity index 100% rename from applications/plugins/game_2048/game_2048.c rename to applications/external/game_2048/game_2048.c diff --git a/applications/plugins/game_2048/game_2048.png b/applications/external/game_2048/game_2048.png similarity index 100% rename from applications/plugins/game_2048/game_2048.png rename to applications/external/game_2048/game_2048.png diff --git a/applications/plugins/game_2048/images/screenshot1.png b/applications/external/game_2048/images/screenshot1.png similarity index 100% rename from applications/plugins/game_2048/images/screenshot1.png rename to applications/external/game_2048/images/screenshot1.png diff --git a/applications/plugins/game_2048/images/screenshot2.png b/applications/external/game_2048/images/screenshot2.png similarity index 100% rename from applications/plugins/game_2048/images/screenshot2.png rename to applications/external/game_2048/images/screenshot2.png diff --git a/applications/plugins/gps_nmea_uart/LICENSE b/applications/external/gps_nmea_uart/LICENSE similarity index 100% rename from applications/plugins/gps_nmea_uart/LICENSE rename to applications/external/gps_nmea_uart/LICENSE diff --git a/applications/plugins/gps_nmea_uart/README.md b/applications/external/gps_nmea_uart/README.md similarity index 100% rename from applications/plugins/gps_nmea_uart/README.md rename to applications/external/gps_nmea_uart/README.md diff --git a/applications/plugins/gps_nmea_uart/application.fam b/applications/external/gps_nmea_uart/application.fam similarity index 100% rename from applications/plugins/gps_nmea_uart/application.fam rename to applications/external/gps_nmea_uart/application.fam diff --git a/applications/plugins/gps_nmea_uart/gps.c b/applications/external/gps_nmea_uart/gps.c similarity index 100% rename from applications/plugins/gps_nmea_uart/gps.c rename to applications/external/gps_nmea_uart/gps.c diff --git a/applications/plugins/gps_nmea_uart/gps_10px.png b/applications/external/gps_nmea_uart/gps_10px.png similarity index 100% rename from applications/plugins/gps_nmea_uart/gps_10px.png rename to applications/external/gps_nmea_uart/gps_10px.png diff --git a/applications/plugins/gps_nmea_uart/gps_uart.c b/applications/external/gps_nmea_uart/gps_uart.c similarity index 100% rename from applications/plugins/gps_nmea_uart/gps_uart.c rename to applications/external/gps_nmea_uart/gps_uart.c diff --git a/applications/plugins/gps_nmea_uart/gps_uart.h b/applications/external/gps_nmea_uart/gps_uart.h similarity index 100% rename from applications/plugins/gps_nmea_uart/gps_uart.h rename to applications/external/gps_nmea_uart/gps_uart.h diff --git a/applications/plugins/gps_nmea_uart/minmea.c b/applications/external/gps_nmea_uart/minmea.c similarity index 100% rename from applications/plugins/gps_nmea_uart/minmea.c rename to applications/external/gps_nmea_uart/minmea.c diff --git a/applications/plugins/gps_nmea_uart/minmea.h b/applications/external/gps_nmea_uart/minmea.h similarity index 100% rename from applications/plugins/gps_nmea_uart/minmea.h rename to applications/external/gps_nmea_uart/minmea.h diff --git a/applications/plugins/gps_nmea_uart/ui.png b/applications/external/gps_nmea_uart/ui.png similarity index 100% rename from applications/plugins/gps_nmea_uart/ui.png rename to applications/external/gps_nmea_uart/ui.png diff --git a/applications/plugins/gps_nmea_uart/wiring.png b/applications/external/gps_nmea_uart/wiring.png similarity index 100% rename from applications/plugins/gps_nmea_uart/wiring.png rename to applications/external/gps_nmea_uart/wiring.png diff --git a/applications/plugins/hc_sr04/application.fam b/applications/external/hc_sr04/application.fam similarity index 100% rename from applications/plugins/hc_sr04/application.fam rename to applications/external/hc_sr04/application.fam diff --git a/applications/plugins/hc_sr04/dist_sensor10px.png b/applications/external/hc_sr04/dist_sensor10px.png similarity index 100% rename from applications/plugins/hc_sr04/dist_sensor10px.png rename to applications/external/hc_sr04/dist_sensor10px.png diff --git a/applications/plugins/hc_sr04/hc_sr04.c b/applications/external/hc_sr04/hc_sr04.c similarity index 100% rename from applications/plugins/hc_sr04/hc_sr04.c rename to applications/external/hc_sr04/hc_sr04.c diff --git a/applications/plugins/heap_defence_game/application.fam b/applications/external/heap_defence_game/application.fam similarity index 100% rename from applications/plugins/heap_defence_game/application.fam rename to applications/external/heap_defence_game/application.fam diff --git a/applications/plugins/heap_defence_game/assets_images/Background_128x64.png b/applications/external/heap_defence_game/assets_images/Background_128x64.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Background_128x64.png rename to applications/external/heap_defence_game/assets_images/Background_128x64.png diff --git a/applications/plugins/heap_defence_game/assets_images/Box1_10x10.png b/applications/external/heap_defence_game/assets_images/Box1_10x10.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Box1_10x10.png rename to applications/external/heap_defence_game/assets_images/Box1_10x10.png diff --git a/applications/plugins/heap_defence_game/assets_images/Box2_10x10.png b/applications/external/heap_defence_game/assets_images/Box2_10x10.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Box2_10x10.png rename to applications/external/heap_defence_game/assets_images/Box2_10x10.png diff --git a/applications/plugins/heap_defence_game/assets_images/Box3_10x10.png b/applications/external/heap_defence_game/assets_images/Box3_10x10.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Box3_10x10.png rename to applications/external/heap_defence_game/assets_images/Box3_10x10.png diff --git a/applications/plugins/heap_defence_game/assets_images/Box4_10x10.png b/applications/external/heap_defence_game/assets_images/Box4_10x10.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Box4_10x10.png rename to applications/external/heap_defence_game/assets_images/Box4_10x10.png diff --git a/applications/plugins/heap_defence_game/assets_images/Box5_10x10.png b/applications/external/heap_defence_game/assets_images/Box5_10x10.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Box5_10x10.png rename to applications/external/heap_defence_game/assets_images/Box5_10x10.png diff --git a/applications/plugins/heap_defence_game/assets_images/Box6p_10x10.png b/applications/external/heap_defence_game/assets_images/Box6p_10x10.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Box6p_10x10.png rename to applications/external/heap_defence_game/assets_images/Box6p_10x10.png diff --git a/applications/plugins/heap_defence_game/assets_images/Box7p_10x10.png b/applications/external/heap_defence_game/assets_images/Box7p_10x10.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Box7p_10x10.png rename to applications/external/heap_defence_game/assets_images/Box7p_10x10.png diff --git a/applications/plugins/heap_defence_game/assets_images/Box8p_10x10.png b/applications/external/heap_defence_game/assets_images/Box8p_10x10.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Box8p_10x10.png rename to applications/external/heap_defence_game/assets_images/Box8p_10x10.png diff --git a/applications/plugins/heap_defence_game/assets_images/Game_over_128x64.png b/applications/external/heap_defence_game/assets_images/Game_over_128x64.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Game_over_128x64.png rename to applications/external/heap_defence_game/assets_images/Game_over_128x64.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate b/applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate rename to applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png rename to applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png rename to applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate b/applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate rename to applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png rename to applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png rename to applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate b/applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate rename to applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate b/applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate rename to applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate b/applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate rename to applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_01.png b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_01.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_01.png rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_01.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_02.png b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_02.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_02.png rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_02.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_03.png b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_03.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_03.png rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_03.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_04.png b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_04.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_04.png rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_04.png diff --git a/applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_rate b/applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_rate similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/HD_start_128x64/frame_rate rename to applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_rate diff --git a/applications/plugins/heap_defence_game/assets_images/Person4_1_10x20.png b/applications/external/heap_defence_game/assets_images/Person4_1_10x20.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Person4_1_10x20.png rename to applications/external/heap_defence_game/assets_images/Person4_1_10x20.png diff --git a/applications/plugins/heap_defence_game/assets_images/Person4_2_10x20.png b/applications/external/heap_defence_game/assets_images/Person4_2_10x20.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Person4_2_10x20.png rename to applications/external/heap_defence_game/assets_images/Person4_2_10x20.png diff --git a/applications/plugins/heap_defence_game/assets_images/Person5_1_10x20.png b/applications/external/heap_defence_game/assets_images/Person5_1_10x20.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Person5_1_10x20.png rename to applications/external/heap_defence_game/assets_images/Person5_1_10x20.png diff --git a/applications/plugins/heap_defence_game/assets_images/Person5_2_10x20.png b/applications/external/heap_defence_game/assets_images/Person5_2_10x20.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Person5_2_10x20.png rename to applications/external/heap_defence_game/assets_images/Person5_2_10x20.png diff --git a/applications/plugins/heap_defence_game/assets_images/Start_128x64.png b/applications/external/heap_defence_game/assets_images/Start_128x64.png similarity index 100% rename from applications/plugins/heap_defence_game/assets_images/Start_128x64.png rename to applications/external/heap_defence_game/assets_images/Start_128x64.png diff --git a/applications/plugins/heap_defence_game/box.png b/applications/external/heap_defence_game/box.png similarity index 100% rename from applications/plugins/heap_defence_game/box.png rename to applications/external/heap_defence_game/box.png diff --git a/applications/plugins/heap_defence_game/heap_defence.c b/applications/external/heap_defence_game/heap_defence.c similarity index 100% rename from applications/plugins/heap_defence_game/heap_defence.c rename to applications/external/heap_defence_game/heap_defence.c diff --git a/applications/plugins/heap_defence_game/hede_assets.c b/applications/external/heap_defence_game/hede_assets.c similarity index 100% rename from applications/plugins/heap_defence_game/hede_assets.c rename to applications/external/heap_defence_game/hede_assets.c diff --git a/applications/plugins/heap_defence_game/hede_assets.h b/applications/external/heap_defence_game/hede_assets.h similarity index 100% rename from applications/plugins/heap_defence_game/hede_assets.h rename to applications/external/heap_defence_game/hede_assets.h diff --git a/applications/plugins/hex_viewer/LICENSE b/applications/external/hex_viewer/LICENSE similarity index 100% rename from applications/plugins/hex_viewer/LICENSE rename to applications/external/hex_viewer/LICENSE diff --git a/applications/plugins/hex_viewer/application.fam b/applications/external/hex_viewer/application.fam similarity index 100% rename from applications/plugins/hex_viewer/application.fam rename to applications/external/hex_viewer/application.fam diff --git a/applications/plugins/hex_viewer/hex_viewer.c b/applications/external/hex_viewer/hex_viewer.c similarity index 100% rename from applications/plugins/hex_viewer/hex_viewer.c rename to applications/external/hex_viewer/hex_viewer.c diff --git a/applications/plugins/hex_viewer/icons/hex_10px.png b/applications/external/hex_viewer/icons/hex_10px.png similarity index 100% rename from applications/plugins/hex_viewer/icons/hex_10px.png rename to applications/external/hex_viewer/icons/hex_10px.png diff --git a/applications/plugins/ibtn_fuzzer/LICENSE.md b/applications/external/ibtn_fuzzer/LICENSE.md similarity index 100% rename from applications/plugins/ibtn_fuzzer/LICENSE.md rename to applications/external/ibtn_fuzzer/LICENSE.md diff --git a/applications/plugins/ibtn_fuzzer/application.fam b/applications/external/ibtn_fuzzer/application.fam similarity index 100% rename from applications/plugins/ibtn_fuzzer/application.fam rename to applications/external/ibtn_fuzzer/application.fam diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.c b/applications/external/ibtn_fuzzer/ibtnfuzzer.c similarity index 100% rename from applications/plugins/ibtn_fuzzer/ibtnfuzzer.c rename to applications/external/ibtn_fuzzer/ibtnfuzzer.c diff --git a/applications/plugins/ibtn_fuzzer/ibtnfuzzer.h b/applications/external/ibtn_fuzzer/ibtnfuzzer.h similarity index 100% rename from applications/plugins/ibtn_fuzzer/ibtnfuzzer.h rename to applications/external/ibtn_fuzzer/ibtnfuzzer.h diff --git a/applications/plugins/ibtn_fuzzer/ibutt_10px.png b/applications/external/ibtn_fuzzer/ibutt_10px.png similarity index 100% rename from applications/plugins/ibtn_fuzzer/ibutt_10px.png rename to applications/external/ibtn_fuzzer/ibutt_10px.png diff --git a/applications/plugins/ibtn_fuzzer/images/ibutt_10px.png b/applications/external/ibtn_fuzzer/images/ibutt_10px.png similarity index 100% rename from applications/plugins/ibtn_fuzzer/images/ibutt_10px.png rename to applications/external/ibtn_fuzzer/images/ibutt_10px.png diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c diff --git a/applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h similarity index 100% rename from applications/plugins/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h rename to applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h diff --git a/applications/plugins/lightmeter/LICENSE b/applications/external/lightmeter/LICENSE similarity index 100% rename from applications/plugins/lightmeter/LICENSE rename to applications/external/lightmeter/LICENSE diff --git a/applications/plugins/lightmeter/README.md b/applications/external/lightmeter/README.md similarity index 100% rename from applications/plugins/lightmeter/README.md rename to applications/external/lightmeter/README.md diff --git a/applications/plugins/lightmeter/application.fam b/applications/external/lightmeter/application.fam similarity index 100% rename from applications/plugins/lightmeter/application.fam rename to applications/external/lightmeter/application.fam diff --git a/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.c b/applications/external/lightmeter/gui/scenes/config/lightmeter_scene.c similarity index 100% rename from applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.c rename to applications/external/lightmeter/gui/scenes/config/lightmeter_scene.c diff --git a/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.h b/applications/external/lightmeter/gui/scenes/config/lightmeter_scene.h similarity index 100% rename from applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene.h rename to applications/external/lightmeter/gui/scenes/config/lightmeter_scene.h diff --git a/applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene_config.h b/applications/external/lightmeter/gui/scenes/config/lightmeter_scene_config.h similarity index 100% rename from applications/plugins/lightmeter/gui/scenes/config/lightmeter_scene_config.h rename to applications/external/lightmeter/gui/scenes/config/lightmeter_scene_config.h diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_about.c b/applications/external/lightmeter/gui/scenes/lightmeter_scene_about.c similarity index 100% rename from applications/plugins/lightmeter/gui/scenes/lightmeter_scene_about.c rename to applications/external/lightmeter/gui/scenes/lightmeter_scene_about.c diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_config.c b/applications/external/lightmeter/gui/scenes/lightmeter_scene_config.c similarity index 100% rename from applications/plugins/lightmeter/gui/scenes/lightmeter_scene_config.c rename to applications/external/lightmeter/gui/scenes/lightmeter_scene_config.c diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_help.c b/applications/external/lightmeter/gui/scenes/lightmeter_scene_help.c similarity index 100% rename from applications/plugins/lightmeter/gui/scenes/lightmeter_scene_help.c rename to applications/external/lightmeter/gui/scenes/lightmeter_scene_help.c diff --git a/applications/plugins/lightmeter/gui/scenes/lightmeter_scene_main.c b/applications/external/lightmeter/gui/scenes/lightmeter_scene_main.c similarity index 100% rename from applications/plugins/lightmeter/gui/scenes/lightmeter_scene_main.c rename to applications/external/lightmeter/gui/scenes/lightmeter_scene_main.c diff --git a/applications/plugins/lightmeter/gui/views/main_view.c b/applications/external/lightmeter/gui/views/main_view.c similarity index 100% rename from applications/plugins/lightmeter/gui/views/main_view.c rename to applications/external/lightmeter/gui/views/main_view.c diff --git a/applications/plugins/lightmeter/gui/views/main_view.h b/applications/external/lightmeter/gui/views/main_view.h similarity index 100% rename from applications/plugins/lightmeter/gui/views/main_view.h rename to applications/external/lightmeter/gui/views/main_view.h diff --git a/applications/plugins/lightmeter/icons/T_10x14.png b/applications/external/lightmeter/icons/T_10x14.png similarity index 100% rename from applications/plugins/lightmeter/icons/T_10x14.png rename to applications/external/lightmeter/icons/T_10x14.png diff --git a/applications/plugins/lightmeter/icons/f_10x14.png b/applications/external/lightmeter/icons/f_10x14.png similarity index 100% rename from applications/plugins/lightmeter/icons/f_10x14.png rename to applications/external/lightmeter/icons/f_10x14.png diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.c b/applications/external/lightmeter/lib/BH1750/BH1750.c similarity index 100% rename from applications/plugins/lightmeter/lib/BH1750/BH1750.c rename to applications/external/lightmeter/lib/BH1750/BH1750.c diff --git a/applications/plugins/lightmeter/lib/BH1750/BH1750.h b/applications/external/lightmeter/lib/BH1750/BH1750.h similarity index 100% rename from applications/plugins/lightmeter/lib/BH1750/BH1750.h rename to applications/external/lightmeter/lib/BH1750/BH1750.h diff --git a/applications/plugins/lightmeter/lib/BH1750/LICENSE b/applications/external/lightmeter/lib/BH1750/LICENSE similarity index 100% rename from applications/plugins/lightmeter/lib/BH1750/LICENSE rename to applications/external/lightmeter/lib/BH1750/LICENSE diff --git a/applications/plugins/lightmeter/lib/BH1750/README.md b/applications/external/lightmeter/lib/BH1750/README.md similarity index 100% rename from applications/plugins/lightmeter/lib/BH1750/README.md rename to applications/external/lightmeter/lib/BH1750/README.md diff --git a/applications/plugins/lightmeter/lib/BH1750/docs/BH1750.pdf b/applications/external/lightmeter/lib/BH1750/docs/BH1750.pdf similarity index 100% rename from applications/plugins/lightmeter/lib/BH1750/docs/BH1750.pdf rename to applications/external/lightmeter/lib/BH1750/docs/BH1750.pdf diff --git a/applications/plugins/lightmeter/lightmeter.c b/applications/external/lightmeter/lightmeter.c similarity index 100% rename from applications/plugins/lightmeter/lightmeter.c rename to applications/external/lightmeter/lightmeter.c diff --git a/applications/plugins/lightmeter/lightmeter.h b/applications/external/lightmeter/lightmeter.h similarity index 100% rename from applications/plugins/lightmeter/lightmeter.h rename to applications/external/lightmeter/lightmeter.h diff --git a/applications/plugins/lightmeter/lightmeter.png b/applications/external/lightmeter/lightmeter.png similarity index 100% rename from applications/plugins/lightmeter/lightmeter.png rename to applications/external/lightmeter/lightmeter.png diff --git a/applications/plugins/lightmeter/lightmeter_config.h b/applications/external/lightmeter/lightmeter_config.h similarity index 100% rename from applications/plugins/lightmeter/lightmeter_config.h rename to applications/external/lightmeter/lightmeter_config.h diff --git a/applications/plugins/lightmeter/lightmeter_helper.c b/applications/external/lightmeter/lightmeter_helper.c similarity index 100% rename from applications/plugins/lightmeter/lightmeter_helper.c rename to applications/external/lightmeter/lightmeter_helper.c diff --git a/applications/plugins/lightmeter/lightmeter_helper.h b/applications/external/lightmeter/lightmeter_helper.h similarity index 100% rename from applications/plugins/lightmeter/lightmeter_helper.h rename to applications/external/lightmeter/lightmeter_helper.h diff --git a/applications/plugins/metronome/README.md b/applications/external/metronome/README.md similarity index 100% rename from applications/plugins/metronome/README.md rename to applications/external/metronome/README.md diff --git a/applications/plugins/metronome/application.fam b/applications/external/metronome/application.fam similarity index 100% rename from applications/plugins/metronome/application.fam rename to applications/external/metronome/application.fam diff --git a/applications/plugins/metronome/gui_extensions.c b/applications/external/metronome/gui_extensions.c similarity index 100% rename from applications/plugins/metronome/gui_extensions.c rename to applications/external/metronome/gui_extensions.c diff --git a/applications/plugins/metronome/gui_extensions.h b/applications/external/metronome/gui_extensions.h similarity index 100% rename from applications/plugins/metronome/gui_extensions.h rename to applications/external/metronome/gui_extensions.h diff --git a/applications/plugins/metronome/images/ButtonUp_7x4.png b/applications/external/metronome/images/ButtonUp_7x4.png similarity index 100% rename from applications/plugins/metronome/images/ButtonUp_7x4.png rename to applications/external/metronome/images/ButtonUp_7x4.png diff --git a/applications/plugins/metronome/img/screenshot.png b/applications/external/metronome/img/screenshot.png similarity index 100% rename from applications/plugins/metronome/img/screenshot.png rename to applications/external/metronome/img/screenshot.png diff --git a/applications/plugins/metronome/img/wave_left_4x14.png b/applications/external/metronome/img/wave_left_4x14.png similarity index 100% rename from applications/plugins/metronome/img/wave_left_4x14.png rename to applications/external/metronome/img/wave_left_4x14.png diff --git a/applications/plugins/metronome/img/wave_right_4x14.png b/applications/external/metronome/img/wave_right_4x14.png similarity index 100% rename from applications/plugins/metronome/img/wave_right_4x14.png rename to applications/external/metronome/img/wave_right_4x14.png diff --git a/applications/plugins/metronome/metronome.c b/applications/external/metronome/metronome.c similarity index 100% rename from applications/plugins/metronome/metronome.c rename to applications/external/metronome/metronome.c diff --git a/applications/plugins/metronome/metronome_icon.png b/applications/external/metronome/metronome_icon.png similarity index 100% rename from applications/plugins/metronome/metronome_icon.png rename to applications/external/metronome/metronome_icon.png diff --git a/applications/plugins/minesweeper/LICENSE b/applications/external/minesweeper/LICENSE similarity index 100% rename from applications/plugins/minesweeper/LICENSE rename to applications/external/minesweeper/LICENSE diff --git a/applications/plugins/minesweeper/README.md b/applications/external/minesweeper/README.md similarity index 100% rename from applications/plugins/minesweeper/README.md rename to applications/external/minesweeper/README.md diff --git a/applications/plugins/minesweeper/application.fam b/applications/external/minesweeper/application.fam similarity index 100% rename from applications/plugins/minesweeper/application.fam rename to applications/external/minesweeper/application.fam diff --git a/applications/plugins/minesweeper/assets.h b/applications/external/minesweeper/assets.h similarity index 100% rename from applications/plugins/minesweeper/assets.h rename to applications/external/minesweeper/assets.h diff --git a/applications/plugins/minesweeper/assets/asset b/applications/external/minesweeper/assets/asset similarity index 100% rename from applications/plugins/minesweeper/assets/asset rename to applications/external/minesweeper/assets/asset diff --git a/applications/plugins/minesweeper/assets/mockup.png b/applications/external/minesweeper/assets/mockup.png similarity index 100% rename from applications/plugins/minesweeper/assets/mockup.png rename to applications/external/minesweeper/assets/mockup.png diff --git a/applications/plugins/minesweeper/assets/tile_0.png b/applications/external/minesweeper/assets/tile_0.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_0.png rename to applications/external/minesweeper/assets/tile_0.png diff --git a/applications/plugins/minesweeper/assets/tile_0.xbm b/applications/external/minesweeper/assets/tile_0.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_0.xbm rename to applications/external/minesweeper/assets/tile_0.xbm diff --git a/applications/plugins/minesweeper/assets/tile_1.png b/applications/external/minesweeper/assets/tile_1.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_1.png rename to applications/external/minesweeper/assets/tile_1.png diff --git a/applications/plugins/minesweeper/assets/tile_1.xbm b/applications/external/minesweeper/assets/tile_1.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_1.xbm rename to applications/external/minesweeper/assets/tile_1.xbm diff --git a/applications/plugins/minesweeper/assets/tile_2.png b/applications/external/minesweeper/assets/tile_2.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_2.png rename to applications/external/minesweeper/assets/tile_2.png diff --git a/applications/plugins/minesweeper/assets/tile_2.xbm b/applications/external/minesweeper/assets/tile_2.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_2.xbm rename to applications/external/minesweeper/assets/tile_2.xbm diff --git a/applications/plugins/minesweeper/assets/tile_3.png b/applications/external/minesweeper/assets/tile_3.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_3.png rename to applications/external/minesweeper/assets/tile_3.png diff --git a/applications/plugins/minesweeper/assets/tile_3.xbm b/applications/external/minesweeper/assets/tile_3.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_3.xbm rename to applications/external/minesweeper/assets/tile_3.xbm diff --git a/applications/plugins/minesweeper/assets/tile_4.png b/applications/external/minesweeper/assets/tile_4.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_4.png rename to applications/external/minesweeper/assets/tile_4.png diff --git a/applications/plugins/minesweeper/assets/tile_4.xbm b/applications/external/minesweeper/assets/tile_4.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_4.xbm rename to applications/external/minesweeper/assets/tile_4.xbm diff --git a/applications/plugins/minesweeper/assets/tile_5.png b/applications/external/minesweeper/assets/tile_5.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_5.png rename to applications/external/minesweeper/assets/tile_5.png diff --git a/applications/plugins/minesweeper/assets/tile_5.xbm b/applications/external/minesweeper/assets/tile_5.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_5.xbm rename to applications/external/minesweeper/assets/tile_5.xbm diff --git a/applications/plugins/minesweeper/assets/tile_6.png b/applications/external/minesweeper/assets/tile_6.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_6.png rename to applications/external/minesweeper/assets/tile_6.png diff --git a/applications/plugins/minesweeper/assets/tile_6.xbm b/applications/external/minesweeper/assets/tile_6.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_6.xbm rename to applications/external/minesweeper/assets/tile_6.xbm diff --git a/applications/plugins/minesweeper/assets/tile_7.png b/applications/external/minesweeper/assets/tile_7.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_7.png rename to applications/external/minesweeper/assets/tile_7.png diff --git a/applications/plugins/minesweeper/assets/tile_7.xbm b/applications/external/minesweeper/assets/tile_7.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_7.xbm rename to applications/external/minesweeper/assets/tile_7.xbm diff --git a/applications/plugins/minesweeper/assets/tile_8.png b/applications/external/minesweeper/assets/tile_8.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_8.png rename to applications/external/minesweeper/assets/tile_8.png diff --git a/applications/plugins/minesweeper/assets/tile_8.xbm b/applications/external/minesweeper/assets/tile_8.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_8.xbm rename to applications/external/minesweeper/assets/tile_8.xbm diff --git a/applications/plugins/minesweeper/assets/tile_empty.png b/applications/external/minesweeper/assets/tile_empty.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_empty.png rename to applications/external/minesweeper/assets/tile_empty.png diff --git a/applications/plugins/minesweeper/assets/tile_flag.png b/applications/external/minesweeper/assets/tile_flag.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_flag.png rename to applications/external/minesweeper/assets/tile_flag.png diff --git a/applications/plugins/minesweeper/assets/tile_flag.xbm b/applications/external/minesweeper/assets/tile_flag.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_flag.xbm rename to applications/external/minesweeper/assets/tile_flag.xbm diff --git a/applications/plugins/minesweeper/assets/tile_mine.png b/applications/external/minesweeper/assets/tile_mine.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_mine.png rename to applications/external/minesweeper/assets/tile_mine.png diff --git a/applications/plugins/minesweeper/assets/tile_mine.xbm b/applications/external/minesweeper/assets/tile_mine.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_mine.xbm rename to applications/external/minesweeper/assets/tile_mine.xbm diff --git a/applications/plugins/minesweeper/assets/tile_uncleared.png b/applications/external/minesweeper/assets/tile_uncleared.png similarity index 100% rename from applications/plugins/minesweeper/assets/tile_uncleared.png rename to applications/external/minesweeper/assets/tile_uncleared.png diff --git a/applications/plugins/minesweeper/assets/tile_uncleared.xbm b/applications/external/minesweeper/assets/tile_uncleared.xbm similarity index 100% rename from applications/plugins/minesweeper/assets/tile_uncleared.xbm rename to applications/external/minesweeper/assets/tile_uncleared.xbm diff --git a/applications/plugins/minesweeper/img/screenshot.png b/applications/external/minesweeper/img/screenshot.png similarity index 100% rename from applications/plugins/minesweeper/img/screenshot.png rename to applications/external/minesweeper/img/screenshot.png diff --git a/applications/plugins/minesweeper/minesweeper.c b/applications/external/minesweeper/minesweeper.c similarity index 100% rename from applications/plugins/minesweeper/minesweeper.c rename to applications/external/minesweeper/minesweeper.c diff --git a/applications/plugins/minesweeper/minesweeper_icon.png b/applications/external/minesweeper/minesweeper_icon.png similarity index 100% rename from applications/plugins/minesweeper/minesweeper_icon.png rename to applications/external/minesweeper/minesweeper_icon.png diff --git a/applications/plugins/morse_code/application.fam b/applications/external/morse_code/application.fam similarity index 100% rename from applications/plugins/morse_code/application.fam rename to applications/external/morse_code/application.fam diff --git a/applications/plugins/morse_code/morse_code.c b/applications/external/morse_code/morse_code.c similarity index 100% rename from applications/plugins/morse_code/morse_code.c rename to applications/external/morse_code/morse_code.c diff --git a/applications/plugins/morse_code/morse_code_10px.png b/applications/external/morse_code/morse_code_10px.png similarity index 100% rename from applications/plugins/morse_code/morse_code_10px.png rename to applications/external/morse_code/morse_code_10px.png diff --git a/applications/plugins/morse_code/morse_code_worker.c b/applications/external/morse_code/morse_code_worker.c similarity index 100% rename from applications/plugins/morse_code/morse_code_worker.c rename to applications/external/morse_code/morse_code_worker.c diff --git a/applications/plugins/morse_code/morse_code_worker.h b/applications/external/morse_code/morse_code_worker.h similarity index 100% rename from applications/plugins/morse_code/morse_code_worker.h rename to applications/external/morse_code/morse_code_worker.h diff --git a/applications/plugins/mousejacker/application.fam b/applications/external/mousejacker/application.fam similarity index 100% rename from applications/plugins/mousejacker/application.fam rename to applications/external/mousejacker/application.fam diff --git a/applications/plugins/mousejacker/images/badusb_10px.png b/applications/external/mousejacker/images/badusb_10px.png similarity index 100% rename from applications/plugins/mousejacker/images/badusb_10px.png rename to applications/external/mousejacker/images/badusb_10px.png diff --git a/applications/plugins/mousejacker/images/sub1_10px.png b/applications/external/mousejacker/images/sub1_10px.png similarity index 100% rename from applications/plugins/mousejacker/images/sub1_10px.png rename to applications/external/mousejacker/images/sub1_10px.png diff --git a/applications/plugins/mousejacker/lib/nrf24/nrf24.c b/applications/external/mousejacker/lib/nrf24/nrf24.c similarity index 100% rename from applications/plugins/mousejacker/lib/nrf24/nrf24.c rename to applications/external/mousejacker/lib/nrf24/nrf24.c diff --git a/applications/plugins/mousejacker/lib/nrf24/nrf24.h b/applications/external/mousejacker/lib/nrf24/nrf24.h similarity index 100% rename from applications/plugins/mousejacker/lib/nrf24/nrf24.h rename to applications/external/mousejacker/lib/nrf24/nrf24.h diff --git a/applications/plugins/mousejacker/mouse_10px.png b/applications/external/mousejacker/mouse_10px.png similarity index 100% rename from applications/plugins/mousejacker/mouse_10px.png rename to applications/external/mousejacker/mouse_10px.png diff --git a/applications/plugins/mousejacker/mousejacker.c b/applications/external/mousejacker/mousejacker.c similarity index 100% rename from applications/plugins/mousejacker/mousejacker.c rename to applications/external/mousejacker/mousejacker.c diff --git a/applications/plugins/mousejacker/mousejacker_ducky.c b/applications/external/mousejacker/mousejacker_ducky.c similarity index 100% rename from applications/plugins/mousejacker/mousejacker_ducky.c rename to applications/external/mousejacker/mousejacker_ducky.c diff --git a/applications/plugins/mousejacker/mousejacker_ducky.h b/applications/external/mousejacker/mousejacker_ducky.h similarity index 100% rename from applications/plugins/mousejacker/mousejacker_ducky.h rename to applications/external/mousejacker/mousejacker_ducky.h diff --git a/applications/plugins/multi_converter/application.fam b/applications/external/multi_converter/application.fam similarity index 100% rename from applications/plugins/multi_converter/application.fam rename to applications/external/multi_converter/application.fam diff --git a/applications/plugins/multi_converter/converter_10px.png b/applications/external/multi_converter/converter_10px.png similarity index 100% rename from applications/plugins/multi_converter/converter_10px.png rename to applications/external/multi_converter/converter_10px.png diff --git a/applications/plugins/multi_converter/multi_converter.c b/applications/external/multi_converter/multi_converter.c similarity index 100% rename from applications/plugins/multi_converter/multi_converter.c rename to applications/external/multi_converter/multi_converter.c diff --git a/applications/plugins/multi_converter/multi_converter_definitions.h b/applications/external/multi_converter/multi_converter_definitions.h similarity index 100% rename from applications/plugins/multi_converter/multi_converter_definitions.h rename to applications/external/multi_converter/multi_converter_definitions.h diff --git a/applications/plugins/multi_converter/multi_converter_mode_display.c b/applications/external/multi_converter/multi_converter_mode_display.c similarity index 100% rename from applications/plugins/multi_converter/multi_converter_mode_display.c rename to applications/external/multi_converter/multi_converter_mode_display.c diff --git a/applications/plugins/multi_converter/multi_converter_mode_display.h b/applications/external/multi_converter/multi_converter_mode_display.h similarity index 100% rename from applications/plugins/multi_converter/multi_converter_mode_display.h rename to applications/external/multi_converter/multi_converter_mode_display.h diff --git a/applications/plugins/multi_converter/multi_converter_mode_select.c b/applications/external/multi_converter/multi_converter_mode_select.c similarity index 100% rename from applications/plugins/multi_converter/multi_converter_mode_select.c rename to applications/external/multi_converter/multi_converter_mode_select.c diff --git a/applications/plugins/multi_converter/multi_converter_mode_select.h b/applications/external/multi_converter/multi_converter_mode_select.h similarity index 100% rename from applications/plugins/multi_converter/multi_converter_mode_select.h rename to applications/external/multi_converter/multi_converter_mode_select.h diff --git a/applications/plugins/multi_converter/multi_converter_units.c b/applications/external/multi_converter/multi_converter_units.c similarity index 100% rename from applications/plugins/multi_converter/multi_converter_units.c rename to applications/external/multi_converter/multi_converter_units.c diff --git a/applications/plugins/multi_converter/multi_converter_units.h b/applications/external/multi_converter/multi_converter_units.h similarity index 100% rename from applications/plugins/multi_converter/multi_converter_units.h rename to applications/external/multi_converter/multi_converter_units.h diff --git a/applications/plugins/nrfsniff/application.fam b/applications/external/nrfsniff/application.fam similarity index 100% rename from applications/plugins/nrfsniff/application.fam rename to applications/external/nrfsniff/application.fam diff --git a/applications/plugins/nrfsniff/lib/nrf24/nrf24.c b/applications/external/nrfsniff/lib/nrf24/nrf24.c similarity index 100% rename from applications/plugins/nrfsniff/lib/nrf24/nrf24.c rename to applications/external/nrfsniff/lib/nrf24/nrf24.c diff --git a/applications/plugins/nrfsniff/lib/nrf24/nrf24.h b/applications/external/nrfsniff/lib/nrf24/nrf24.h similarity index 100% rename from applications/plugins/nrfsniff/lib/nrf24/nrf24.h rename to applications/external/nrfsniff/lib/nrf24/nrf24.h diff --git a/applications/plugins/nrfsniff/nrfsniff.c b/applications/external/nrfsniff/nrfsniff.c similarity index 100% rename from applications/plugins/nrfsniff/nrfsniff.c rename to applications/external/nrfsniff/nrfsniff.c diff --git a/applications/plugins/nrfsniff/nrfsniff_10px.png b/applications/external/nrfsniff/nrfsniff_10px.png similarity index 100% rename from applications/plugins/nrfsniff/nrfsniff_10px.png rename to applications/external/nrfsniff/nrfsniff_10px.png diff --git a/applications/plugins/playlist/application.fam b/applications/external/playlist/application.fam similarity index 100% rename from applications/plugins/playlist/application.fam rename to applications/external/playlist/application.fam diff --git a/applications/plugins/playlist/canvas_helper.c b/applications/external/playlist/canvas_helper.c similarity index 100% rename from applications/plugins/playlist/canvas_helper.c rename to applications/external/playlist/canvas_helper.c diff --git a/applications/plugins/playlist/canvas_helper.h b/applications/external/playlist/canvas_helper.h similarity index 100% rename from applications/plugins/playlist/canvas_helper.h rename to applications/external/playlist/canvas_helper.h diff --git a/applications/plugins/playlist/images/ButtonRight_4x7.png b/applications/external/playlist/images/ButtonRight_4x7.png similarity index 100% rename from applications/plugins/playlist/images/ButtonRight_4x7.png rename to applications/external/playlist/images/ButtonRight_4x7.png diff --git a/applications/plugins/playlist/images/sub1_10px.png b/applications/external/playlist/images/sub1_10px.png similarity index 100% rename from applications/plugins/playlist/images/sub1_10px.png rename to applications/external/playlist/images/sub1_10px.png diff --git a/applications/plugins/playlist/playlist.c b/applications/external/playlist/playlist.c similarity index 100% rename from applications/plugins/playlist/playlist.c rename to applications/external/playlist/playlist.c diff --git a/applications/plugins/playlist/playlist_10px.png b/applications/external/playlist/playlist_10px.png similarity index 100% rename from applications/plugins/playlist/playlist_10px.png rename to applications/external/playlist/playlist_10px.png diff --git a/applications/plugins/playlist/playlist_file.c b/applications/external/playlist/playlist_file.c similarity index 100% rename from applications/plugins/playlist/playlist_file.c rename to applications/external/playlist/playlist_file.c diff --git a/applications/plugins/playlist/playlist_file.h b/applications/external/playlist/playlist_file.h similarity index 100% rename from applications/plugins/playlist/playlist_file.h rename to applications/external/playlist/playlist_file.h diff --git a/applications/plugins/pocsag_pager/application.fam b/applications/external/pocsag_pager/application.fam similarity index 100% rename from applications/plugins/pocsag_pager/application.fam rename to applications/external/pocsag_pager/application.fam diff --git a/applications/plugins/pocsag_pager/helpers/pocsag_pager_event.h b/applications/external/pocsag_pager/helpers/pocsag_pager_event.h similarity index 100% rename from applications/plugins/pocsag_pager/helpers/pocsag_pager_event.h rename to applications/external/pocsag_pager/helpers/pocsag_pager_event.h diff --git a/applications/plugins/pocsag_pager/helpers/pocsag_pager_types.h b/applications/external/pocsag_pager/helpers/pocsag_pager_types.h similarity index 100% rename from applications/plugins/pocsag_pager/helpers/pocsag_pager_types.h rename to applications/external/pocsag_pager/helpers/pocsag_pager_types.h diff --git a/applications/plugins/pocsag_pager/images/Lock_7x8.png b/applications/external/pocsag_pager/images/Lock_7x8.png similarity index 100% rename from applications/plugins/pocsag_pager/images/Lock_7x8.png rename to applications/external/pocsag_pager/images/Lock_7x8.png diff --git a/applications/plugins/pocsag_pager/images/Message_8x7.png b/applications/external/pocsag_pager/images/Message_8x7.png similarity index 100% rename from applications/plugins/pocsag_pager/images/Message_8x7.png rename to applications/external/pocsag_pager/images/Message_8x7.png diff --git a/applications/plugins/pocsag_pager/images/Pin_back_arrow_10x8.png b/applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png similarity index 100% rename from applications/plugins/pocsag_pager/images/Pin_back_arrow_10x8.png rename to applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png diff --git a/applications/plugins/pocsag_pager/images/Quest_7x8.png b/applications/external/pocsag_pager/images/Quest_7x8.png similarity index 100% rename from applications/plugins/pocsag_pager/images/Quest_7x8.png rename to applications/external/pocsag_pager/images/Quest_7x8.png diff --git a/applications/plugins/pocsag_pager/images/Scanning_123x52.png b/applications/external/pocsag_pager/images/Scanning_123x52.png similarity index 100% rename from applications/plugins/pocsag_pager/images/Scanning_123x52.png rename to applications/external/pocsag_pager/images/Scanning_123x52.png diff --git a/applications/plugins/pocsag_pager/images/Unlock_7x8.png b/applications/external/pocsag_pager/images/Unlock_7x8.png similarity index 100% rename from applications/plugins/pocsag_pager/images/Unlock_7x8.png rename to applications/external/pocsag_pager/images/Unlock_7x8.png diff --git a/applications/plugins/pocsag_pager/images/WarningDolphin_45x42.png b/applications/external/pocsag_pager/images/WarningDolphin_45x42.png similarity index 100% rename from applications/plugins/pocsag_pager/images/WarningDolphin_45x42.png rename to applications/external/pocsag_pager/images/WarningDolphin_45x42.png diff --git a/applications/plugins/pocsag_pager/pocsag_pager_10px.png b/applications/external/pocsag_pager/pocsag_pager_10px.png similarity index 100% rename from applications/plugins/pocsag_pager/pocsag_pager_10px.png rename to applications/external/pocsag_pager/pocsag_pager_10px.png diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app.c b/applications/external/pocsag_pager/pocsag_pager_app.c similarity index 100% rename from applications/plugins/pocsag_pager/pocsag_pager_app.c rename to applications/external/pocsag_pager/pocsag_pager_app.c diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app_i.c b/applications/external/pocsag_pager/pocsag_pager_app_i.c similarity index 100% rename from applications/plugins/pocsag_pager/pocsag_pager_app_i.c rename to applications/external/pocsag_pager/pocsag_pager_app_i.c diff --git a/applications/plugins/pocsag_pager/pocsag_pager_app_i.h b/applications/external/pocsag_pager/pocsag_pager_app_i.h similarity index 100% rename from applications/plugins/pocsag_pager/pocsag_pager_app_i.h rename to applications/external/pocsag_pager/pocsag_pager_app_i.h diff --git a/applications/plugins/pocsag_pager/pocsag_pager_history.c b/applications/external/pocsag_pager/pocsag_pager_history.c similarity index 100% rename from applications/plugins/pocsag_pager/pocsag_pager_history.c rename to applications/external/pocsag_pager/pocsag_pager_history.c diff --git a/applications/plugins/pocsag_pager/pocsag_pager_history.h b/applications/external/pocsag_pager/pocsag_pager_history.h similarity index 100% rename from applications/plugins/pocsag_pager/pocsag_pager_history.h rename to applications/external/pocsag_pager/pocsag_pager_history.h diff --git a/applications/plugins/pocsag_pager/protocols/pcsg_generic.c b/applications/external/pocsag_pager/protocols/pcsg_generic.c similarity index 100% rename from applications/plugins/pocsag_pager/protocols/pcsg_generic.c rename to applications/external/pocsag_pager/protocols/pcsg_generic.c diff --git a/applications/plugins/pocsag_pager/protocols/pcsg_generic.h b/applications/external/pocsag_pager/protocols/pcsg_generic.h similarity index 100% rename from applications/plugins/pocsag_pager/protocols/pcsg_generic.h rename to applications/external/pocsag_pager/protocols/pcsg_generic.h diff --git a/applications/plugins/pocsag_pager/protocols/pocsag.c b/applications/external/pocsag_pager/protocols/pocsag.c similarity index 100% rename from applications/plugins/pocsag_pager/protocols/pocsag.c rename to applications/external/pocsag_pager/protocols/pocsag.c diff --git a/applications/plugins/pocsag_pager/protocols/pocsag.h b/applications/external/pocsag_pager/protocols/pocsag.h similarity index 100% rename from applications/plugins/pocsag_pager/protocols/pocsag.h rename to applications/external/pocsag_pager/protocols/pocsag.h diff --git a/applications/plugins/pocsag_pager/protocols/protocol_items.c b/applications/external/pocsag_pager/protocols/protocol_items.c similarity index 100% rename from applications/plugins/pocsag_pager/protocols/protocol_items.c rename to applications/external/pocsag_pager/protocols/protocol_items.c diff --git a/applications/plugins/pocsag_pager/protocols/protocol_items.h b/applications/external/pocsag_pager/protocols/protocol_items.h similarity index 100% rename from applications/plugins/pocsag_pager/protocols/protocol_items.h rename to applications/external/pocsag_pager/protocols/protocol_items.h diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c b/applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c similarity index 100% rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_receiver.c rename to applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene.c similarity index 100% rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene.c rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene.c diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene.h b/applications/external/pocsag_pager/scenes/pocsag_pager_scene.h similarity index 100% rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene.h rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene.h diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_about.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_about.c similarity index 100% rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_about.c rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_about.c diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_config.h b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_config.h similarity index 100% rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_config.h rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_config.h diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c similarity index 100% rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c similarity index 100% rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c diff --git a/applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_start.c b/applications/external/pocsag_pager/scenes/pocsag_pager_scene_start.c similarity index 100% rename from applications/plugins/pocsag_pager/scenes/pocsag_pager_scene_start.c rename to applications/external/pocsag_pager/scenes/pocsag_pager_scene_start.c diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver.c similarity index 100% rename from applications/plugins/pocsag_pager/views/pocsag_pager_receiver.c rename to applications/external/pocsag_pager/views/pocsag_pager_receiver.c diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h b/applications/external/pocsag_pager/views/pocsag_pager_receiver.h similarity index 100% rename from applications/plugins/pocsag_pager/views/pocsag_pager_receiver.h rename to applications/external/pocsag_pager/views/pocsag_pager_receiver.h diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.c b/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c similarity index 100% rename from applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.c rename to applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c diff --git a/applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.h b/applications/external/pocsag_pager/views/pocsag_pager_receiver_info.h similarity index 100% rename from applications/plugins/pocsag_pager/views/pocsag_pager_receiver_info.h rename to applications/external/pocsag_pager/views/pocsag_pager_receiver_info.h diff --git a/applications/plugins/protoview/LICENSE b/applications/external/protoview/LICENSE similarity index 100% rename from applications/plugins/protoview/LICENSE rename to applications/external/protoview/LICENSE diff --git a/applications/plugins/protoview/README.md b/applications/external/protoview/README.md similarity index 100% rename from applications/plugins/protoview/README.md rename to applications/external/protoview/README.md diff --git a/applications/plugins/protoview/app.c b/applications/external/protoview/app.c similarity index 100% rename from applications/plugins/protoview/app.c rename to applications/external/protoview/app.c diff --git a/applications/plugins/protoview/app.h b/applications/external/protoview/app.h similarity index 100% rename from applications/plugins/protoview/app.h rename to applications/external/protoview/app.h diff --git a/applications/plugins/protoview/app_subghz.c b/applications/external/protoview/app_subghz.c similarity index 100% rename from applications/plugins/protoview/app_subghz.c rename to applications/external/protoview/app_subghz.c diff --git a/applications/plugins/protoview/appicon.png b/applications/external/protoview/appicon.png similarity index 100% rename from applications/plugins/protoview/appicon.png rename to applications/external/protoview/appicon.png diff --git a/applications/plugins/protoview/application.fam b/applications/external/protoview/application.fam similarity index 100% rename from applications/plugins/protoview/application.fam rename to applications/external/protoview/application.fam diff --git a/applications/plugins/protoview/crc.c b/applications/external/protoview/crc.c similarity index 100% rename from applications/plugins/protoview/crc.c rename to applications/external/protoview/crc.c diff --git a/applications/plugins/protoview/custom_presets.h b/applications/external/protoview/custom_presets.h similarity index 100% rename from applications/plugins/protoview/custom_presets.h rename to applications/external/protoview/custom_presets.h diff --git a/applications/plugins/protoview/fields.c b/applications/external/protoview/fields.c similarity index 100% rename from applications/plugins/protoview/fields.c rename to applications/external/protoview/fields.c diff --git a/applications/plugins/protoview/images/ProtoViewSignal.jpg b/applications/external/protoview/images/ProtoViewSignal.jpg similarity index 100% rename from applications/plugins/protoview/images/ProtoViewSignal.jpg rename to applications/external/protoview/images/ProtoViewSignal.jpg diff --git a/applications/plugins/protoview/images/protoview_1.jpg b/applications/external/protoview/images/protoview_1.jpg similarity index 100% rename from applications/plugins/protoview/images/protoview_1.jpg rename to applications/external/protoview/images/protoview_1.jpg diff --git a/applications/plugins/protoview/images/protoview_2.jpg b/applications/external/protoview/images/protoview_2.jpg similarity index 100% rename from applications/plugins/protoview/images/protoview_2.jpg rename to applications/external/protoview/images/protoview_2.jpg diff --git a/applications/plugins/protoview/protocols/b4b1.c b/applications/external/protoview/protocols/b4b1.c similarity index 100% rename from applications/plugins/protoview/protocols/b4b1.c rename to applications/external/protoview/protocols/b4b1.c diff --git a/applications/plugins/protoview/protocols/keeloq.c b/applications/external/protoview/protocols/keeloq.c similarity index 100% rename from applications/plugins/protoview/protocols/keeloq.c rename to applications/external/protoview/protocols/keeloq.c diff --git a/applications/plugins/protoview/protocols/oregon2.c b/applications/external/protoview/protocols/oregon2.c similarity index 100% rename from applications/plugins/protoview/protocols/oregon2.c rename to applications/external/protoview/protocols/oregon2.c diff --git a/applications/plugins/protoview/protocols/pvchat.c b/applications/external/protoview/protocols/pvchat.c similarity index 100% rename from applications/plugins/protoview/protocols/pvchat.c rename to applications/external/protoview/protocols/pvchat.c diff --git a/applications/plugins/protoview/protocols/tpms/citroen.c b/applications/external/protoview/protocols/tpms/citroen.c similarity index 100% rename from applications/plugins/protoview/protocols/tpms/citroen.c rename to applications/external/protoview/protocols/tpms/citroen.c diff --git a/applications/plugins/protoview/protocols/tpms/ford.c b/applications/external/protoview/protocols/tpms/ford.c similarity index 100% rename from applications/plugins/protoview/protocols/tpms/ford.c rename to applications/external/protoview/protocols/tpms/ford.c diff --git a/applications/plugins/protoview/protocols/tpms/renault.c b/applications/external/protoview/protocols/tpms/renault.c similarity index 100% rename from applications/plugins/protoview/protocols/tpms/renault.c rename to applications/external/protoview/protocols/tpms/renault.c diff --git a/applications/plugins/protoview/protocols/tpms/schrader.c b/applications/external/protoview/protocols/tpms/schrader.c similarity index 100% rename from applications/plugins/protoview/protocols/tpms/schrader.c rename to applications/external/protoview/protocols/tpms/schrader.c diff --git a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c b/applications/external/protoview/protocols/tpms/schrader_eg53ma4.c similarity index 100% rename from applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c rename to applications/external/protoview/protocols/tpms/schrader_eg53ma4.c diff --git a/applications/plugins/protoview/protocols/tpms/toyota.c b/applications/external/protoview/protocols/tpms/toyota.c similarity index 100% rename from applications/plugins/protoview/protocols/tpms/toyota.c rename to applications/external/protoview/protocols/tpms/toyota.c diff --git a/applications/plugins/protoview/protocols/unknown.c b/applications/external/protoview/protocols/unknown.c similarity index 100% rename from applications/plugins/protoview/protocols/unknown.c rename to applications/external/protoview/protocols/unknown.c diff --git a/applications/plugins/protoview/raw_samples.c b/applications/external/protoview/raw_samples.c similarity index 100% rename from applications/plugins/protoview/raw_samples.c rename to applications/external/protoview/raw_samples.c diff --git a/applications/plugins/protoview/raw_samples.h b/applications/external/protoview/raw_samples.h similarity index 100% rename from applications/plugins/protoview/raw_samples.h rename to applications/external/protoview/raw_samples.h diff --git a/applications/plugins/protoview/signal.c b/applications/external/protoview/signal.c similarity index 100% rename from applications/plugins/protoview/signal.c rename to applications/external/protoview/signal.c diff --git a/applications/plugins/protoview/signal_file.c b/applications/external/protoview/signal_file.c similarity index 100% rename from applications/plugins/protoview/signal_file.c rename to applications/external/protoview/signal_file.c diff --git a/applications/plugins/protoview/ui.c b/applications/external/protoview/ui.c similarity index 100% rename from applications/plugins/protoview/ui.c rename to applications/external/protoview/ui.c diff --git a/applications/plugins/protoview/view_build.c b/applications/external/protoview/view_build.c similarity index 100% rename from applications/plugins/protoview/view_build.c rename to applications/external/protoview/view_build.c diff --git a/applications/plugins/protoview/view_direct_sampling.c b/applications/external/protoview/view_direct_sampling.c similarity index 100% rename from applications/plugins/protoview/view_direct_sampling.c rename to applications/external/protoview/view_direct_sampling.c diff --git a/applications/plugins/protoview/view_info.c b/applications/external/protoview/view_info.c similarity index 100% rename from applications/plugins/protoview/view_info.c rename to applications/external/protoview/view_info.c diff --git a/applications/plugins/protoview/view_raw_signal.c b/applications/external/protoview/view_raw_signal.c similarity index 100% rename from applications/plugins/protoview/view_raw_signal.c rename to applications/external/protoview/view_raw_signal.c diff --git a/applications/plugins/protoview/view_settings.c b/applications/external/protoview/view_settings.c similarity index 100% rename from applications/plugins/protoview/view_settings.c rename to applications/external/protoview/view_settings.c diff --git a/applications/plugins/sentry_safe/application.fam b/applications/external/sentry_safe/application.fam similarity index 100% rename from applications/plugins/sentry_safe/application.fam rename to applications/external/sentry_safe/application.fam diff --git a/applications/plugins/sentry_safe/safe_10px.png b/applications/external/sentry_safe/safe_10px.png similarity index 100% rename from applications/plugins/sentry_safe/safe_10px.png rename to applications/external/sentry_safe/safe_10px.png diff --git a/applications/plugins/sentry_safe/sentry_safe.c b/applications/external/sentry_safe/sentry_safe.c similarity index 100% rename from applications/plugins/sentry_safe/sentry_safe.c rename to applications/external/sentry_safe/sentry_safe.c diff --git a/applications/plugins/solitaire/application.fam b/applications/external/solitaire/application.fam similarity index 100% rename from applications/plugins/solitaire/application.fam rename to applications/external/solitaire/application.fam diff --git a/applications/plugins/solitaire/assets/card_graphics.png b/applications/external/solitaire/assets/card_graphics.png similarity index 100% rename from applications/plugins/solitaire/assets/card_graphics.png rename to applications/external/solitaire/assets/card_graphics.png diff --git a/applications/plugins/solitaire/assets/solitaire_main.png b/applications/external/solitaire/assets/solitaire_main.png similarity index 100% rename from applications/plugins/solitaire/assets/solitaire_main.png rename to applications/external/solitaire/assets/solitaire_main.png diff --git a/applications/plugins/solitaire/common/card.c b/applications/external/solitaire/common/card.c similarity index 100% rename from applications/plugins/solitaire/common/card.c rename to applications/external/solitaire/common/card.c diff --git a/applications/plugins/solitaire/common/card.h b/applications/external/solitaire/common/card.h similarity index 100% rename from applications/plugins/solitaire/common/card.h rename to applications/external/solitaire/common/card.h diff --git a/applications/plugins/solitaire/common/dml.c b/applications/external/solitaire/common/dml.c similarity index 100% rename from applications/plugins/solitaire/common/dml.c rename to applications/external/solitaire/common/dml.c diff --git a/applications/plugins/solitaire/common/dml.h b/applications/external/solitaire/common/dml.h similarity index 100% rename from applications/plugins/solitaire/common/dml.h rename to applications/external/solitaire/common/dml.h diff --git a/applications/plugins/solitaire/common/menu.c b/applications/external/solitaire/common/menu.c similarity index 100% rename from applications/plugins/solitaire/common/menu.c rename to applications/external/solitaire/common/menu.c diff --git a/applications/plugins/solitaire/common/menu.h b/applications/external/solitaire/common/menu.h similarity index 100% rename from applications/plugins/solitaire/common/menu.h rename to applications/external/solitaire/common/menu.h diff --git a/applications/plugins/solitaire/common/queue.c b/applications/external/solitaire/common/queue.c similarity index 100% rename from applications/plugins/solitaire/common/queue.c rename to applications/external/solitaire/common/queue.c diff --git a/applications/plugins/solitaire/common/queue.h b/applications/external/solitaire/common/queue.h similarity index 100% rename from applications/plugins/solitaire/common/queue.h rename to applications/external/solitaire/common/queue.h diff --git a/applications/plugins/solitaire/common/ui.c b/applications/external/solitaire/common/ui.c similarity index 100% rename from applications/plugins/solitaire/common/ui.c rename to applications/external/solitaire/common/ui.c diff --git a/applications/plugins/solitaire/common/ui.h b/applications/external/solitaire/common/ui.h similarity index 100% rename from applications/plugins/solitaire/common/ui.h rename to applications/external/solitaire/common/ui.h diff --git a/applications/plugins/solitaire/defines.h b/applications/external/solitaire/defines.h similarity index 100% rename from applications/plugins/solitaire/defines.h rename to applications/external/solitaire/defines.h diff --git a/applications/plugins/solitaire/solitaire.c b/applications/external/solitaire/solitaire.c similarity index 100% rename from applications/plugins/solitaire/solitaire.c rename to applications/external/solitaire/solitaire.c diff --git a/applications/plugins/solitaire/solitaire_10px.png b/applications/external/solitaire/solitaire_10px.png similarity index 100% rename from applications/plugins/solitaire/solitaire_10px.png rename to applications/external/solitaire/solitaire_10px.png diff --git a/applications/plugins/spectrum_analyzer/application.fam b/applications/external/spectrum_analyzer/application.fam similarity index 100% rename from applications/plugins/spectrum_analyzer/application.fam rename to applications/external/spectrum_analyzer/application.fam diff --git a/applications/plugins/spectrum_analyzer/spectrum_10px.png b/applications/external/spectrum_analyzer/spectrum_10px.png similarity index 100% rename from applications/plugins/spectrum_analyzer/spectrum_10px.png rename to applications/external/spectrum_analyzer/spectrum_10px.png diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer.c b/applications/external/spectrum_analyzer/spectrum_analyzer.c similarity index 100% rename from applications/plugins/spectrum_analyzer/spectrum_analyzer.c rename to applications/external/spectrum_analyzer/spectrum_analyzer.c diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer.h b/applications/external/spectrum_analyzer/spectrum_analyzer.h similarity index 100% rename from applications/plugins/spectrum_analyzer/spectrum_analyzer.h rename to applications/external/spectrum_analyzer/spectrum_analyzer.h diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer_worker.c b/applications/external/spectrum_analyzer/spectrum_analyzer_worker.c similarity index 100% rename from applications/plugins/spectrum_analyzer/spectrum_analyzer_worker.c rename to applications/external/spectrum_analyzer/spectrum_analyzer_worker.c diff --git a/applications/plugins/spectrum_analyzer/spectrum_analyzer_worker.h b/applications/external/spectrum_analyzer/spectrum_analyzer_worker.h similarity index 100% rename from applications/plugins/spectrum_analyzer/spectrum_analyzer_worker.h rename to applications/external/spectrum_analyzer/spectrum_analyzer_worker.h diff --git a/applications/plugins/subbrute b/applications/external/subbrute similarity index 100% rename from applications/plugins/subbrute rename to applications/external/subbrute diff --git a/applications/plugins/swd_probe/.gitignore b/applications/external/swd_probe/.gitignore similarity index 100% rename from applications/plugins/swd_probe/.gitignore rename to applications/external/swd_probe/.gitignore diff --git a/applications/plugins/swd_probe/LICENSE.txt b/applications/external/swd_probe/LICENSE.txt similarity index 100% rename from applications/plugins/swd_probe/LICENSE.txt rename to applications/external/swd_probe/LICENSE.txt diff --git a/applications/plugins/swd_probe/README.md b/applications/external/swd_probe/README.md similarity index 100% rename from applications/plugins/swd_probe/README.md rename to applications/external/swd_probe/README.md diff --git a/applications/plugins/swd_probe/adi.c b/applications/external/swd_probe/adi.c similarity index 100% rename from applications/plugins/swd_probe/adi.c rename to applications/external/swd_probe/adi.c diff --git a/applications/plugins/swd_probe/adi.h b/applications/external/swd_probe/adi.h similarity index 100% rename from applications/plugins/swd_probe/adi.h rename to applications/external/swd_probe/adi.h diff --git a/applications/plugins/swd_probe/application.fam b/applications/external/swd_probe/application.fam similarity index 100% rename from applications/plugins/swd_probe/application.fam rename to applications/external/swd_probe/application.fam diff --git a/applications/plugins/swd_probe/icons/ButtonDown_7x4.png b/applications/external/swd_probe/icons/ButtonDown_7x4.png similarity index 100% rename from applications/plugins/swd_probe/icons/ButtonDown_7x4.png rename to applications/external/swd_probe/icons/ButtonDown_7x4.png diff --git a/applications/plugins/swd_probe/icons/ButtonUp_7x4.png b/applications/external/swd_probe/icons/ButtonUp_7x4.png similarity index 100% rename from applications/plugins/swd_probe/icons/ButtonUp_7x4.png rename to applications/external/swd_probe/icons/ButtonUp_7x4.png diff --git a/applications/plugins/swd_probe/icons/app.png b/applications/external/swd_probe/icons/app.png similarity index 100% rename from applications/plugins/swd_probe/icons/app.png rename to applications/external/swd_probe/icons/app.png diff --git a/applications/plugins/swd_probe/icons/swd.png b/applications/external/swd_probe/icons/swd.png similarity index 100% rename from applications/plugins/swd_probe/icons/swd.png rename to applications/external/swd_probe/icons/swd.png diff --git a/applications/plugins/swd_probe/jep106.c b/applications/external/swd_probe/jep106.c similarity index 100% rename from applications/plugins/swd_probe/jep106.c rename to applications/external/swd_probe/jep106.c diff --git a/applications/plugins/swd_probe/jep106.h b/applications/external/swd_probe/jep106.h similarity index 100% rename from applications/plugins/swd_probe/jep106.h rename to applications/external/swd_probe/jep106.h diff --git a/applications/plugins/swd_probe/jep106.inc b/applications/external/swd_probe/jep106.inc similarity index 100% rename from applications/plugins/swd_probe/jep106.inc rename to applications/external/swd_probe/jep106.inc diff --git a/applications/plugins/swd_probe/model/chip.ply b/applications/external/swd_probe/model/chip.ply similarity index 100% rename from applications/plugins/swd_probe/model/chip.ply rename to applications/external/swd_probe/model/chip.ply diff --git a/applications/plugins/swd_probe/model/convert.py b/applications/external/swd_probe/model/convert.py similarity index 100% rename from applications/plugins/swd_probe/model/convert.py rename to applications/external/swd_probe/model/convert.py diff --git a/applications/plugins/swd_probe/model/model_chip.h b/applications/external/swd_probe/model/model_chip.h similarity index 100% rename from applications/plugins/swd_probe/model/model_chip.h rename to applications/external/swd_probe/model/model_chip.h diff --git a/applications/plugins/swd_probe/swd_probe_app.c b/applications/external/swd_probe/swd_probe_app.c similarity index 100% rename from applications/plugins/swd_probe/swd_probe_app.c rename to applications/external/swd_probe/swd_probe_app.c diff --git a/applications/plugins/swd_probe/swd_probe_app.h b/applications/external/swd_probe/swd_probe_app.h similarity index 100% rename from applications/plugins/swd_probe/swd_probe_app.h rename to applications/external/swd_probe/swd_probe_app.h diff --git a/applications/plugins/swd_probe/usb_uart.c b/applications/external/swd_probe/usb_uart.c similarity index 100% rename from applications/plugins/swd_probe/usb_uart.c rename to applications/external/swd_probe/usb_uart.c diff --git a/applications/plugins/swd_probe/usb_uart.h b/applications/external/swd_probe/usb_uart.h similarity index 100% rename from applications/plugins/swd_probe/usb_uart.h rename to applications/external/swd_probe/usb_uart.h diff --git a/applications/plugins/tetris_game/application.fam b/applications/external/tetris_game/application.fam similarity index 100% rename from applications/plugins/tetris_game/application.fam rename to applications/external/tetris_game/application.fam diff --git a/applications/plugins/tetris_game/tetris_10px.png b/applications/external/tetris_game/tetris_10px.png similarity index 100% rename from applications/plugins/tetris_game/tetris_10px.png rename to applications/external/tetris_game/tetris_10px.png diff --git a/applications/plugins/tetris_game/tetris_game.c b/applications/external/tetris_game/tetris_game.c similarity index 100% rename from applications/plugins/tetris_game/tetris_game.c rename to applications/external/tetris_game/tetris_game.c diff --git a/applications/plugins/text_viewer/LICENSE b/applications/external/text_viewer/LICENSE similarity index 100% rename from applications/plugins/text_viewer/LICENSE rename to applications/external/text_viewer/LICENSE diff --git a/applications/plugins/text_viewer/README.md b/applications/external/text_viewer/README.md similarity index 100% rename from applications/plugins/text_viewer/README.md rename to applications/external/text_viewer/README.md diff --git a/applications/plugins/text_viewer/application.fam b/applications/external/text_viewer/application.fam similarity index 100% rename from applications/plugins/text_viewer/application.fam rename to applications/external/text_viewer/application.fam diff --git a/applications/plugins/text_viewer/icons/text_10px.png b/applications/external/text_viewer/icons/text_10px.png similarity index 100% rename from applications/plugins/text_viewer/icons/text_10px.png rename to applications/external/text_viewer/icons/text_10px.png diff --git a/applications/plugins/text_viewer/text_viewer.c b/applications/external/text_viewer/text_viewer.c similarity index 100% rename from applications/plugins/text_viewer/text_viewer.c rename to applications/external/text_viewer/text_viewer.c diff --git a/applications/plugins/text_viewer/textviewerflipper.PNG b/applications/external/text_viewer/textviewerflipper.PNG similarity index 100% rename from applications/plugins/text_viewer/textviewerflipper.PNG rename to applications/external/text_viewer/textviewerflipper.PNG diff --git a/applications/plugins/tictactoe_game/application.fam b/applications/external/tictactoe_game/application.fam similarity index 100% rename from applications/plugins/tictactoe_game/application.fam rename to applications/external/tictactoe_game/application.fam diff --git a/applications/plugins/tictactoe_game/tictactoe_10px.png b/applications/external/tictactoe_game/tictactoe_10px.png similarity index 100% rename from applications/plugins/tictactoe_game/tictactoe_10px.png rename to applications/external/tictactoe_game/tictactoe_10px.png diff --git a/applications/plugins/tictactoe_game/tictactoe_game.c b/applications/external/tictactoe_game/tictactoe_game.c similarity index 100% rename from applications/plugins/tictactoe_game/tictactoe_game.c rename to applications/external/tictactoe_game/tictactoe_game.c diff --git a/applications/plugins/totp/LICENSE b/applications/external/totp/LICENSE similarity index 100% rename from applications/plugins/totp/LICENSE rename to applications/external/totp/LICENSE diff --git a/applications/plugins/totp/application.fam b/applications/external/totp/application.fam similarity index 100% rename from applications/plugins/totp/application.fam rename to applications/external/totp/application.fam diff --git a/applications/plugins/totp/cli/cli.c b/applications/external/totp/cli/cli.c similarity index 100% rename from applications/plugins/totp/cli/cli.c rename to applications/external/totp/cli/cli.c diff --git a/applications/plugins/totp/cli/cli.h b/applications/external/totp/cli/cli.h similarity index 100% rename from applications/plugins/totp/cli/cli.h rename to applications/external/totp/cli/cli.h diff --git a/applications/plugins/totp/cli/cli_helpers.c b/applications/external/totp/cli/cli_helpers.c similarity index 100% rename from applications/plugins/totp/cli/cli_helpers.c rename to applications/external/totp/cli/cli_helpers.c diff --git a/applications/plugins/totp/cli/cli_helpers.h b/applications/external/totp/cli/cli_helpers.h similarity index 100% rename from applications/plugins/totp/cli/cli_helpers.h rename to applications/external/totp/cli/cli_helpers.h diff --git a/applications/plugins/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c similarity index 100% rename from applications/plugins/totp/cli/commands/add/add.c rename to applications/external/totp/cli/commands/add/add.c diff --git a/applications/plugins/totp/cli/commands/add/add.h b/applications/external/totp/cli/commands/add/add.h similarity index 100% rename from applications/plugins/totp/cli/commands/add/add.h rename to applications/external/totp/cli/commands/add/add.h diff --git a/applications/plugins/totp/cli/commands/delete/delete.c b/applications/external/totp/cli/commands/delete/delete.c similarity index 100% rename from applications/plugins/totp/cli/commands/delete/delete.c rename to applications/external/totp/cli/commands/delete/delete.c diff --git a/applications/plugins/totp/cli/commands/delete/delete.h b/applications/external/totp/cli/commands/delete/delete.h similarity index 100% rename from applications/plugins/totp/cli/commands/delete/delete.h rename to applications/external/totp/cli/commands/delete/delete.h diff --git a/applications/plugins/totp/cli/commands/help/help.c b/applications/external/totp/cli/commands/help/help.c similarity index 100% rename from applications/plugins/totp/cli/commands/help/help.c rename to applications/external/totp/cli/commands/help/help.c diff --git a/applications/plugins/totp/cli/commands/help/help.h b/applications/external/totp/cli/commands/help/help.h similarity index 100% rename from applications/plugins/totp/cli/commands/help/help.h rename to applications/external/totp/cli/commands/help/help.h diff --git a/applications/plugins/totp/cli/commands/list/list.c b/applications/external/totp/cli/commands/list/list.c similarity index 100% rename from applications/plugins/totp/cli/commands/list/list.c rename to applications/external/totp/cli/commands/list/list.c diff --git a/applications/plugins/totp/cli/commands/list/list.h b/applications/external/totp/cli/commands/list/list.h similarity index 100% rename from applications/plugins/totp/cli/commands/list/list.h rename to applications/external/totp/cli/commands/list/list.h diff --git a/applications/plugins/totp/cli/commands/move/move.c b/applications/external/totp/cli/commands/move/move.c similarity index 100% rename from applications/plugins/totp/cli/commands/move/move.c rename to applications/external/totp/cli/commands/move/move.c diff --git a/applications/plugins/totp/cli/commands/move/move.h b/applications/external/totp/cli/commands/move/move.h similarity index 100% rename from applications/plugins/totp/cli/commands/move/move.h rename to applications/external/totp/cli/commands/move/move.h diff --git a/applications/plugins/totp/cli/commands/notification/notification.c b/applications/external/totp/cli/commands/notification/notification.c similarity index 100% rename from applications/plugins/totp/cli/commands/notification/notification.c rename to applications/external/totp/cli/commands/notification/notification.c diff --git a/applications/plugins/totp/cli/commands/notification/notification.h b/applications/external/totp/cli/commands/notification/notification.h similarity index 100% rename from applications/plugins/totp/cli/commands/notification/notification.h rename to applications/external/totp/cli/commands/notification/notification.h diff --git a/applications/plugins/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c similarity index 100% rename from applications/plugins/totp/cli/commands/pin/pin.c rename to applications/external/totp/cli/commands/pin/pin.c diff --git a/applications/plugins/totp/cli/commands/pin/pin.h b/applications/external/totp/cli/commands/pin/pin.h similarity index 100% rename from applications/plugins/totp/cli/commands/pin/pin.h rename to applications/external/totp/cli/commands/pin/pin.h diff --git a/applications/plugins/totp/cli/commands/reset/reset.c b/applications/external/totp/cli/commands/reset/reset.c similarity index 100% rename from applications/plugins/totp/cli/commands/reset/reset.c rename to applications/external/totp/cli/commands/reset/reset.c diff --git a/applications/plugins/totp/cli/commands/reset/reset.h b/applications/external/totp/cli/commands/reset/reset.h similarity index 100% rename from applications/plugins/totp/cli/commands/reset/reset.h rename to applications/external/totp/cli/commands/reset/reset.h diff --git a/applications/plugins/totp/cli/commands/timezone/timezone.c b/applications/external/totp/cli/commands/timezone/timezone.c similarity index 100% rename from applications/plugins/totp/cli/commands/timezone/timezone.c rename to applications/external/totp/cli/commands/timezone/timezone.c diff --git a/applications/plugins/totp/cli/commands/timezone/timezone.h b/applications/external/totp/cli/commands/timezone/timezone.h similarity index 100% rename from applications/plugins/totp/cli/commands/timezone/timezone.h rename to applications/external/totp/cli/commands/timezone/timezone.h diff --git a/applications/plugins/totp/images/DolphinCommon_56x48.png b/applications/external/totp/images/DolphinCommon_56x48.png similarity index 100% rename from applications/plugins/totp/images/DolphinCommon_56x48.png rename to applications/external/totp/images/DolphinCommon_56x48.png diff --git a/applications/plugins/totp/images/totp_arrow_bottom_10x5.png b/applications/external/totp/images/totp_arrow_bottom_10x5.png similarity index 100% rename from applications/plugins/totp/images/totp_arrow_bottom_10x5.png rename to applications/external/totp/images/totp_arrow_bottom_10x5.png diff --git a/applications/plugins/totp/images/totp_arrow_left_8x9.png b/applications/external/totp/images/totp_arrow_left_8x9.png similarity index 100% rename from applications/plugins/totp/images/totp_arrow_left_8x9.png rename to applications/external/totp/images/totp_arrow_left_8x9.png diff --git a/applications/plugins/totp/images/totp_arrow_right_8x9.png b/applications/external/totp/images/totp_arrow_right_8x9.png similarity index 100% rename from applications/plugins/totp/images/totp_arrow_right_8x9.png rename to applications/external/totp/images/totp_arrow_right_8x9.png diff --git a/applications/plugins/totp/lib/base32/base32.c b/applications/external/totp/lib/base32/base32.c similarity index 100% rename from applications/plugins/totp/lib/base32/base32.c rename to applications/external/totp/lib/base32/base32.c diff --git a/applications/plugins/totp/lib/base32/base32.h b/applications/external/totp/lib/base32/base32.h similarity index 100% rename from applications/plugins/totp/lib/base32/base32.h rename to applications/external/totp/lib/base32/base32.h diff --git a/applications/plugins/totp/lib/list/list.c b/applications/external/totp/lib/list/list.c similarity index 100% rename from applications/plugins/totp/lib/list/list.c rename to applications/external/totp/lib/list/list.c diff --git a/applications/plugins/totp/lib/list/list.h b/applications/external/totp/lib/list/list.h similarity index 100% rename from applications/plugins/totp/lib/list/list.h rename to applications/external/totp/lib/list/list.h diff --git a/applications/plugins/totp/lib/polyfills/memset_s.c b/applications/external/totp/lib/polyfills/memset_s.c similarity index 100% rename from applications/plugins/totp/lib/polyfills/memset_s.c rename to applications/external/totp/lib/polyfills/memset_s.c diff --git a/applications/plugins/totp/lib/polyfills/memset_s.h b/applications/external/totp/lib/polyfills/memset_s.h similarity index 100% rename from applications/plugins/totp/lib/polyfills/memset_s.h rename to applications/external/totp/lib/polyfills/memset_s.h diff --git a/applications/plugins/totp/lib/polyfills/strnlen.c b/applications/external/totp/lib/polyfills/strnlen.c similarity index 100% rename from applications/plugins/totp/lib/polyfills/strnlen.c rename to applications/external/totp/lib/polyfills/strnlen.c diff --git a/applications/plugins/totp/lib/polyfills/strnlen.h b/applications/external/totp/lib/polyfills/strnlen.h similarity index 100% rename from applications/plugins/totp/lib/polyfills/strnlen.h rename to applications/external/totp/lib/polyfills/strnlen.h diff --git a/applications/plugins/totp/lib/roll_value/roll_value.c b/applications/external/totp/lib/roll_value/roll_value.c similarity index 100% rename from applications/plugins/totp/lib/roll_value/roll_value.c rename to applications/external/totp/lib/roll_value/roll_value.c diff --git a/applications/plugins/totp/lib/roll_value/roll_value.h b/applications/external/totp/lib/roll_value/roll_value.h similarity index 100% rename from applications/plugins/totp/lib/roll_value/roll_value.h rename to applications/external/totp/lib/roll_value/roll_value.h diff --git a/applications/plugins/totp/lib/timezone_utils/timezone_utils.c b/applications/external/totp/lib/timezone_utils/timezone_utils.c similarity index 100% rename from applications/plugins/totp/lib/timezone_utils/timezone_utils.c rename to applications/external/totp/lib/timezone_utils/timezone_utils.c diff --git a/applications/plugins/totp/lib/timezone_utils/timezone_utils.h b/applications/external/totp/lib/timezone_utils/timezone_utils.h similarity index 100% rename from applications/plugins/totp/lib/timezone_utils/timezone_utils.h rename to applications/external/totp/lib/timezone_utils/timezone_utils.h diff --git a/applications/plugins/totp/services/config/config.c b/applications/external/totp/services/config/config.c similarity index 100% rename from applications/plugins/totp/services/config/config.c rename to applications/external/totp/services/config/config.c diff --git a/applications/plugins/totp/services/config/config.h b/applications/external/totp/services/config/config.h similarity index 100% rename from applications/plugins/totp/services/config/config.h rename to applications/external/totp/services/config/config.h diff --git a/applications/plugins/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h similarity index 100% rename from applications/plugins/totp/services/config/constants.h rename to applications/external/totp/services/config/constants.h diff --git a/applications/plugins/totp/services/config/migrations/config_migration_v1_to_v2.c b/applications/external/totp/services/config/migrations/config_migration_v1_to_v2.c similarity index 100% rename from applications/plugins/totp/services/config/migrations/config_migration_v1_to_v2.c rename to applications/external/totp/services/config/migrations/config_migration_v1_to_v2.c diff --git a/applications/plugins/totp/services/config/migrations/config_migration_v1_to_v2.h b/applications/external/totp/services/config/migrations/config_migration_v1_to_v2.h similarity index 100% rename from applications/plugins/totp/services/config/migrations/config_migration_v1_to_v2.h rename to applications/external/totp/services/config/migrations/config_migration_v1_to_v2.h diff --git a/applications/plugins/totp/services/config/migrations/config_migration_v2_to_v3.c b/applications/external/totp/services/config/migrations/config_migration_v2_to_v3.c similarity index 100% rename from applications/plugins/totp/services/config/migrations/config_migration_v2_to_v3.c rename to applications/external/totp/services/config/migrations/config_migration_v2_to_v3.c diff --git a/applications/plugins/totp/services/config/migrations/config_migration_v2_to_v3.h b/applications/external/totp/services/config/migrations/config_migration_v2_to_v3.h similarity index 100% rename from applications/plugins/totp/services/config/migrations/config_migration_v2_to_v3.h rename to applications/external/totp/services/config/migrations/config_migration_v2_to_v3.h diff --git a/applications/plugins/totp/services/convert/convert.h b/applications/external/totp/services/convert/convert.h similarity index 100% rename from applications/plugins/totp/services/convert/convert.h rename to applications/external/totp/services/convert/convert.h diff --git a/applications/plugins/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c similarity index 100% rename from applications/plugins/totp/services/crypto/crypto.c rename to applications/external/totp/services/crypto/crypto.c diff --git a/applications/plugins/totp/services/crypto/crypto.h b/applications/external/totp/services/crypto/crypto.h similarity index 100% rename from applications/plugins/totp/services/crypto/crypto.h rename to applications/external/totp/services/crypto/crypto.h diff --git a/applications/plugins/totp/services/hmac/byteswap.c b/applications/external/totp/services/hmac/byteswap.c similarity index 100% rename from applications/plugins/totp/services/hmac/byteswap.c rename to applications/external/totp/services/hmac/byteswap.c diff --git a/applications/plugins/totp/services/hmac/byteswap.h b/applications/external/totp/services/hmac/byteswap.h similarity index 100% rename from applications/plugins/totp/services/hmac/byteswap.h rename to applications/external/totp/services/hmac/byteswap.h diff --git a/applications/plugins/totp/services/hmac/hmac_common.h b/applications/external/totp/services/hmac/hmac_common.h similarity index 100% rename from applications/plugins/totp/services/hmac/hmac_common.h rename to applications/external/totp/services/hmac/hmac_common.h diff --git a/applications/plugins/totp/services/hmac/hmac_sha1.c b/applications/external/totp/services/hmac/hmac_sha1.c similarity index 100% rename from applications/plugins/totp/services/hmac/hmac_sha1.c rename to applications/external/totp/services/hmac/hmac_sha1.c diff --git a/applications/plugins/totp/services/hmac/hmac_sha1.h b/applications/external/totp/services/hmac/hmac_sha1.h similarity index 100% rename from applications/plugins/totp/services/hmac/hmac_sha1.h rename to applications/external/totp/services/hmac/hmac_sha1.h diff --git a/applications/plugins/totp/services/hmac/hmac_sha256.c b/applications/external/totp/services/hmac/hmac_sha256.c similarity index 100% rename from applications/plugins/totp/services/hmac/hmac_sha256.c rename to applications/external/totp/services/hmac/hmac_sha256.c diff --git a/applications/plugins/totp/services/hmac/hmac_sha256.h b/applications/external/totp/services/hmac/hmac_sha256.h similarity index 100% rename from applications/plugins/totp/services/hmac/hmac_sha256.h rename to applications/external/totp/services/hmac/hmac_sha256.h diff --git a/applications/plugins/totp/services/hmac/hmac_sha512.c b/applications/external/totp/services/hmac/hmac_sha512.c similarity index 100% rename from applications/plugins/totp/services/hmac/hmac_sha512.c rename to applications/external/totp/services/hmac/hmac_sha512.c diff --git a/applications/plugins/totp/services/hmac/hmac_sha512.h b/applications/external/totp/services/hmac/hmac_sha512.h similarity index 100% rename from applications/plugins/totp/services/hmac/hmac_sha512.h rename to applications/external/totp/services/hmac/hmac_sha512.h diff --git a/applications/plugins/totp/services/hmac/memxor.c b/applications/external/totp/services/hmac/memxor.c similarity index 100% rename from applications/plugins/totp/services/hmac/memxor.c rename to applications/external/totp/services/hmac/memxor.c diff --git a/applications/plugins/totp/services/hmac/memxor.h b/applications/external/totp/services/hmac/memxor.h similarity index 100% rename from applications/plugins/totp/services/hmac/memxor.h rename to applications/external/totp/services/hmac/memxor.h diff --git a/applications/plugins/totp/services/hmac/sha1.c b/applications/external/totp/services/hmac/sha1.c similarity index 100% rename from applications/plugins/totp/services/hmac/sha1.c rename to applications/external/totp/services/hmac/sha1.c diff --git a/applications/plugins/totp/services/hmac/sha1.h b/applications/external/totp/services/hmac/sha1.h similarity index 100% rename from applications/plugins/totp/services/hmac/sha1.h rename to applications/external/totp/services/hmac/sha1.h diff --git a/applications/plugins/totp/services/hmac/sha256.c b/applications/external/totp/services/hmac/sha256.c similarity index 100% rename from applications/plugins/totp/services/hmac/sha256.c rename to applications/external/totp/services/hmac/sha256.c diff --git a/applications/plugins/totp/services/hmac/sha256.h b/applications/external/totp/services/hmac/sha256.h similarity index 100% rename from applications/plugins/totp/services/hmac/sha256.h rename to applications/external/totp/services/hmac/sha256.h diff --git a/applications/plugins/totp/services/hmac/sha512.c b/applications/external/totp/services/hmac/sha512.c similarity index 100% rename from applications/plugins/totp/services/hmac/sha512.c rename to applications/external/totp/services/hmac/sha512.c diff --git a/applications/plugins/totp/services/hmac/sha512.h b/applications/external/totp/services/hmac/sha512.h similarity index 100% rename from applications/plugins/totp/services/hmac/sha512.h rename to applications/external/totp/services/hmac/sha512.h diff --git a/applications/plugins/totp/services/hmac/u64.h b/applications/external/totp/services/hmac/u64.h similarity index 100% rename from applications/plugins/totp/services/hmac/u64.h rename to applications/external/totp/services/hmac/u64.h diff --git a/applications/plugins/totp/services/totp/totp.c b/applications/external/totp/services/totp/totp.c similarity index 100% rename from applications/plugins/totp/services/totp/totp.c rename to applications/external/totp/services/totp/totp.c diff --git a/applications/plugins/totp/services/totp/totp.h b/applications/external/totp/services/totp/totp.h similarity index 100% rename from applications/plugins/totp/services/totp/totp.h rename to applications/external/totp/services/totp/totp.h diff --git a/applications/plugins/totp/totp_10px.png b/applications/external/totp/totp_10px.png similarity index 100% rename from applications/plugins/totp/totp_10px.png rename to applications/external/totp/totp_10px.png diff --git a/applications/plugins/totp/totp_app.c b/applications/external/totp/totp_app.c similarity index 100% rename from applications/plugins/totp/totp_app.c rename to applications/external/totp/totp_app.c diff --git a/applications/plugins/totp/types/common.h b/applications/external/totp/types/common.h similarity index 100% rename from applications/plugins/totp/types/common.h rename to applications/external/totp/types/common.h diff --git a/applications/plugins/totp/types/event_type.h b/applications/external/totp/types/event_type.h similarity index 100% rename from applications/plugins/totp/types/event_type.h rename to applications/external/totp/types/event_type.h diff --git a/applications/plugins/totp/types/notification_method.h b/applications/external/totp/types/notification_method.h similarity index 100% rename from applications/plugins/totp/types/notification_method.h rename to applications/external/totp/types/notification_method.h diff --git a/applications/plugins/totp/types/nullable.h b/applications/external/totp/types/nullable.h similarity index 100% rename from applications/plugins/totp/types/nullable.h rename to applications/external/totp/types/nullable.h diff --git a/applications/plugins/totp/types/plugin_event.h b/applications/external/totp/types/plugin_event.h similarity index 100% rename from applications/plugins/totp/types/plugin_event.h rename to applications/external/totp/types/plugin_event.h diff --git a/applications/plugins/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h similarity index 100% rename from applications/plugins/totp/types/plugin_state.h rename to applications/external/totp/types/plugin_state.h diff --git a/applications/plugins/totp/types/token_info.c b/applications/external/totp/types/token_info.c similarity index 100% rename from applications/plugins/totp/types/token_info.c rename to applications/external/totp/types/token_info.c diff --git a/applications/plugins/totp/types/token_info.h b/applications/external/totp/types/token_info.h similarity index 100% rename from applications/plugins/totp/types/token_info.h rename to applications/external/totp/types/token_info.h diff --git a/applications/plugins/totp/types/user_pin_codes.h b/applications/external/totp/types/user_pin_codes.h similarity index 100% rename from applications/plugins/totp/types/user_pin_codes.h rename to applications/external/totp/types/user_pin_codes.h diff --git a/applications/plugins/totp/ui/common_dialogs.c b/applications/external/totp/ui/common_dialogs.c similarity index 100% rename from applications/plugins/totp/ui/common_dialogs.c rename to applications/external/totp/ui/common_dialogs.c diff --git a/applications/plugins/totp/ui/common_dialogs.h b/applications/external/totp/ui/common_dialogs.h similarity index 100% rename from applications/plugins/totp/ui/common_dialogs.h rename to applications/external/totp/ui/common_dialogs.h diff --git a/applications/plugins/totp/ui/constants.h b/applications/external/totp/ui/constants.h similarity index 100% rename from applications/plugins/totp/ui/constants.h rename to applications/external/totp/ui/constants.h diff --git a/applications/plugins/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c similarity index 100% rename from applications/plugins/totp/ui/scene_director.c rename to applications/external/totp/ui/scene_director.c diff --git a/applications/plugins/totp/ui/scene_director.h b/applications/external/totp/ui/scene_director.h similarity index 100% rename from applications/plugins/totp/ui/scene_director.h rename to applications/external/totp/ui/scene_director.h diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.c b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c similarity index 100% rename from applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.c rename to applications/external/totp/ui/scenes/add_new_token/totp_input_text.c diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.h b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h similarity index 100% rename from applications/plugins/totp/ui/scenes/add_new_token/totp_input_text.h rename to applications/external/totp/ui/scenes/add_new_token/totp_input_text.h diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c similarity index 100% rename from applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c rename to applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c diff --git a/applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h similarity index 100% rename from applications/plugins/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h rename to applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h diff --git a/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c similarity index 100% rename from applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.c rename to applications/external/totp/ui/scenes/app_settings/totp_app_settings.c diff --git a/applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.h b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.h similarity index 100% rename from applications/plugins/totp/ui/scenes/app_settings/totp_app_settings.h rename to applications/external/totp/ui/scenes/app_settings/totp_app_settings.h diff --git a/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c similarity index 100% rename from applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.c rename to applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c diff --git a/applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.h b/applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h similarity index 100% rename from applications/plugins/totp/ui/scenes/authenticate/totp_scene_authenticate.h rename to applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h diff --git a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c similarity index 100% rename from applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c rename to applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c diff --git a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.h b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h similarity index 100% rename from applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.h rename to applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h diff --git a/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c similarity index 100% rename from applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.c rename to applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c diff --git a/applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.h b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h similarity index 100% rename from applications/plugins/totp/ui/scenes/token_menu/totp_scene_token_menu.h rename to applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h diff --git a/applications/plugins/totp/ui/totp_scenes_enum.h b/applications/external/totp/ui/totp_scenes_enum.h similarity index 100% rename from applications/plugins/totp/ui/totp_scenes_enum.h rename to applications/external/totp/ui/totp_scenes_enum.h diff --git a/applications/plugins/totp/ui/ui_controls.c b/applications/external/totp/ui/ui_controls.c similarity index 100% rename from applications/plugins/totp/ui/ui_controls.c rename to applications/external/totp/ui/ui_controls.c diff --git a/applications/plugins/totp/ui/ui_controls.h b/applications/external/totp/ui/ui_controls.h similarity index 100% rename from applications/plugins/totp/ui/ui_controls.h rename to applications/external/totp/ui/ui_controls.h diff --git a/applications/plugins/totp/workers/type_code/type_code.c b/applications/external/totp/workers/type_code/type_code.c similarity index 100% rename from applications/plugins/totp/workers/type_code/type_code.c rename to applications/external/totp/workers/type_code/type_code.c diff --git a/applications/plugins/totp/workers/type_code/type_code.h b/applications/external/totp/workers/type_code/type_code.h similarity index 100% rename from applications/plugins/totp/workers/type_code/type_code.h rename to applications/external/totp/workers/type_code/type_code.h diff --git a/applications/plugins/uart_terminal/LICENSE b/applications/external/uart_terminal/LICENSE similarity index 100% rename from applications/plugins/uart_terminal/LICENSE rename to applications/external/uart_terminal/LICENSE diff --git a/applications/plugins/uart_terminal/README.md b/applications/external/uart_terminal/README.md similarity index 100% rename from applications/plugins/uart_terminal/README.md rename to applications/external/uart_terminal/README.md diff --git a/applications/plugins/uart_terminal/application.fam b/applications/external/uart_terminal/application.fam similarity index 100% rename from applications/plugins/uart_terminal/application.fam rename to applications/external/uart_terminal/application.fam diff --git a/applications/plugins/uart_terminal/assets/KeyBackspaceSelected_16x9.png b/applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png similarity index 100% rename from applications/plugins/uart_terminal/assets/KeyBackspaceSelected_16x9.png rename to applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png diff --git a/applications/plugins/uart_terminal/assets/KeyBackspace_16x9.png b/applications/external/uart_terminal/assets/KeyBackspace_16x9.png similarity index 100% rename from applications/plugins/uart_terminal/assets/KeyBackspace_16x9.png rename to applications/external/uart_terminal/assets/KeyBackspace_16x9.png diff --git a/applications/plugins/uart_terminal/assets/KeySaveSelected_24x11.png b/applications/external/uart_terminal/assets/KeySaveSelected_24x11.png similarity index 100% rename from applications/plugins/uart_terminal/assets/KeySaveSelected_24x11.png rename to applications/external/uart_terminal/assets/KeySaveSelected_24x11.png diff --git a/applications/plugins/uart_terminal/assets/KeySave_24x11.png b/applications/external/uart_terminal/assets/KeySave_24x11.png similarity index 100% rename from applications/plugins/uart_terminal/assets/KeySave_24x11.png rename to applications/external/uart_terminal/assets/KeySave_24x11.png diff --git a/applications/plugins/uart_terminal/assets/WarningDolphin_45x42.png b/applications/external/uart_terminal/assets/WarningDolphin_45x42.png similarity index 100% rename from applications/plugins/uart_terminal/assets/WarningDolphin_45x42.png rename to applications/external/uart_terminal/assets/WarningDolphin_45x42.png diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene.c b/applications/external/uart_terminal/scenes/uart_terminal_scene.c similarity index 100% rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene.c rename to applications/external/uart_terminal/scenes/uart_terminal_scene.c diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene.h b/applications/external/uart_terminal/scenes/uart_terminal_scene.h similarity index 100% rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene.h rename to applications/external/uart_terminal/scenes/uart_terminal_scene.h diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_config.h b/applications/external/uart_terminal/scenes/uart_terminal_scene_config.h similarity index 100% rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene_config.h rename to applications/external/uart_terminal/scenes/uart_terminal_scene_config.h diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_console_output.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c similarity index 100% rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene_console_output.c rename to applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_start.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_start.c similarity index 100% rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene_start.c rename to applications/external/uart_terminal/scenes/uart_terminal_scene_start.c diff --git a/applications/plugins/uart_terminal/scenes/uart_terminal_scene_text_input.c b/applications/external/uart_terminal/scenes/uart_terminal_scene_text_input.c similarity index 100% rename from applications/plugins/uart_terminal/scenes/uart_terminal_scene_text_input.c rename to applications/external/uart_terminal/scenes/uart_terminal_scene_text_input.c diff --git a/applications/plugins/uart_terminal/uart_terminal.png b/applications/external/uart_terminal/uart_terminal.png similarity index 100% rename from applications/plugins/uart_terminal/uart_terminal.png rename to applications/external/uart_terminal/uart_terminal.png diff --git a/applications/plugins/uart_terminal/uart_terminal_app.c b/applications/external/uart_terminal/uart_terminal_app.c similarity index 100% rename from applications/plugins/uart_terminal/uart_terminal_app.c rename to applications/external/uart_terminal/uart_terminal_app.c diff --git a/applications/plugins/uart_terminal/uart_terminal_app.h b/applications/external/uart_terminal/uart_terminal_app.h similarity index 100% rename from applications/plugins/uart_terminal/uart_terminal_app.h rename to applications/external/uart_terminal/uart_terminal_app.h diff --git a/applications/plugins/uart_terminal/uart_terminal_app_i.h b/applications/external/uart_terminal/uart_terminal_app_i.h similarity index 100% rename from applications/plugins/uart_terminal/uart_terminal_app_i.h rename to applications/external/uart_terminal/uart_terminal_app_i.h diff --git a/applications/plugins/uart_terminal/uart_terminal_custom_event.h b/applications/external/uart_terminal/uart_terminal_custom_event.h similarity index 100% rename from applications/plugins/uart_terminal/uart_terminal_custom_event.h rename to applications/external/uart_terminal/uart_terminal_custom_event.h diff --git a/applications/plugins/uart_terminal/uart_terminal_uart.c b/applications/external/uart_terminal/uart_terminal_uart.c similarity index 100% rename from applications/plugins/uart_terminal/uart_terminal_uart.c rename to applications/external/uart_terminal/uart_terminal_uart.c diff --git a/applications/plugins/uart_terminal/uart_terminal_uart.h b/applications/external/uart_terminal/uart_terminal_uart.h similarity index 100% rename from applications/plugins/uart_terminal/uart_terminal_uart.h rename to applications/external/uart_terminal/uart_terminal_uart.h diff --git a/applications/plugins/uart_terminal/uart_text_input.c b/applications/external/uart_terminal/uart_text_input.c similarity index 100% rename from applications/plugins/uart_terminal/uart_text_input.c rename to applications/external/uart_terminal/uart_text_input.c diff --git a/applications/plugins/uart_terminal/uart_text_input.h b/applications/external/uart_terminal/uart_text_input.h similarity index 100% rename from applications/plugins/uart_terminal/uart_text_input.h rename to applications/external/uart_terminal/uart_text_input.h diff --git a/applications/plugins/uart_terminal/uart_validators.c b/applications/external/uart_terminal/uart_validators.c similarity index 100% rename from applications/plugins/uart_terminal/uart_validators.c rename to applications/external/uart_terminal/uart_validators.c diff --git a/applications/plugins/uart_terminal/uart_validators.h b/applications/external/uart_terminal/uart_validators.h similarity index 100% rename from applications/plugins/uart_terminal/uart_validators.h rename to applications/external/uart_terminal/uart_validators.h diff --git a/applications/plugins/unitemp/LICENSE.md b/applications/external/unitemp/LICENSE.md similarity index 100% rename from applications/plugins/unitemp/LICENSE.md rename to applications/external/unitemp/LICENSE.md diff --git a/applications/plugins/unitemp/README.md b/applications/external/unitemp/README.md similarity index 100% rename from applications/plugins/unitemp/README.md rename to applications/external/unitemp/README.md diff --git a/applications/plugins/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c similarity index 100% rename from applications/plugins/unitemp/Sensors.c rename to applications/external/unitemp/Sensors.c diff --git a/applications/plugins/unitemp/Sensors.h b/applications/external/unitemp/Sensors.h similarity index 100% rename from applications/plugins/unitemp/Sensors.h rename to applications/external/unitemp/Sensors.h diff --git a/applications/plugins/unitemp/application.fam b/applications/external/unitemp/application.fam similarity index 100% rename from applications/plugins/unitemp/application.fam rename to applications/external/unitemp/application.fam diff --git a/applications/plugins/unitemp/assets/README.MD b/applications/external/unitemp/assets/README.MD similarity index 100% rename from applications/plugins/unitemp/assets/README.MD rename to applications/external/unitemp/assets/README.MD diff --git a/applications/plugins/unitemp/assets/flipper_happy_2_60x38.png b/applications/external/unitemp/assets/flipper_happy_2_60x38.png similarity index 100% rename from applications/plugins/unitemp/assets/flipper_happy_2_60x38.png rename to applications/external/unitemp/assets/flipper_happy_2_60x38.png diff --git a/applications/plugins/unitemp/assets/flipper_happy_60x38.png b/applications/external/unitemp/assets/flipper_happy_60x38.png similarity index 100% rename from applications/plugins/unitemp/assets/flipper_happy_60x38.png rename to applications/external/unitemp/assets/flipper_happy_60x38.png diff --git a/applications/plugins/unitemp/assets/flipper_sad_60x38.png b/applications/external/unitemp/assets/flipper_sad_60x38.png similarity index 100% rename from applications/plugins/unitemp/assets/flipper_sad_60x38.png rename to applications/external/unitemp/assets/flipper_sad_60x38.png diff --git a/applications/plugins/unitemp/assets/hum_9x15.png b/applications/external/unitemp/assets/hum_9x15.png similarity index 100% rename from applications/plugins/unitemp/assets/hum_9x15.png rename to applications/external/unitemp/assets/hum_9x15.png diff --git a/applications/plugins/unitemp/assets/in_hg_15x15.png b/applications/external/unitemp/assets/in_hg_15x15.png similarity index 100% rename from applications/plugins/unitemp/assets/in_hg_15x15.png rename to applications/external/unitemp/assets/in_hg_15x15.png diff --git a/applications/plugins/unitemp/assets/mm_hg_15x15.png b/applications/external/unitemp/assets/mm_hg_15x15.png similarity index 100% rename from applications/plugins/unitemp/assets/mm_hg_15x15.png rename to applications/external/unitemp/assets/mm_hg_15x15.png diff --git a/applications/plugins/unitemp/assets/pressure_7x13.png b/applications/external/unitemp/assets/pressure_7x13.png similarity index 100% rename from applications/plugins/unitemp/assets/pressure_7x13.png rename to applications/external/unitemp/assets/pressure_7x13.png diff --git a/applications/plugins/unitemp/assets/repo_qr_50x50.png b/applications/external/unitemp/assets/repo_qr_50x50.png similarity index 100% rename from applications/plugins/unitemp/assets/repo_qr_50x50.png rename to applications/external/unitemp/assets/repo_qr_50x50.png diff --git a/applications/plugins/unitemp/assets/sherlok_53x45.png b/applications/external/unitemp/assets/sherlok_53x45.png similarity index 100% rename from applications/plugins/unitemp/assets/sherlok_53x45.png rename to applications/external/unitemp/assets/sherlok_53x45.png diff --git a/applications/plugins/unitemp/assets/temp_C_11x14.png b/applications/external/unitemp/assets/temp_C_11x14.png similarity index 100% rename from applications/plugins/unitemp/assets/temp_C_11x14.png rename to applications/external/unitemp/assets/temp_C_11x14.png diff --git a/applications/plugins/unitemp/assets/temp_F_11x14.png b/applications/external/unitemp/assets/temp_F_11x14.png similarity index 100% rename from applications/plugins/unitemp/assets/temp_F_11x14.png rename to applications/external/unitemp/assets/temp_F_11x14.png diff --git a/applications/plugins/unitemp/icon.png b/applications/external/unitemp/icon.png similarity index 100% rename from applications/plugins/unitemp/icon.png rename to applications/external/unitemp/icon.png diff --git a/applications/plugins/unitemp/interfaces/I2CSensor.c b/applications/external/unitemp/interfaces/I2CSensor.c similarity index 100% rename from applications/plugins/unitemp/interfaces/I2CSensor.c rename to applications/external/unitemp/interfaces/I2CSensor.c diff --git a/applications/plugins/unitemp/interfaces/I2CSensor.h b/applications/external/unitemp/interfaces/I2CSensor.h similarity index 100% rename from applications/plugins/unitemp/interfaces/I2CSensor.h rename to applications/external/unitemp/interfaces/I2CSensor.h diff --git a/applications/plugins/unitemp/interfaces/OneWireSensor.c b/applications/external/unitemp/interfaces/OneWireSensor.c similarity index 100% rename from applications/plugins/unitemp/interfaces/OneWireSensor.c rename to applications/external/unitemp/interfaces/OneWireSensor.c diff --git a/applications/plugins/unitemp/interfaces/OneWireSensor.h b/applications/external/unitemp/interfaces/OneWireSensor.h similarity index 100% rename from applications/plugins/unitemp/interfaces/OneWireSensor.h rename to applications/external/unitemp/interfaces/OneWireSensor.h diff --git a/applications/plugins/unitemp/interfaces/SPISensor.c b/applications/external/unitemp/interfaces/SPISensor.c similarity index 100% rename from applications/plugins/unitemp/interfaces/SPISensor.c rename to applications/external/unitemp/interfaces/SPISensor.c diff --git a/applications/plugins/unitemp/interfaces/SPISensor.h b/applications/external/unitemp/interfaces/SPISensor.h similarity index 100% rename from applications/plugins/unitemp/interfaces/SPISensor.h rename to applications/external/unitemp/interfaces/SPISensor.h diff --git a/applications/plugins/unitemp/interfaces/SingleWireSensor.c b/applications/external/unitemp/interfaces/SingleWireSensor.c similarity index 100% rename from applications/plugins/unitemp/interfaces/SingleWireSensor.c rename to applications/external/unitemp/interfaces/SingleWireSensor.c diff --git a/applications/plugins/unitemp/interfaces/SingleWireSensor.h b/applications/external/unitemp/interfaces/SingleWireSensor.h similarity index 100% rename from applications/plugins/unitemp/interfaces/SingleWireSensor.h rename to applications/external/unitemp/interfaces/SingleWireSensor.h diff --git a/applications/plugins/unitemp/sensors/AM2320.c b/applications/external/unitemp/sensors/AM2320.c similarity index 100% rename from applications/plugins/unitemp/sensors/AM2320.c rename to applications/external/unitemp/sensors/AM2320.c diff --git a/applications/plugins/unitemp/sensors/AM2320.h b/applications/external/unitemp/sensors/AM2320.h similarity index 100% rename from applications/plugins/unitemp/sensors/AM2320.h rename to applications/external/unitemp/sensors/AM2320.h diff --git a/applications/plugins/unitemp/sensors/BME680.c b/applications/external/unitemp/sensors/BME680.c similarity index 100% rename from applications/plugins/unitemp/sensors/BME680.c rename to applications/external/unitemp/sensors/BME680.c diff --git a/applications/plugins/unitemp/sensors/BME680.h b/applications/external/unitemp/sensors/BME680.h similarity index 100% rename from applications/plugins/unitemp/sensors/BME680.h rename to applications/external/unitemp/sensors/BME680.h diff --git a/applications/plugins/unitemp/sensors/BMP180.c b/applications/external/unitemp/sensors/BMP180.c similarity index 100% rename from applications/plugins/unitemp/sensors/BMP180.c rename to applications/external/unitemp/sensors/BMP180.c diff --git a/applications/plugins/unitemp/sensors/BMP180.h b/applications/external/unitemp/sensors/BMP180.h similarity index 100% rename from applications/plugins/unitemp/sensors/BMP180.h rename to applications/external/unitemp/sensors/BMP180.h diff --git a/applications/plugins/unitemp/sensors/BMx280.c b/applications/external/unitemp/sensors/BMx280.c similarity index 100% rename from applications/plugins/unitemp/sensors/BMx280.c rename to applications/external/unitemp/sensors/BMx280.c diff --git a/applications/plugins/unitemp/sensors/BMx280.h b/applications/external/unitemp/sensors/BMx280.h similarity index 100% rename from applications/plugins/unitemp/sensors/BMx280.h rename to applications/external/unitemp/sensors/BMx280.h diff --git a/applications/plugins/unitemp/sensors/DHT20.c b/applications/external/unitemp/sensors/DHT20.c similarity index 100% rename from applications/plugins/unitemp/sensors/DHT20.c rename to applications/external/unitemp/sensors/DHT20.c diff --git a/applications/plugins/unitemp/sensors/DHT20.h b/applications/external/unitemp/sensors/DHT20.h similarity index 100% rename from applications/plugins/unitemp/sensors/DHT20.h rename to applications/external/unitemp/sensors/DHT20.h diff --git a/applications/plugins/unitemp/sensors/HDC1080.c b/applications/external/unitemp/sensors/HDC1080.c similarity index 100% rename from applications/plugins/unitemp/sensors/HDC1080.c rename to applications/external/unitemp/sensors/HDC1080.c diff --git a/applications/plugins/unitemp/sensors/HDC1080.h b/applications/external/unitemp/sensors/HDC1080.h similarity index 100% rename from applications/plugins/unitemp/sensors/HDC1080.h rename to applications/external/unitemp/sensors/HDC1080.h diff --git a/applications/plugins/unitemp/sensors/HTU21x.c b/applications/external/unitemp/sensors/HTU21x.c similarity index 100% rename from applications/plugins/unitemp/sensors/HTU21x.c rename to applications/external/unitemp/sensors/HTU21x.c diff --git a/applications/plugins/unitemp/sensors/HTU21x.h b/applications/external/unitemp/sensors/HTU21x.h similarity index 100% rename from applications/plugins/unitemp/sensors/HTU21x.h rename to applications/external/unitemp/sensors/HTU21x.h diff --git a/applications/plugins/unitemp/sensors/LM75.c b/applications/external/unitemp/sensors/LM75.c similarity index 100% rename from applications/plugins/unitemp/sensors/LM75.c rename to applications/external/unitemp/sensors/LM75.c diff --git a/applications/plugins/unitemp/sensors/LM75.h b/applications/external/unitemp/sensors/LM75.h similarity index 100% rename from applications/plugins/unitemp/sensors/LM75.h rename to applications/external/unitemp/sensors/LM75.h diff --git a/applications/plugins/unitemp/sensors/MAX31855.c b/applications/external/unitemp/sensors/MAX31855.c similarity index 100% rename from applications/plugins/unitemp/sensors/MAX31855.c rename to applications/external/unitemp/sensors/MAX31855.c diff --git a/applications/plugins/unitemp/sensors/MAX31855.h b/applications/external/unitemp/sensors/MAX31855.h similarity index 100% rename from applications/plugins/unitemp/sensors/MAX31855.h rename to applications/external/unitemp/sensors/MAX31855.h diff --git a/applications/plugins/unitemp/sensors/MAX6675.c b/applications/external/unitemp/sensors/MAX6675.c similarity index 100% rename from applications/plugins/unitemp/sensors/MAX6675.c rename to applications/external/unitemp/sensors/MAX6675.c diff --git a/applications/plugins/unitemp/sensors/MAX6675.h b/applications/external/unitemp/sensors/MAX6675.h similarity index 100% rename from applications/plugins/unitemp/sensors/MAX6675.h rename to applications/external/unitemp/sensors/MAX6675.h diff --git a/applications/plugins/unitemp/sensors/SHT30.c b/applications/external/unitemp/sensors/SHT30.c similarity index 100% rename from applications/plugins/unitemp/sensors/SHT30.c rename to applications/external/unitemp/sensors/SHT30.c diff --git a/applications/plugins/unitemp/sensors/SHT30.h b/applications/external/unitemp/sensors/SHT30.h similarity index 100% rename from applications/plugins/unitemp/sensors/SHT30.h rename to applications/external/unitemp/sensors/SHT30.h diff --git a/applications/plugins/unitemp/sensors/Sensors.xlsx b/applications/external/unitemp/sensors/Sensors.xlsx similarity index 100% rename from applications/plugins/unitemp/sensors/Sensors.xlsx rename to applications/external/unitemp/sensors/Sensors.xlsx diff --git a/applications/plugins/unitemp/unitemp.c b/applications/external/unitemp/unitemp.c similarity index 100% rename from applications/plugins/unitemp/unitemp.c rename to applications/external/unitemp/unitemp.c diff --git a/applications/plugins/unitemp/unitemp.h b/applications/external/unitemp/unitemp.h similarity index 100% rename from applications/plugins/unitemp/unitemp.h rename to applications/external/unitemp/unitemp.h diff --git a/applications/plugins/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c similarity index 100% rename from applications/plugins/unitemp/views/General_view.c rename to applications/external/unitemp/views/General_view.c diff --git a/applications/plugins/unitemp/views/MainMenu_view.c b/applications/external/unitemp/views/MainMenu_view.c similarity index 100% rename from applications/plugins/unitemp/views/MainMenu_view.c rename to applications/external/unitemp/views/MainMenu_view.c diff --git a/applications/plugins/unitemp/views/Popup_view.c b/applications/external/unitemp/views/Popup_view.c similarity index 100% rename from applications/plugins/unitemp/views/Popup_view.c rename to applications/external/unitemp/views/Popup_view.c diff --git a/applications/plugins/unitemp/views/SensorActions_view.c b/applications/external/unitemp/views/SensorActions_view.c similarity index 100% rename from applications/plugins/unitemp/views/SensorActions_view.c rename to applications/external/unitemp/views/SensorActions_view.c diff --git a/applications/plugins/unitemp/views/SensorEdit_view.c b/applications/external/unitemp/views/SensorEdit_view.c similarity index 100% rename from applications/plugins/unitemp/views/SensorEdit_view.c rename to applications/external/unitemp/views/SensorEdit_view.c diff --git a/applications/plugins/unitemp/views/SensorNameEdit_view.c b/applications/external/unitemp/views/SensorNameEdit_view.c similarity index 100% rename from applications/plugins/unitemp/views/SensorNameEdit_view.c rename to applications/external/unitemp/views/SensorNameEdit_view.c diff --git a/applications/plugins/unitemp/views/SensorsList_view.c b/applications/external/unitemp/views/SensorsList_view.c similarity index 100% rename from applications/plugins/unitemp/views/SensorsList_view.c rename to applications/external/unitemp/views/SensorsList_view.c diff --git a/applications/plugins/unitemp/views/Settings_view.c b/applications/external/unitemp/views/Settings_view.c similarity index 100% rename from applications/plugins/unitemp/views/Settings_view.c rename to applications/external/unitemp/views/Settings_view.c diff --git a/applications/plugins/unitemp/views/UnitempViews.h b/applications/external/unitemp/views/UnitempViews.h similarity index 100% rename from applications/plugins/unitemp/views/UnitempViews.h rename to applications/external/unitemp/views/UnitempViews.h diff --git a/applications/plugins/unitemp/views/Widgets_view.c b/applications/external/unitemp/views/Widgets_view.c similarity index 100% rename from applications/plugins/unitemp/views/Widgets_view.c rename to applications/external/unitemp/views/Widgets_view.c diff --git a/applications/plugins/wav_player/README.md b/applications/external/wav_player/README.md similarity index 100% rename from applications/plugins/wav_player/README.md rename to applications/external/wav_player/README.md diff --git a/applications/plugins/wav_player/application.fam b/applications/external/wav_player/application.fam similarity index 100% rename from applications/plugins/wav_player/application.fam rename to applications/external/wav_player/application.fam diff --git a/applications/plugins/wav_player/images/music_10px.png b/applications/external/wav_player/images/music_10px.png similarity index 100% rename from applications/plugins/wav_player/images/music_10px.png rename to applications/external/wav_player/images/music_10px.png diff --git a/applications/plugins/wav_player/wav_10px.png b/applications/external/wav_player/wav_10px.png similarity index 100% rename from applications/plugins/wav_player/wav_10px.png rename to applications/external/wav_player/wav_10px.png diff --git a/applications/plugins/wav_player/wav_parser.c b/applications/external/wav_player/wav_parser.c similarity index 100% rename from applications/plugins/wav_player/wav_parser.c rename to applications/external/wav_player/wav_parser.c diff --git a/applications/plugins/wav_player/wav_parser.h b/applications/external/wav_player/wav_parser.h similarity index 100% rename from applications/plugins/wav_player/wav_parser.h rename to applications/external/wav_player/wav_parser.h diff --git a/applications/plugins/wav_player/wav_player.c b/applications/external/wav_player/wav_player.c similarity index 100% rename from applications/plugins/wav_player/wav_player.c rename to applications/external/wav_player/wav_player.c diff --git a/applications/plugins/wav_player/wav_player_hal.c b/applications/external/wav_player/wav_player_hal.c similarity index 100% rename from applications/plugins/wav_player/wav_player_hal.c rename to applications/external/wav_player/wav_player_hal.c diff --git a/applications/plugins/wav_player/wav_player_hal.h b/applications/external/wav_player/wav_player_hal.h similarity index 100% rename from applications/plugins/wav_player/wav_player_hal.h rename to applications/external/wav_player/wav_player_hal.h diff --git a/applications/plugins/wav_player/wav_player_view.c b/applications/external/wav_player/wav_player_view.c similarity index 100% rename from applications/plugins/wav_player/wav_player_view.c rename to applications/external/wav_player/wav_player_view.c diff --git a/applications/plugins/wav_player/wav_player_view.h b/applications/external/wav_player/wav_player_view.h similarity index 100% rename from applications/plugins/wav_player/wav_player_view.h rename to applications/external/wav_player/wav_player_view.h diff --git a/applications/plugins/wifi_marauder_companion/application.fam b/applications/external/wifi_marauder_companion/application.fam similarity index 100% rename from applications/plugins/wifi_marauder_companion/application.fam rename to applications/external/wifi_marauder_companion/application.fam diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.c similarity index 100% rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene.c rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.c diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene.h b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.h similarity index 100% rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene.h rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.h diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h similarity index 100% rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c similarity index 100% rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c similarity index 100% rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c diff --git a/applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c similarity index 100% rename from applications/plugins/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c rename to applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c diff --git a/applications/plugins/wifi_marauder_companion/wifi_10px.png b/applications/external/wifi_marauder_companion/wifi_10px.png similarity index 100% rename from applications/plugins/wifi_marauder_companion/wifi_10px.png rename to applications/external/wifi_marauder_companion/wifi_10px.png diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c similarity index 100% rename from applications/plugins/wifi_marauder_companion/wifi_marauder_app.c rename to applications/external/wifi_marauder_companion/wifi_marauder_app.c diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_app.h b/applications/external/wifi_marauder_companion/wifi_marauder_app.h similarity index 100% rename from applications/plugins/wifi_marauder_companion/wifi_marauder_app.h rename to applications/external/wifi_marauder_companion/wifi_marauder_app.h diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h similarity index 100% rename from applications/plugins/wifi_marauder_companion/wifi_marauder_app_i.h rename to applications/external/wifi_marauder_companion/wifi_marauder_app_i.h diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_custom_event.h b/applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h similarity index 100% rename from applications/plugins/wifi_marauder_companion/wifi_marauder_custom_event.h rename to applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.c b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c similarity index 100% rename from applications/plugins/wifi_marauder_companion/wifi_marauder_uart.c rename to applications/external/wifi_marauder_companion/wifi_marauder_uart.c diff --git a/applications/plugins/wifi_marauder_companion/wifi_marauder_uart.h b/applications/external/wifi_marauder_companion/wifi_marauder_uart.h similarity index 100% rename from applications/plugins/wifi_marauder_companion/wifi_marauder_uart.h rename to applications/external/wifi_marauder_companion/wifi_marauder_uart.h diff --git a/applications/plugins/wifi_scanner/FlipperZeroWiFiModuleDefines.h b/applications/external/wifi_scanner/FlipperZeroWiFiModuleDefines.h similarity index 100% rename from applications/plugins/wifi_scanner/FlipperZeroWiFiModuleDefines.h rename to applications/external/wifi_scanner/FlipperZeroWiFiModuleDefines.h diff --git a/applications/plugins/wifi_scanner/application.fam b/applications/external/wifi_scanner/application.fam similarity index 100% rename from applications/plugins/wifi_scanner/application.fam rename to applications/external/wifi_scanner/application.fam diff --git a/applications/plugins/wifi_scanner/wifi_10px.png b/applications/external/wifi_scanner/wifi_10px.png similarity index 100% rename from applications/plugins/wifi_scanner/wifi_10px.png rename to applications/external/wifi_scanner/wifi_10px.png diff --git a/applications/plugins/wifi_scanner/wifi_scanner.c b/applications/external/wifi_scanner/wifi_scanner.c similarity index 100% rename from applications/plugins/wifi_scanner/wifi_scanner.c rename to applications/external/wifi_scanner/wifi_scanner.c diff --git a/applications/plugins/zombiez/application.fam b/applications/external/zombiez/application.fam similarity index 100% rename from applications/plugins/zombiez/application.fam rename to applications/external/zombiez/application.fam diff --git a/applications/plugins/zombiez/zombie_10px.png b/applications/external/zombiez/zombie_10px.png similarity index 100% rename from applications/plugins/zombiez/zombie_10px.png rename to applications/external/zombiez/zombie_10px.png diff --git a/applications/plugins/zombiez/zombiez.c b/applications/external/zombiez/zombiez.c similarity index 100% rename from applications/plugins/zombiez/zombiez.c rename to applications/external/zombiez/zombiez.c diff --git a/applications/plugins/zombiez/zombiez.h b/applications/external/zombiez/zombiez.h similarity index 100% rename from applications/plugins/zombiez/zombiez.h rename to applications/external/zombiez/zombiez.h diff --git a/applications/plugins/dap_link/lib/free-dap b/applications/plugins/dap_link/lib/free-dap deleted file mode 160000 index e7752beb5..000000000 --- a/applications/plugins/dap_link/lib/free-dap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e7752beb5e8a69119af67b70b9179cb3c90f3ac5 From 05e53cac2647236da455952c643917609c56a1a6 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Tue, 14 Mar 2023 20:25:33 +0300 Subject: [PATCH 10/34] Fix merge issues --- .ci_files/anims_ofw.txt | 4 +- ReadMe.md | 4 +- applications/debug/usb_mouse/application.fam | 2 +- applications/external/dap_link/README.md | 105 ++++ applications/external/dap_link/dap_config.h | 234 ++++++++ applications/external/dap_link/dap_link.c | 527 ++++++++++++++++++ applications/external/dap_link/dap_link.h | 55 ++ applications/external/dap_link/gui/dap_gui.c | 92 +++ applications/external/dap_link/gui/dap_gui.h | 4 + .../dap_link/gui/dap_gui_custom_event.h | 7 + .../external/dap_link/gui/dap_gui_i.h | 34 ++ .../gui/scenes/config/dap_scene_config.h | 4 + .../dap_link/gui/scenes/dap_scene_config.c | 107 ++++ .../dap_link/gui/scenes/dap_scene_help.c | 102 ++++ .../dap_link/gui/scenes/dap_scene_main.c | 154 +++++ .../dap_link/gui/views/dap_main_view.c | 189 +++++++ .../dap_link/gui/views/dap_main_view.h | 45 ++ .../dap_link/icons/ActiveConnection_50x64.png | Bin 0 -> 3842 bytes .../dap_link/icons/ArrowUpFilled_12x18.png | Bin 0 -> 173 bytes applications/external/hid_app/application.fam | 8 +- .../external/music_player/application.fam | 2 +- .../external/signal_generator/application.fam | 2 +- 22 files changed, 1670 insertions(+), 11 deletions(-) create mode 100644 applications/external/dap_link/README.md create mode 100644 applications/external/dap_link/dap_config.h create mode 100644 applications/external/dap_link/dap_link.c create mode 100644 applications/external/dap_link/dap_link.h create mode 100644 applications/external/dap_link/gui/dap_gui.c create mode 100644 applications/external/dap_link/gui/dap_gui.h create mode 100644 applications/external/dap_link/gui/dap_gui_custom_event.h create mode 100644 applications/external/dap_link/gui/dap_gui_i.h create mode 100644 applications/external/dap_link/gui/scenes/config/dap_scene_config.h create mode 100644 applications/external/dap_link/gui/scenes/dap_scene_config.c create mode 100644 applications/external/dap_link/gui/scenes/dap_scene_help.c create mode 100644 applications/external/dap_link/gui/scenes/dap_scene_main.c create mode 100644 applications/external/dap_link/gui/views/dap_main_view.c create mode 100644 applications/external/dap_link/gui/views/dap_main_view.h create mode 100644 applications/external/dap_link/icons/ActiveConnection_50x64.png create mode 100644 applications/external/dap_link/icons/ArrowUpFilled_12x18.png diff --git a/.ci_files/anims_ofw.txt b/.ci_files/anims_ofw.txt index 94c61e0ff..a9a6fd241 100644 --- a/.ci_files/anims_ofw.txt +++ b/.ci_files/anims_ofw.txt @@ -134,9 +134,9 @@ Min level: 3 Max level: 3 Weight: 3 -Name: L1_Sleigh_ride_128x64 +Name: L1_Senpai_128x64 Min butthurt: 0 -Max butthurt: 14 +Max butthurt: 5 Min level: 1 Max level: 3 Weight: 4 diff --git a/ReadMe.md b/ReadMe.md index ed31961fa..84b80a1e8 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -211,9 +211,9 @@ Games: ## [- How to use: Unitemp - Temperature sensors reader](https://github.com/quen0n/unitemp-flipperzero#readme) -## [- How to use: [NMEA] GPS](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/plugins/gps_nmea_uart/README.md) +## [- How to use: [NMEA] GPS](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/external/gps_nmea_uart/README.md) -## [- How to use: i2c Tools](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/plugins/flipper_i2ctools/README.md) +## [- How to use: i2c Tools](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/external/flipper_i2ctools/README.md) ## [- How to use: [NRF24] plugins](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/NRF24.md) diff --git a/applications/debug/usb_mouse/application.fam b/applications/debug/usb_mouse/application.fam index bf555c671..38ba55425 100644 --- a/applications/debug/usb_mouse/application.fam +++ b/applications/debug/usb_mouse/application.fam @@ -7,6 +7,6 @@ App( requires=["gui"], stack_size=1 * 1024, order=60, - fap_icon="../../plugins/mousejacker/mouse_10px.png", + fap_icon="../../external/mousejacker/mouse_10px.png", fap_category="Debug", ) diff --git a/applications/external/dap_link/README.md b/applications/external/dap_link/README.md new file mode 100644 index 000000000..aead0a60a --- /dev/null +++ b/applications/external/dap_link/README.md @@ -0,0 +1,105 @@ +# Flipper Zero as CMSIS DAP/DAP Link +Flipper Zero as a [Free-DAP](https://github.com/ataradov/free-dap) based SWD\JTAG debugger. Free-DAP is a free and open source firmware implementation of the [CMSIS-DAP](https://www.keil.com/pack/doc/CMSIS_Dev/DAP/html/index.html) debugger. + +## Protocols +SWD, JTAG , CMSIS-DAP v1 (18 KiB/s), CMSIS-DAP v2 (46 KiB/s), VCP (USB-UART). + +WinUSB for driverless installation for Windows 8 and above. + +## Usage + +### VSCode + Cortex-Debug + Set `"device": "cmsis-dap"` + +
+ BluePill configuration example + + ```json +{ + "name": "Attach (DAP)", + "cwd": "${workspaceFolder}", + "executable": "./build/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "configFiles": [ + "interface/cmsis-dap.cfg", + "target/stm32f1x.cfg", + ], +}, + ``` +
+ +
+ Flipper Zero configuration example + + ```json +{ + "name": "Attach (DAP)", + "cwd": "${workspaceFolder}", + "executable": "./build/latest/firmware.elf", + "request": "attach", + "type": "cortex-debug", + "servertype": "openocd", + "device": "cmsis-dap", + "svdFile": "./debug/STM32WB55_CM4.svd", + "rtos": "FreeRTOS", + "configFiles": [ + "interface/cmsis-dap.cfg", + "./debug/stm32wbx.cfg", + ], + "postAttachCommands": [ + "source debug/flipperapps.py", + ], +}, + ``` +
+ +### OpenOCD +Use `interface/cmsis-dap.cfg`. You will need OpenOCD v0.11.0. + +Additional commands: +* `cmsis_dap_backend hid` for CMSIS-DAP v1 protocol. +* `cmsis_dap_backend usb_bulk` for CMSIS-DAP v2 protocol. +* `cmsis_dap_serial DAP_Oyevoxo` use DAP-Link running on Flipper named `Oyevoxo`. +* `cmsis-dap cmd 81` - reboot connected DAP-Link. + +
+ Flash BluePill + + ``` +openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c init -c "program build/firmware.bin reset exit 0x8000000" + ``` +
+ +
+ Flash Flipper Zero using DAP v2 protocol + + ``` +openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_backend usb_bulk" -f debug/stm32wbx.cfg -c init -c "program build/latest/firmware.bin reset exit 0x8000000" + ``` +
+ +
+ Reboot connected DAP-Link on Flipper named Oyevoxo + + ``` +openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_serial DAP_Oyevoxo" -c "transport select swd" -c "adapter speed 4000000" -c init -c "cmsis-dap cmd 81" -c "exit" + ``` +
+ +### PlatformIO +Use `debug_tool = cmsis-dap` and `upload_protocol = cmsis-dap`. [Documentation](https://docs.platformio.org/en/latest/plus/debug-tools/cmsis-dap.html#debugging-tool-cmsis-dap). Remember that Windows 8 and above do not require drivers. + +
+ BluePill platformio.ini example + + ``` +[env:bluepill_f103c8] +platform = ststm32 +board = bluepill_f103c8 +debug_tool = cmsis-dap +upload_protocol = cmsis-dap + ``` +
diff --git a/applications/external/dap_link/dap_config.h b/applications/external/dap_link/dap_config.h new file mode 100644 index 000000000..88b90bd34 --- /dev/null +++ b/applications/external/dap_link/dap_config.h @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright (c) 2022, Alex Taradov . All rights reserved. + +#ifndef _DAP_CONFIG_H_ +#define _DAP_CONFIG_H_ + +/*- Includes ----------------------------------------------------------------*/ +#include + +/*- Definitions -------------------------------------------------------------*/ +#define DAP_CONFIG_ENABLE_JTAG + +#define DAP_CONFIG_DEFAULT_PORT DAP_PORT_SWD +#define DAP_CONFIG_DEFAULT_CLOCK 4200000 // Hz + +#define DAP_CONFIG_PACKET_SIZE 64 +#define DAP_CONFIG_PACKET_COUNT 1 + +#define DAP_CONFIG_JTAG_DEV_COUNT 8 + +// DAP_CONFIG_PRODUCT_STR must contain "CMSIS-DAP" to be compatible with the standard +#define DAP_CONFIG_VENDOR_STR "Flipper Zero" +#define DAP_CONFIG_PRODUCT_STR "Generic CMSIS-DAP Adapter" +#define DAP_CONFIG_SER_NUM_STR usb_serial_number +#define DAP_CONFIG_CMSIS_DAP_VER_STR "2.0.0" + +#define DAP_CONFIG_RESET_TARGET_FN dap_app_target_reset +#define DAP_CONFIG_VENDOR_FN dap_app_vendor_cmd + +// Attribute to use for performance-critical functions +#define DAP_CONFIG_PERFORMANCE_ATTR + +// A value at which dap_clock_test() produces 1 kHz output on the SWCLK pin +// #define DAP_CONFIG_DELAY_CONSTANT 19000 +#define DAP_CONFIG_DELAY_CONSTANT 6290 + +// A threshold for switching to fast clock (no added delays) +// This is the frequency produced by dap_clock_test(1) on the SWCLK pin +#define DAP_CONFIG_FAST_CLOCK 2400000 // Hz + +/*- Prototypes --------------------------------------------------------------*/ +extern char usb_serial_number[16]; + +/*- Implementations ---------------------------------------------------------*/ +extern GpioPin flipper_dap_swclk_pin; +extern GpioPin flipper_dap_swdio_pin; +extern GpioPin flipper_dap_reset_pin; +extern GpioPin flipper_dap_tdo_pin; +extern GpioPin flipper_dap_tdi_pin; + +extern void dap_app_vendor_cmd(uint8_t cmd); +extern void dap_app_target_reset(); +extern void dap_app_disconnect(); +extern void dap_app_connect_swd(); +extern void dap_app_connect_jtag(); + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWCLK_TCK_write(int value) { + furi_hal_gpio_write(&flipper_dap_swclk_pin, value); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWDIO_TMS_write(int value) { + furi_hal_gpio_write(&flipper_dap_swdio_pin, value); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_TDI_write(int value) { +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_write(&flipper_dap_tdi_pin, value); +#else + (void)value; +#endif +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_TDO_write(int value) { +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_write(&flipper_dap_tdo_pin, value); +#else + (void)value; +#endif +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_nTRST_write(int value) { + (void)value; +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_nRESET_write(int value) { + furi_hal_gpio_write(&flipper_dap_reset_pin, value); +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_SWCLK_TCK_read(void) { + return furi_hal_gpio_read(&flipper_dap_swclk_pin); +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_SWDIO_TMS_read(void) { + return furi_hal_gpio_read(&flipper_dap_swdio_pin); +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_TDO_read(void) { +#ifdef DAP_CONFIG_ENABLE_JTAG + return furi_hal_gpio_read(&flipper_dap_tdo_pin); +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_TDI_read(void) { +#ifdef DAP_CONFIG_ENABLE_JTAG + return furi_hal_gpio_read(&flipper_dap_tdi_pin); +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_nTRST_read(void) { + return 0; +} + +//----------------------------------------------------------------------------- +static inline int DAP_CONFIG_nRESET_read(void) { + return furi_hal_gpio_read(&flipper_dap_reset_pin); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWCLK_TCK_set(void) { + LL_GPIO_SetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWCLK_TCK_clr(void) { + LL_GPIO_ResetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWDIO_TMS_in(void) { + LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_INPUT); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SWDIO_TMS_out(void) { + LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_OUTPUT); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_SETUP(void) { + furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#endif +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_DISCONNECT(void) { + furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#endif + dap_app_disconnect(); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_CONNECT_SWD(void) { + furi_hal_gpio_init( + &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swdio_pin, true); + + furi_hal_gpio_init( + &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swclk_pin, true); + + furi_hal_gpio_init( + &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_reset_pin, true); + +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +#endif + dap_app_connect_swd(); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_CONNECT_JTAG(void) { + furi_hal_gpio_init( + &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swdio_pin, true); + + furi_hal_gpio_init( + &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_swclk_pin, true); + + furi_hal_gpio_init( + &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_reset_pin, true); + +#ifdef DAP_CONFIG_ENABLE_JTAG + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + + furi_hal_gpio_init( + &flipper_dap_tdi_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&flipper_dap_tdi_pin, true); +#endif + dap_app_connect_jtag(); +} + +//----------------------------------------------------------------------------- +static inline void DAP_CONFIG_LED(int index, int state) { + (void)index; + (void)state; +} + +//----------------------------------------------------------------------------- +__attribute__((always_inline)) static inline void DAP_CONFIG_DELAY(uint32_t cycles) { + asm volatile("1: subs %[cycles], %[cycles], #1 \n" + " bne 1b \n" + : [cycles] "+l"(cycles)); +} + +#endif // _DAP_CONFIG_H_ diff --git a/applications/external/dap_link/dap_link.c b/applications/external/dap_link/dap_link.c new file mode 100644 index 000000000..eafb435e7 --- /dev/null +++ b/applications/external/dap_link/dap_link.c @@ -0,0 +1,527 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dap_link.h" +#include "dap_config.h" +#include "gui/dap_gui.h" +#include "usb/dap_v2_usb.h" +#include +#include "dap_link_icons.h" + +/***************************************************************************/ +/****************************** DAP COMMON *********************************/ +/***************************************************************************/ + +struct DapApp { + FuriThread* dap_thread; + FuriThread* cdc_thread; + FuriThread* gui_thread; + + DapState state; + DapConfig config; +}; + +void dap_app_get_state(DapApp* app, DapState* state) { + *state = app->state; +} + +#define DAP_PROCESS_THREAD_TICK 500 + +typedef enum { + DapThreadEventStop = (1 << 0), +} DapThreadEvent; + +void dap_thread_send_stop(FuriThread* thread) { + furi_thread_flags_set(furi_thread_get_id(thread), DapThreadEventStop); +} + +GpioPin flipper_dap_swclk_pin; +GpioPin flipper_dap_swdio_pin; +GpioPin flipper_dap_reset_pin; +GpioPin flipper_dap_tdo_pin; +GpioPin flipper_dap_tdi_pin; + +/***************************************************************************/ +/****************************** DAP PROCESS ********************************/ +/***************************************************************************/ + +typedef struct { + uint8_t data[DAP_CONFIG_PACKET_SIZE]; + uint8_t size; +} DapPacket; + +typedef enum { + DAPThreadEventStop = DapThreadEventStop, + DAPThreadEventRxV1 = (1 << 1), + DAPThreadEventRxV2 = (1 << 2), + DAPThreadEventUSBConnect = (1 << 3), + DAPThreadEventUSBDisconnect = (1 << 4), + DAPThreadEventApplyConfig = (1 << 5), + DAPThreadEventAll = DAPThreadEventStop | DAPThreadEventRxV1 | DAPThreadEventRxV2 | + DAPThreadEventUSBConnect | DAPThreadEventUSBDisconnect | + DAPThreadEventApplyConfig, +} DAPThreadEvent; + +#define USB_SERIAL_NUMBER_LEN 16 +char usb_serial_number[USB_SERIAL_NUMBER_LEN] = {0}; + +const char* dap_app_get_serial(DapApp* app) { + UNUSED(app); + return usb_serial_number; +} + +static void dap_app_rx1_callback(void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + furi_thread_flags_set(thread_id, DAPThreadEventRxV1); +} + +static void dap_app_rx2_callback(void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + furi_thread_flags_set(thread_id, DAPThreadEventRxV2); +} + +static void dap_app_usb_state_callback(bool state, void* context) { + furi_assert(context); + FuriThreadId thread_id = (FuriThreadId)context; + if(state) { + furi_thread_flags_set(thread_id, DAPThreadEventUSBConnect); + } else { + furi_thread_flags_set(thread_id, DAPThreadEventUSBDisconnect); + } +} + +static void dap_app_process_v1() { + DapPacket tx_packet; + DapPacket rx_packet; + memset(&tx_packet, 0, sizeof(DapPacket)); + rx_packet.size = dap_v1_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); + dap_process_request(rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); + dap_v1_usb_tx(tx_packet.data, DAP_CONFIG_PACKET_SIZE); +} + +static void dap_app_process_v2() { + DapPacket tx_packet; + DapPacket rx_packet; + memset(&tx_packet, 0, sizeof(DapPacket)); + rx_packet.size = dap_v2_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); + size_t len = dap_process_request( + rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); + dap_v2_usb_tx(tx_packet.data, len); +} + +void dap_app_vendor_cmd(uint8_t cmd) { + // openocd -c "cmsis-dap cmd 81" + if(cmd == 0x01) { + furi_hal_power_reset(); + } +} + +void dap_app_target_reset() { + FURI_LOG_I("DAP", "Target reset"); +} + +static void dap_init_gpio(DapSwdPins swd_pins) { + switch(swd_pins) { + case DapSwdPinsPA7PA6: + flipper_dap_swclk_pin = gpio_ext_pa7; + flipper_dap_swdio_pin = gpio_ext_pa6; + break; + case DapSwdPinsPA14PA13: + flipper_dap_swclk_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_14}; + flipper_dap_swdio_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_13}; + break; + } + + flipper_dap_reset_pin = gpio_ext_pa4; + flipper_dap_tdo_pin = gpio_ext_pb3; + flipper_dap_tdi_pin = gpio_ext_pb2; +} + +static void dap_deinit_gpio(DapSwdPins swd_pins) { + // setup gpio pins to default state + furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + if(DapSwdPinsPA14PA13 == swd_pins) { + // PA14 and PA13 are used by SWD + furi_hal_gpio_init_ex( + &flipper_dap_swclk_pin, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedLow, + GpioAltFn0JTCK_SWCLK); + furi_hal_gpio_init_ex( + &flipper_dap_swdio_pin, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn0JTMS_SWDIO); + } else { + furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } +} + +static int32_t dap_process(void* p) { + DapApp* app = p; + DapState* dap_state = &(app->state); + + // allocate resources + FuriHalUsbInterface* usb_config_prev; + app->config.swd_pins = DapSwdPinsPA7PA6; + DapSwdPins swd_pins_prev = app->config.swd_pins; + + // init pins + dap_init_gpio(swd_pins_prev); + + // init dap + dap_init(); + + // get name + const char* name = furi_hal_version_get_name_ptr(); + if(!name) { + name = "Flipper"; + } + snprintf(usb_serial_number, USB_SERIAL_NUMBER_LEN, "DAP_%s", name); + + // init usb + usb_config_prev = furi_hal_usb_get_config(); + dap_common_usb_alloc_name(usb_serial_number); + dap_common_usb_set_context(furi_thread_get_id(furi_thread_get_current())); + dap_v1_usb_set_rx_callback(dap_app_rx1_callback); + dap_v2_usb_set_rx_callback(dap_app_rx2_callback); + dap_common_usb_set_state_callback(dap_app_usb_state_callback); + furi_hal_usb_set_config(&dap_v2_usb_hid, NULL); + + // work + uint32_t events; + while(1) { + events = furi_thread_flags_wait(DAPThreadEventAll, FuriFlagWaitAny, FuriWaitForever); + + if(!(events & FuriFlagError)) { + if(events & DAPThreadEventRxV1) { + dap_app_process_v1(); + dap_state->dap_counter++; + dap_state->dap_version = DapVersionV1; + } + + if(events & DAPThreadEventRxV2) { + dap_app_process_v2(); + dap_state->dap_counter++; + dap_state->dap_version = DapVersionV2; + } + + if(events & DAPThreadEventUSBConnect) { + dap_state->usb_connected = true; + } + + if(events & DAPThreadEventUSBDisconnect) { + dap_state->usb_connected = false; + dap_state->dap_version = DapVersionUnknown; + } + + if(events & DAPThreadEventApplyConfig) { + if(swd_pins_prev != app->config.swd_pins) { + dap_deinit_gpio(swd_pins_prev); + swd_pins_prev = app->config.swd_pins; + dap_init_gpio(swd_pins_prev); + } + } + + if(events & DAPThreadEventStop) { + break; + } + } + } + + // deinit usb + furi_hal_usb_set_config(usb_config_prev, NULL); + dap_common_usb_free_name(); + dap_deinit_gpio(swd_pins_prev); + return 0; +} + +/***************************************************************************/ +/****************************** CDC PROCESS ********************************/ +/***************************************************************************/ + +typedef enum { + CDCThreadEventStop = DapThreadEventStop, + CDCThreadEventUARTRx = (1 << 1), + CDCThreadEventCDCRx = (1 << 2), + CDCThreadEventCDCConfig = (1 << 3), + CDCThreadEventApplyConfig = (1 << 4), + CDCThreadEventAll = CDCThreadEventStop | CDCThreadEventUARTRx | CDCThreadEventCDCRx | + CDCThreadEventCDCConfig | CDCThreadEventApplyConfig, +} CDCThreadEvent; + +typedef struct { + FuriStreamBuffer* rx_stream; + FuriThreadId thread_id; + FuriHalUartId uart_id; + struct usb_cdc_line_coding line_coding; +} CDCProcess; + +static void cdc_uart_irq_cb(UartIrqEvent ev, uint8_t data, void* ctx) { + CDCProcess* app = ctx; + + if(ev == UartIrqEventRXNE) { + furi_stream_buffer_send(app->rx_stream, &data, 1, 0); + furi_thread_flags_set(app->thread_id, CDCThreadEventUARTRx); + } +} + +static void cdc_usb_rx_callback(void* context) { + CDCProcess* app = context; + furi_thread_flags_set(app->thread_id, CDCThreadEventCDCRx); +} + +static void cdc_usb_control_line_callback(uint8_t state, void* context) { + UNUSED(context); + UNUSED(state); +} + +static void cdc_usb_config_callback(struct usb_cdc_line_coding* config, void* context) { + CDCProcess* app = context; + app->line_coding = *config; + furi_thread_flags_set(app->thread_id, CDCThreadEventCDCConfig); +} + +static FuriHalUartId cdc_init_uart( + DapUartType type, + DapUartTXRX swap, + uint32_t baudrate, + void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), + void* ctx) { + FuriHalUartId uart_id = FuriHalUartIdUSART1; + if(baudrate == 0) baudrate = 115200; + + switch(type) { + case DapUartTypeUSART1: + uart_id = FuriHalUartIdUSART1; + furi_hal_console_disable(); + furi_hal_uart_deinit(uart_id); + if(swap == DapUartTXRXSwap) { + LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_SWAPPED); + } else { + LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); + } + furi_hal_uart_init(uart_id, baudrate); + furi_hal_uart_set_irq_cb(uart_id, cb, ctx); + break; + case DapUartTypeLPUART1: + uart_id = FuriHalUartIdLPUART1; + furi_hal_uart_deinit(uart_id); + if(swap == DapUartTXRXSwap) { + LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_SWAPPED); + } else { + LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); + } + furi_hal_uart_init(uart_id, baudrate); + furi_hal_uart_set_irq_cb(uart_id, cb, ctx); + break; + } + + return uart_id; +} + +static void cdc_deinit_uart(DapUartType type) { + switch(type) { + case DapUartTypeUSART1: + furi_hal_uart_deinit(FuriHalUartIdUSART1); + LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); + furi_hal_console_init(); + break; + case DapUartTypeLPUART1: + furi_hal_uart_deinit(FuriHalUartIdLPUART1); + LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); + break; + } +} + +static int32_t cdc_process(void* p) { + DapApp* dap_app = p; + DapState* dap_state = &(dap_app->state); + + dap_app->config.uart_pins = DapUartTypeLPUART1; + dap_app->config.uart_swap = DapUartTXRXNormal; + + DapUartType uart_pins_prev = dap_app->config.uart_pins; + DapUartTXRX uart_swap_prev = dap_app->config.uart_swap; + + CDCProcess* app = malloc(sizeof(CDCProcess)); + app->thread_id = furi_thread_get_id(furi_thread_get_current()); + app->rx_stream = furi_stream_buffer_alloc(512, 1); + + const uint8_t rx_buffer_size = 64; + uint8_t* rx_buffer = malloc(rx_buffer_size); + + app->uart_id = cdc_init_uart( + uart_pins_prev, uart_swap_prev, dap_state->cdc_baudrate, cdc_uart_irq_cb, app); + + dap_cdc_usb_set_context(app); + dap_cdc_usb_set_rx_callback(cdc_usb_rx_callback); + dap_cdc_usb_set_control_line_callback(cdc_usb_control_line_callback); + dap_cdc_usb_set_config_callback(cdc_usb_config_callback); + + uint32_t events; + while(1) { + events = furi_thread_flags_wait(CDCThreadEventAll, FuriFlagWaitAny, FuriWaitForever); + + if(!(events & FuriFlagError)) { + if(events & CDCThreadEventCDCConfig) { + if(dap_state->cdc_baudrate != app->line_coding.dwDTERate) { + dap_state->cdc_baudrate = app->line_coding.dwDTERate; + if(dap_state->cdc_baudrate > 0) { + furi_hal_uart_set_br(app->uart_id, dap_state->cdc_baudrate); + } + } + } + + if(events & CDCThreadEventUARTRx) { + size_t len = + furi_stream_buffer_receive(app->rx_stream, rx_buffer, rx_buffer_size, 0); + + if(len > 0) { + dap_cdc_usb_tx(rx_buffer, len); + } + dap_state->cdc_rx_counter += len; + } + + if(events & CDCThreadEventCDCRx) { + size_t len = dap_cdc_usb_rx(rx_buffer, rx_buffer_size); + if(len > 0) { + furi_hal_uart_tx(app->uart_id, rx_buffer, len); + } + dap_state->cdc_tx_counter += len; + } + + if(events & CDCThreadEventApplyConfig) { + if(uart_pins_prev != dap_app->config.uart_pins || + uart_swap_prev != dap_app->config.uart_swap) { + cdc_deinit_uart(uart_pins_prev); + uart_pins_prev = dap_app->config.uart_pins; + uart_swap_prev = dap_app->config.uart_swap; + app->uart_id = cdc_init_uart( + uart_pins_prev, + uart_swap_prev, + dap_state->cdc_baudrate, + cdc_uart_irq_cb, + app); + } + } + + if(events & CDCThreadEventStop) { + break; + } + } + } + + cdc_deinit_uart(uart_pins_prev); + free(rx_buffer); + furi_stream_buffer_free(app->rx_stream); + free(app); + + return 0; +} + +/***************************************************************************/ +/******************************* MAIN APP **********************************/ +/***************************************************************************/ + +static DapApp* dap_app_alloc() { + DapApp* dap_app = malloc(sizeof(DapApp)); + dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app); + dap_app->cdc_thread = furi_thread_alloc_ex("DAP CDC", 1024, cdc_process, dap_app); + dap_app->gui_thread = furi_thread_alloc_ex("DAP GUI", 1024, dap_gui_thread, dap_app); + return dap_app; +} + +static void dap_app_free(DapApp* dap_app) { + furi_assert(dap_app); + furi_thread_free(dap_app->dap_thread); + furi_thread_free(dap_app->cdc_thread); + furi_thread_free(dap_app->gui_thread); + free(dap_app); +} + +static DapApp* app_handle = NULL; + +void dap_app_disconnect() { + app_handle->state.dap_mode = DapModeDisconnected; +} + +void dap_app_connect_swd() { + app_handle->state.dap_mode = DapModeSWD; +} + +void dap_app_connect_jtag() { + app_handle->state.dap_mode = DapModeJTAG; +} + +void dap_app_set_config(DapApp* app, DapConfig* config) { + app->config = *config; + furi_thread_flags_set(furi_thread_get_id(app->dap_thread), DAPThreadEventApplyConfig); + furi_thread_flags_set(furi_thread_get_id(app->cdc_thread), CDCThreadEventApplyConfig); +} + +DapConfig* dap_app_get_config(DapApp* app) { + return &app->config; +} + +int32_t dap_link_app(void* p) { + UNUSED(p); + + if(furi_hal_usb_is_locked()) { + DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); + dialog_message_set_text( + message, + "Disconnect from\nPC or phone to\nuse this function.", + 3, + 30, + AlignLeft, + AlignTop); + dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0); + dialog_message_show(dialogs, message); + dialog_message_free(message); + furi_record_close(RECORD_DIALOGS); + return -1; + } + + // alloc app + DapApp* app = dap_app_alloc(); + app_handle = app; + + furi_thread_start(app->dap_thread); + furi_thread_start(app->cdc_thread); + furi_thread_start(app->gui_thread); + + // wait until gui thread is finished + furi_thread_join(app->gui_thread); + + // send stop event to threads + dap_thread_send_stop(app->dap_thread); + dap_thread_send_stop(app->cdc_thread); + + // wait for threads to stop + furi_thread_join(app->dap_thread); + furi_thread_join(app->cdc_thread); + + // free app + dap_app_free(app); + + return 0; +} \ No newline at end of file diff --git a/applications/external/dap_link/dap_link.h b/applications/external/dap_link/dap_link.h new file mode 100644 index 000000000..d51726c45 --- /dev/null +++ b/applications/external/dap_link/dap_link.h @@ -0,0 +1,55 @@ +#pragma once +#include + +typedef enum { + DapModeDisconnected, + DapModeSWD, + DapModeJTAG, +} DapMode; + +typedef enum { + DapVersionUnknown, + DapVersionV1, + DapVersionV2, +} DapVersion; + +typedef struct { + bool usb_connected; + DapMode dap_mode; + DapVersion dap_version; + uint32_t dap_counter; + uint32_t cdc_baudrate; + uint32_t cdc_tx_counter; + uint32_t cdc_rx_counter; +} DapState; + +typedef enum { + DapSwdPinsPA7PA6, // Pins 2, 3 + DapSwdPinsPA14PA13, // Pins 10, 12 +} DapSwdPins; + +typedef enum { + DapUartTypeUSART1, // Pins 13, 14 + DapUartTypeLPUART1, // Pins 15, 16 +} DapUartType; + +typedef enum { + DapUartTXRXNormal, + DapUartTXRXSwap, +} DapUartTXRX; + +typedef struct { + DapSwdPins swd_pins; + DapUartType uart_pins; + DapUartTXRX uart_swap; +} DapConfig; + +typedef struct DapApp DapApp; + +void dap_app_get_state(DapApp* app, DapState* state); + +const char* dap_app_get_serial(DapApp* app); + +void dap_app_set_config(DapApp* app, DapConfig* config); + +DapConfig* dap_app_get_config(DapApp* app); \ No newline at end of file diff --git a/applications/external/dap_link/gui/dap_gui.c b/applications/external/dap_link/gui/dap_gui.c new file mode 100644 index 000000000..4dd986153 --- /dev/null +++ b/applications/external/dap_link/gui/dap_gui.c @@ -0,0 +1,92 @@ +#include "dap_gui.h" +#include "dap_gui_i.h" + +#define DAP_GUI_TICK 250 + +static bool dap_gui_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + DapGuiApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool dap_gui_back_event_callback(void* context) { + furi_assert(context); + DapGuiApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void dap_gui_tick_event_callback(void* context) { + furi_assert(context); + DapGuiApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +DapGuiApp* dap_gui_alloc() { + DapGuiApp* app = malloc(sizeof(DapGuiApp)); + app->gui = furi_record_open(RECORD_GUI); + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&dap_scene_handlers, app); + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback(app->view_dispatcher, dap_gui_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, dap_gui_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, dap_gui_tick_event_callback, DAP_GUI_TICK); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + DapGuiAppViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + app->main_view = dap_main_view_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, DapGuiAppViewMainView, dap_main_view_get_view(app->main_view)); + + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, DapGuiAppViewWidget, widget_get_view(app->widget)); + + scene_manager_next_scene(app->scene_manager, DapSceneMain); + + return app; +} + +void dap_gui_free(DapGuiApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewVarItemList); + variable_item_list_free(app->var_item_list); + + view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewMainView); + dap_main_view_free(app->main_view); + + view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewWidget); + widget_free(app->widget); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + + free(app); +} + +int32_t dap_gui_thread(void* arg) { + DapGuiApp* app = dap_gui_alloc(); + app->dap_app = arg; + + notification_message_block(app->notifications, &sequence_display_backlight_enforce_on); + view_dispatcher_run(app->view_dispatcher); + notification_message_block(app->notifications, &sequence_display_backlight_enforce_auto); + + dap_gui_free(app); + return 0; +} \ No newline at end of file diff --git a/applications/external/dap_link/gui/dap_gui.h b/applications/external/dap_link/gui/dap_gui.h new file mode 100644 index 000000000..3d8e6bdf9 --- /dev/null +++ b/applications/external/dap_link/gui/dap_gui.h @@ -0,0 +1,4 @@ +#pragma once +#include + +int32_t dap_gui_thread(void* arg); \ No newline at end of file diff --git a/applications/external/dap_link/gui/dap_gui_custom_event.h b/applications/external/dap_link/gui/dap_gui_custom_event.h new file mode 100644 index 000000000..8b127c9d4 --- /dev/null +++ b/applications/external/dap_link/gui/dap_gui_custom_event.h @@ -0,0 +1,7 @@ +#pragma once + +typedef enum { + DapAppCustomEventConfig, + DapAppCustomEventHelp, + DapAppCustomEventAbout, +} DapAppCustomEvent; diff --git a/applications/external/dap_link/gui/dap_gui_i.h b/applications/external/dap_link/gui/dap_gui_i.h new file mode 100644 index 000000000..59411e78c --- /dev/null +++ b/applications/external/dap_link/gui/dap_gui_i.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "dap_gui.h" +#include "../dap_link.h" +#include "scenes/config/dap_scene.h" +#include "dap_gui_custom_event.h" +#include "views/dap_main_view.h" + +typedef struct { + DapApp* dap_app; + + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + VariableItemList* var_item_list; + DapMainView* main_view; + Widget* widget; +} DapGuiApp; + +typedef enum { + DapGuiAppViewVarItemList, + DapGuiAppViewMainView, + DapGuiAppViewWidget, +} DapGuiAppView; diff --git a/applications/external/dap_link/gui/scenes/config/dap_scene_config.h b/applications/external/dap_link/gui/scenes/config/dap_scene_config.h new file mode 100644 index 000000000..8957aca06 --- /dev/null +++ b/applications/external/dap_link/gui/scenes/config/dap_scene_config.h @@ -0,0 +1,4 @@ +ADD_SCENE(dap, main, Main) +ADD_SCENE(dap, config, Config) +ADD_SCENE(dap, help, Help) +ADD_SCENE(dap, about, About) \ No newline at end of file diff --git a/applications/external/dap_link/gui/scenes/dap_scene_config.c b/applications/external/dap_link/gui/scenes/dap_scene_config.c new file mode 100644 index 000000000..48d5fedcd --- /dev/null +++ b/applications/external/dap_link/gui/scenes/dap_scene_config.c @@ -0,0 +1,107 @@ +#include "../dap_gui_i.h" + +static const char* swd_pins[] = {[DapSwdPinsPA7PA6] = "2,3", [DapSwdPinsPA14PA13] = "10,12"}; +static const char* uart_pins[] = {[DapUartTypeUSART1] = "13,14", [DapUartTypeLPUART1] = "15,16"}; +static const char* uart_swap[] = {[DapUartTXRXNormal] = "No", [DapUartTXRXSwap] = "Yes"}; + +static void swd_pins_cb(VariableItem* item) { + DapGuiApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, swd_pins[index]); + + DapConfig* config = dap_app_get_config(app->dap_app); + config->swd_pins = index; + dap_app_set_config(app->dap_app, config); +} + +static void uart_pins_cb(VariableItem* item) { + DapGuiApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, uart_pins[index]); + + DapConfig* config = dap_app_get_config(app->dap_app); + config->uart_pins = index; + dap_app_set_config(app->dap_app, config); +} + +static void uart_swap_cb(VariableItem* item) { + DapGuiApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, uart_swap[index]); + + DapConfig* config = dap_app_get_config(app->dap_app); + config->uart_swap = index; + dap_app_set_config(app->dap_app, config); +} + +static void ok_cb(void* context, uint32_t index) { + DapGuiApp* app = context; + switch(index) { + case 3: + view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventHelp); + break; + case 4: + view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventAbout); + break; + default: + break; + } +} + +void dap_scene_config_on_enter(void* context) { + DapGuiApp* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + DapConfig* config = dap_app_get_config(app->dap_app); + + item = variable_item_list_add( + var_item_list, "SWC SWD Pins", COUNT_OF(swd_pins), swd_pins_cb, app); + variable_item_set_current_value_index(item, config->swd_pins); + variable_item_set_current_value_text(item, swd_pins[config->swd_pins]); + + item = + variable_item_list_add(var_item_list, "UART Pins", COUNT_OF(uart_pins), uart_pins_cb, app); + variable_item_set_current_value_index(item, config->uart_pins); + variable_item_set_current_value_text(item, uart_pins[config->uart_pins]); + + item = variable_item_list_add( + var_item_list, "Swap TX RX", COUNT_OF(uart_swap), uart_swap_cb, app); + variable_item_set_current_value_index(item, config->uart_swap); + variable_item_set_current_value_text(item, uart_swap[config->uart_swap]); + + variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL); + variable_item_list_add(var_item_list, "About", 0, NULL, NULL); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, DapSceneConfig)); + + variable_item_list_set_enter_callback(var_item_list, ok_cb, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewVarItemList); +} + +bool dap_scene_config_on_event(void* context, SceneManagerEvent event) { + DapGuiApp* app = context; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DapAppCustomEventHelp) { + scene_manager_next_scene(app->scene_manager, DapSceneHelp); + return true; + } else if(event.event == DapAppCustomEventAbout) { + scene_manager_next_scene(app->scene_manager, DapSceneAbout); + return true; + } + } + return false; +} + +void dap_scene_config_on_exit(void* context) { + DapGuiApp* app = context; + scene_manager_set_scene_state( + app->scene_manager, + DapSceneConfig, + variable_item_list_get_selected_item_index(app->var_item_list)); + variable_item_list_reset(app->var_item_list); +} \ No newline at end of file diff --git a/applications/external/dap_link/gui/scenes/dap_scene_help.c b/applications/external/dap_link/gui/scenes/dap_scene_help.c new file mode 100644 index 000000000..d8d70e7ff --- /dev/null +++ b/applications/external/dap_link/gui/scenes/dap_scene_help.c @@ -0,0 +1,102 @@ +#include "../dap_gui_i.h" + +void dap_scene_help_on_enter(void* context) { + DapGuiApp* app = context; + DapConfig* config = dap_app_get_config(app->dap_app); + FuriString* string = furi_string_alloc(); + + furi_string_cat(string, "CMSIS DAP/DAP Link v2\r\n"); + furi_string_cat_printf(string, "Serial: %s\r\n", dap_app_get_serial(app->dap_app)); + furi_string_cat( + string, + "Pinout:\r\n" + "\e#SWD:\r\n"); + + switch(config->swd_pins) { + case DapSwdPinsPA7PA6: + furi_string_cat( + string, + " SWC: 2 [A7]\r\n" + " SWD: 3 [A6]\r\n"); + break; + case DapSwdPinsPA14PA13: + furi_string_cat( + string, + " SWC: 10 [SWC]\r\n" + " SWD: 12 [SIO]\r\n"); + break; + default: + break; + } + + furi_string_cat(string, "\e#JTAG:\r\n"); + switch(config->swd_pins) { + case DapSwdPinsPA7PA6: + furi_string_cat( + string, + " TCK: 2 [A7]\r\n" + " TMS: 3 [A6]\r\n" + " RST: 4 [A4]\r\n" + " TDO: 5 [B3]\r\n" + " TDI: 6 [B2]\r\n"); + break; + case DapSwdPinsPA14PA13: + furi_string_cat( + string, + " RST: 4 [A4]\r\n" + " TDO: 5 [B3]\r\n" + " TDI: 6 [B2]\r\n" + " TCK: 10 [SWC]\r\n" + " TMS: 12 [SIO]\r\n"); + break; + default: + break; + } + + furi_string_cat(string, "\e#UART:\r\n"); + switch(config->uart_pins) { + case DapUartTypeUSART1: + if(config->uart_swap == DapUartTXRXNormal) { + furi_string_cat( + string, + " TX: 13 [TX]\r\n" + " RX: 14 [RX]\r\n"); + } else { + furi_string_cat( + string, + " RX: 13 [TX]\r\n" + " TX: 14 [RX]\r\n"); + } + break; + case DapUartTypeLPUART1: + if(config->uart_swap == DapUartTXRXNormal) { + furi_string_cat( + string, + " TX: 15 [C1]\r\n" + " RX: 16 [C0]\r\n"); + } else { + furi_string_cat( + string, + " RX: 15 [C1]\r\n" + " TX: 16 [C0]\r\n"); + } + break; + default: + break; + } + + widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(string)); + furi_string_free(string); + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); +} + +bool dap_scene_help_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void dap_scene_help_on_exit(void* context) { + DapGuiApp* app = context; + widget_reset(app->widget); +} \ No newline at end of file diff --git a/applications/external/dap_link/gui/scenes/dap_scene_main.c b/applications/external/dap_link/gui/scenes/dap_scene_main.c new file mode 100644 index 000000000..8c19bd6a5 --- /dev/null +++ b/applications/external/dap_link/gui/scenes/dap_scene_main.c @@ -0,0 +1,154 @@ +#include "../dap_gui_i.h" +#include "../../dap_link.h" + +typedef struct { + DapState dap_state; + bool dap_active; + bool tx_active; + bool rx_active; +} DapSceneMainState; + +static bool process_dap_state(DapGuiApp* app) { + DapSceneMainState* state = + (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); + if(state == NULL) return true; + + DapState* prev_state = &state->dap_state; + DapState next_state; + dap_app_get_state(app->dap_app, &next_state); + bool need_to_update = false; + + if(prev_state->dap_mode != next_state.dap_mode) { + switch(next_state.dap_mode) { + case DapModeDisconnected: + dap_main_view_set_mode(app->main_view, DapMainViewModeDisconnected); + notification_message(app->notifications, &sequence_blink_stop); + break; + case DapModeSWD: + dap_main_view_set_mode(app->main_view, DapMainViewModeSWD); + notification_message(app->notifications, &sequence_blink_start_blue); + break; + case DapModeJTAG: + dap_main_view_set_mode(app->main_view, DapMainViewModeJTAG); + notification_message(app->notifications, &sequence_blink_start_magenta); + break; + } + need_to_update = true; + } + + if(prev_state->dap_version != next_state.dap_version) { + switch(next_state.dap_version) { + case DapVersionUnknown: + dap_main_view_set_version(app->main_view, DapMainViewVersionUnknown); + break; + case DapVersionV1: + dap_main_view_set_version(app->main_view, DapMainViewVersionV1); + break; + case DapVersionV2: + dap_main_view_set_version(app->main_view, DapMainViewVersionV2); + break; + } + need_to_update = true; + } + + if(prev_state->usb_connected != next_state.usb_connected) { + dap_main_view_set_usb_connected(app->main_view, next_state.usb_connected); + need_to_update = true; + } + + if(prev_state->dap_counter != next_state.dap_counter) { + if(!state->dap_active) { + state->dap_active = true; + dap_main_view_set_dap(app->main_view, state->dap_active); + need_to_update = true; + } + } else { + if(state->dap_active) { + state->dap_active = false; + dap_main_view_set_dap(app->main_view, state->dap_active); + need_to_update = true; + } + } + + if(prev_state->cdc_baudrate != next_state.cdc_baudrate) { + dap_main_view_set_baudrate(app->main_view, next_state.cdc_baudrate); + need_to_update = true; + } + + if(prev_state->cdc_tx_counter != next_state.cdc_tx_counter) { + if(!state->tx_active) { + state->tx_active = true; + dap_main_view_set_tx(app->main_view, state->tx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_start_red); + } + } else { + if(state->tx_active) { + state->tx_active = false; + dap_main_view_set_tx(app->main_view, state->tx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_stop); + } + } + + if(prev_state->cdc_rx_counter != next_state.cdc_rx_counter) { + if(!state->rx_active) { + state->rx_active = true; + dap_main_view_set_rx(app->main_view, state->rx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_start_green); + } + } else { + if(state->rx_active) { + state->rx_active = false; + dap_main_view_set_rx(app->main_view, state->rx_active); + need_to_update = true; + notification_message(app->notifications, &sequence_blink_stop); + } + } + + if(need_to_update) { + dap_main_view_update(app->main_view); + } + + *prev_state = next_state; + return true; +} + +static void dap_scene_main_on_left(void* context) { + DapGuiApp* app = (DapGuiApp*)context; + view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventConfig); +} + +void dap_scene_main_on_enter(void* context) { + DapGuiApp* app = context; + DapSceneMainState* state = malloc(sizeof(DapSceneMainState)); + dap_main_view_set_left_callback(app->main_view, dap_scene_main_on_left, app); + view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewMainView); + scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)state); +} + +bool dap_scene_main_on_event(void* context, SceneManagerEvent event) { + DapGuiApp* app = context; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == DapAppCustomEventConfig) { + scene_manager_next_scene(app->scene_manager, DapSceneConfig); + return true; + } + } else if(event.type == SceneManagerEventTypeTick) { + return process_dap_state(app); + } + + return false; +} + +void dap_scene_main_on_exit(void* context) { + DapGuiApp* app = context; + DapSceneMainState* state = + (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); + scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)NULL); + FURI_SW_MEMBARRIER(); + free(state); + notification_message(app->notifications, &sequence_blink_stop); +} \ No newline at end of file diff --git a/applications/external/dap_link/gui/views/dap_main_view.c b/applications/external/dap_link/gui/views/dap_main_view.c new file mode 100644 index 000000000..c5c8f9dff --- /dev/null +++ b/applications/external/dap_link/gui/views/dap_main_view.c @@ -0,0 +1,189 @@ +#include "dap_main_view.h" +#include "dap_link_icons.h" +#include + +// extern const Icon I_ArrowDownEmpty_12x18; +// extern const Icon I_ArrowDownFilled_12x18; +// extern const Icon I_ArrowUpEmpty_12x18; +// extern const Icon I_ArrowUpFilled_12x18; + +struct DapMainView { + View* view; + DapMainViewButtonCallback cb_left; + void* cb_context; +}; + +typedef struct { + DapMainViewMode mode; + DapMainViewVersion version; + bool usb_connected; + uint32_t baudrate; + bool dap_active; + bool tx_active; + bool rx_active; +} DapMainViewModel; + +static void dap_main_view_draw_callback(Canvas* canvas, void* _model) { + DapMainViewModel* model = _model; + UNUSED(model); + canvas_clear(canvas); + elements_button_left(canvas, "Config"); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0, 127, 11); + canvas_set_color(canvas, ColorWhite); + + const char* header_string; + if(model->usb_connected) { + if(model->version == DapMainViewVersionV1) { + header_string = "DAP Link V1 Connected"; + } else if(model->version == DapMainViewVersionV2) { + header_string = "DAP Link V2 Connected"; + } else { + header_string = "DAP Link Connected"; + } + } else { + header_string = "DAP Link"; + } + + canvas_draw_str_aligned(canvas, 64, 9, AlignCenter, AlignBottom, header_string); + + canvas_set_color(canvas, ColorBlack); + if(model->dap_active) { + canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18); + canvas_draw_icon(canvas, 28, 16, &I_ArrowDownFilled_12x18); + } else { + canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18); + canvas_draw_icon(canvas, 28, 16, &I_ArrowDownEmpty_12x18); + } + + switch(model->mode) { + case DapMainViewModeDisconnected: + canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "----"); + break; + case DapMainViewModeSWD: + canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "SWD"); + break; + case DapMainViewModeJTAG: + canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "JTAG"); + break; + } + + if(model->tx_active) { + canvas_draw_icon(canvas, 87, 16, &I_ArrowUpFilled_12x18); + } else { + canvas_draw_icon(canvas, 87, 16, &I_ArrowUpEmpty_12x18); + } + + if(model->rx_active) { + canvas_draw_icon(canvas, 101, 16, &I_ArrowDownFilled_12x18); + } else { + canvas_draw_icon(canvas, 101, 16, &I_ArrowDownEmpty_12x18); + } + + canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART"); + + canvas_draw_line(canvas, 44, 52, 123, 52); + if(model->baudrate == 0) { + canvas_draw_str(canvas, 45, 62, "Baud: ????"); + } else { + char baudrate_str[18]; + snprintf(baudrate_str, 18, "Baud: %lu", model->baudrate); + canvas_draw_str(canvas, 45, 62, baudrate_str); + } +} + +static bool dap_main_view_input_callback(InputEvent* event, void* context) { + furi_assert(context); + DapMainView* dap_main_view = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyLeft) { + if(dap_main_view->cb_left) { + dap_main_view->cb_left(dap_main_view->cb_context); + } + consumed = true; + } + } + + return consumed; +} + +DapMainView* dap_main_view_alloc() { + DapMainView* dap_main_view = malloc(sizeof(DapMainView)); + + dap_main_view->view = view_alloc(); + view_allocate_model(dap_main_view->view, ViewModelTypeLocking, sizeof(DapMainViewModel)); + view_set_context(dap_main_view->view, dap_main_view); + view_set_draw_callback(dap_main_view->view, dap_main_view_draw_callback); + view_set_input_callback(dap_main_view->view, dap_main_view_input_callback); + return dap_main_view; +} + +void dap_main_view_free(DapMainView* dap_main_view) { + view_free(dap_main_view->view); + free(dap_main_view); +} + +View* dap_main_view_get_view(DapMainView* dap_main_view) { + return dap_main_view->view; +} + +void dap_main_view_set_left_callback( + DapMainView* dap_main_view, + DapMainViewButtonCallback callback, + void* context) { + with_view_model( + dap_main_view->view, + DapMainViewModel * model, + { + UNUSED(model); + dap_main_view->cb_left = callback; + dap_main_view->cb_context = context; + }, + true); +} + +void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->mode = mode; }, false); +} + +void dap_main_view_set_dap(DapMainView* dap_main_view, bool active) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->dap_active = active; }, false); +} + +void dap_main_view_set_tx(DapMainView* dap_main_view, bool active) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->tx_active = active; }, false); +} + +void dap_main_view_set_rx(DapMainView* dap_main_view, bool active) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->rx_active = active; }, false); +} + +void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->baudrate = baudrate; }, false); +} + +void dap_main_view_update(DapMainView* dap_main_view) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { UNUSED(model); }, true); +} + +void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version) { + with_view_model( + dap_main_view->view, DapMainViewModel * model, { model->version = version; }, false); +} + +void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected) { + with_view_model( + dap_main_view->view, + DapMainViewModel * model, + { model->usb_connected = connected; }, + false); +} \ No newline at end of file diff --git a/applications/external/dap_link/gui/views/dap_main_view.h b/applications/external/dap_link/gui/views/dap_main_view.h new file mode 100644 index 000000000..1fd900452 --- /dev/null +++ b/applications/external/dap_link/gui/views/dap_main_view.h @@ -0,0 +1,45 @@ +#pragma once +#include + +typedef struct DapMainView DapMainView; + +typedef void (*DapMainViewButtonCallback)(void* context); + +typedef enum { + DapMainViewVersionUnknown, + DapMainViewVersionV1, + DapMainViewVersionV2, +} DapMainViewVersion; + +typedef enum { + DapMainViewModeDisconnected, + DapMainViewModeSWD, + DapMainViewModeJTAG, +} DapMainViewMode; + +DapMainView* dap_main_view_alloc(); + +void dap_main_view_free(DapMainView* dap_main_view); + +View* dap_main_view_get_view(DapMainView* dap_main_view); + +void dap_main_view_set_left_callback( + DapMainView* dap_main_view, + DapMainViewButtonCallback callback, + void* context); + +void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode); + +void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version); + +void dap_main_view_set_dap(DapMainView* dap_main_view, bool active); + +void dap_main_view_set_tx(DapMainView* dap_main_view, bool active); + +void dap_main_view_set_rx(DapMainView* dap_main_view, bool active); + +void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected); + +void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate); + +void dap_main_view_update(DapMainView* dap_main_view); \ No newline at end of file diff --git a/applications/external/dap_link/icons/ActiveConnection_50x64.png b/applications/external/dap_link/icons/ActiveConnection_50x64.png new file mode 100644 index 0000000000000000000000000000000000000000..1d7686dddf8a33b724c7528ed36435514b7518b2 GIT binary patch literal 3842 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 WwRe~lwXYaxC4;A{pUXO@geCw-ib4GV literal 0 HcmV?d00001 diff --git a/applications/external/hid_app/application.fam b/applications/external/hid_app/application.fam index a9d8305dd..e96e956d8 100644 --- a/applications/external/hid_app/application.fam +++ b/applications/external/hid_app/application.fam @@ -1,10 +1,10 @@ App( appid="hid_usb", - name="Remote", + name="USB Keyboard & Mouse", apptype=FlipperAppType.EXTERNAL, entry_point="hid_usb_app", stack_size=1 * 1024, - fap_category="USB", + fap_category="Misc", fap_icon="hid_usb_10px.png", fap_icon_assets="assets", fap_icon_assets_symbol="hid", @@ -13,11 +13,11 @@ App( App( appid="hid_ble", - name="Remote", + name="Bluetooth Remote", apptype=FlipperAppType.EXTERNAL, entry_point="hid_ble_app", stack_size=1 * 1024, - fap_category="Bluetooth", + fap_category="Misc", fap_icon="hid_ble_10px.png", fap_icon_assets="assets", fap_icon_assets_symbol="hid", diff --git a/applications/external/music_player/application.fam b/applications/external/music_player/application.fam index 3414c0a48..e0254250e 100644 --- a/applications/external/music_player/application.fam +++ b/applications/external/music_player/application.fam @@ -1,5 +1,5 @@ App( - appid="music_player", + appid="Music_Player", name="Music Player", apptype=FlipperAppType.EXTERNAL, entry_point="music_player_app", diff --git a/applications/external/signal_generator/application.fam b/applications/external/signal_generator/application.fam index 094e784cc..bcf7d20b6 100644 --- a/applications/external/signal_generator/application.fam +++ b/applications/external/signal_generator/application.fam @@ -1,5 +1,5 @@ App( - appid="signal_generator", + appid="Signal_Generator", name="Signal Generator", apptype=FlipperAppType.EXTERNAL, entry_point="signal_gen_app", From fe403c6994195cf6bf2d52f60ac6198311ab5bc8 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 15 Mar 2023 01:53:55 +0300 Subject: [PATCH 11/34] Remove cdefines from external apps --- applications/external/arkanoid/application.fam | 1 - applications/external/barcode_generator/application.fam | 1 - applications/external/blackjack/application.fam | 1 - applications/external/doom/application.fam | 1 - applications/external/dtmf_dolphin/application.fam | 1 - applications/external/esp8266_deauth/application.fam | 1 - applications/external/flappy_bird/application.fam | 1 - applications/external/flipfrid/application.fam | 1 - applications/external/flipper_i2ctools/application.fam | 1 - applications/external/game15/application.fam | 1 - applications/external/game_2048/application.fam | 1 - applications/external/gps_nmea_uart/application.fam | 1 - applications/external/hc_sr04/application.fam | 1 - applications/external/hex_viewer/application.fam | 1 - applications/external/ibtn_fuzzer/application.fam | 1 - applications/external/lightmeter/application.fam | 1 - applications/external/metronome/application.fam | 1 - applications/external/minesweeper/application.fam | 1 - applications/external/morse_code/application.fam | 1 - applications/external/mousejacker/application.fam | 1 - applications/external/multi_converter/application.fam | 1 - applications/external/nrfsniff/application.fam | 1 - applications/external/playlist/application.fam | 1 - applications/external/pocsag_pager/application.fam | 1 - applications/external/protoview/application.fam | 1 - applications/external/sentry_safe/application.fam | 1 - applications/external/solitaire/application.fam | 1 - applications/external/spectrum_analyzer/application.fam | 1 - applications/external/subbrute | 2 +- applications/external/swd_probe/application.fam | 1 - applications/external/tetris_game/application.fam | 1 - applications/external/text_viewer/application.fam | 1 - applications/external/tictactoe_game/application.fam | 1 - applications/external/totp/application.fam | 1 - applications/external/uart_terminal/application.fam | 1 - applications/external/unitemp/application.fam | 1 - applications/external/wav_player/application.fam | 1 - applications/external/wifi_marauder_companion/application.fam | 1 - applications/external/wifi_scanner/application.fam | 1 - applications/external/zombiez/application.fam | 1 - 40 files changed, 1 insertion(+), 40 deletions(-) diff --git a/applications/external/arkanoid/application.fam b/applications/external/arkanoid/application.fam index 05520b4bf..5cfec8862 100644 --- a/applications/external/arkanoid/application.fam +++ b/applications/external/arkanoid/application.fam @@ -3,7 +3,6 @@ App( name="Arkanoid", apptype=FlipperAppType.EXTERNAL, entry_point="arkanoid_game_app", - cdefines=["APP_ARKANOID_GAME"], requires=["gui"], stack_size=1 * 1024, order=30, diff --git a/applications/external/barcode_generator/application.fam b/applications/external/barcode_generator/application.fam index d2d7c4b96..c92498005 100644 --- a/applications/external/barcode_generator/application.fam +++ b/applications/external/barcode_generator/application.fam @@ -3,7 +3,6 @@ App( name="Barcode Generator", apptype=FlipperAppType.EXTERNAL, entry_point="barcode_generator_app", - cdefines=["APP_BARCODE_GEN"], requires=[ "gui", "dialogs", diff --git a/applications/external/blackjack/application.fam b/applications/external/blackjack/application.fam index d365cb895..f0645f003 100644 --- a/applications/external/blackjack/application.fam +++ b/applications/external/blackjack/application.fam @@ -3,7 +3,6 @@ App( name="Blackjack", apptype=FlipperAppType.EXTERNAL, entry_point="blackjack_app", - cdefines=["APP_BLACKJACK"], requires=["gui","storage","canvas"], stack_size=2 * 1024, order=30, diff --git a/applications/external/doom/application.fam b/applications/external/doom/application.fam index d9ae4d67d..4ff7b12f3 100644 --- a/applications/external/doom/application.fam +++ b/applications/external/doom/application.fam @@ -3,7 +3,6 @@ App( name="DOOM", apptype=FlipperAppType.EXTERNAL, entry_point="doom_app", - cdefines=["APP_DOOM_GAME"], requires=[ "gui", "music_player", diff --git a/applications/external/dtmf_dolphin/application.fam b/applications/external/dtmf_dolphin/application.fam index 0727f5f52..460f6ded6 100644 --- a/applications/external/dtmf_dolphin/application.fam +++ b/applications/external/dtmf_dolphin/application.fam @@ -3,7 +3,6 @@ App( name="DTMF Dolphin", apptype=FlipperAppType.EXTERNAL, entry_point="dtmf_dolphin_app", - cdefines=["DTMF_DOLPHIN"], requires=[ "storage", "gui", diff --git a/applications/external/esp8266_deauth/application.fam b/applications/external/esp8266_deauth/application.fam index 0b305e1fb..780c51c53 100644 --- a/applications/external/esp8266_deauth/application.fam +++ b/applications/external/esp8266_deauth/application.fam @@ -3,7 +3,6 @@ App( name="[ESP8266] Deauther", apptype=FlipperAppType.EXTERNAL, entry_point="esp8266_deauth_app", - cdefines=["APP_ESP8266_deauth"], requires=["gui"], stack_size=2 * 1024, order=100, diff --git a/applications/external/flappy_bird/application.fam b/applications/external/flappy_bird/application.fam index 0912178ca..f9fe85fa2 100644 --- a/applications/external/flappy_bird/application.fam +++ b/applications/external/flappy_bird/application.fam @@ -3,7 +3,6 @@ App( name="Flappy Bird", apptype=FlipperAppType.EXTERNAL, entry_point="flappy_game_app", - cdefines=["APP_FLAPPY_GAME"], requires=["gui"], stack_size=4 * 1024, order=100, diff --git a/applications/external/flipfrid/application.fam b/applications/external/flipfrid/application.fam index 07649efca..8f620f4ac 100644 --- a/applications/external/flipfrid/application.fam +++ b/applications/external/flipfrid/application.fam @@ -3,7 +3,6 @@ App( name="RFID Fuzzer", apptype=FlipperAppType.EXTERNAL, entry_point="flipfrid_start", - cdefines=["APP_FLIP_FRID"], requires=["gui", "storage", "dialogs", "input", "notification"], stack_size=1 * 1024, order=15, diff --git a/applications/external/flipper_i2ctools/application.fam b/applications/external/flipper_i2ctools/application.fam index 6fafeef58..5a00aa045 100644 --- a/applications/external/flipper_i2ctools/application.fam +++ b/applications/external/flipper_i2ctools/application.fam @@ -3,7 +3,6 @@ App( name="[GPIO] i2c Tools", apptype=FlipperAppType.EXTERNAL, entry_point="i2ctools_app", - cdefines=["APP_I2CTOOLS"], requires=["gui"], stack_size=2 * 1024, order=175, diff --git a/applications/external/game15/application.fam b/applications/external/game15/application.fam index ab00316c1..969cb536a 100644 --- a/applications/external/game15/application.fam +++ b/applications/external/game15/application.fam @@ -3,7 +3,6 @@ App( name="Game 15", apptype=FlipperAppType.EXTERNAL, entry_point="game15_app", - cdefines=["APP_GAME15"], requires=["gui"], stack_size=1 * 1024, fap_icon="game15_10px.png", diff --git a/applications/external/game_2048/application.fam b/applications/external/game_2048/application.fam index eb51d65e8..1be179dca 100644 --- a/applications/external/game_2048/application.fam +++ b/applications/external/game_2048/application.fam @@ -3,7 +3,6 @@ App( name="2048", apptype=FlipperAppType.EXTERNAL, entry_point="game_2048_app", - cdefines=["APP_GAME_2048"], requires=[ "gui", ], diff --git a/applications/external/gps_nmea_uart/application.fam b/applications/external/gps_nmea_uart/application.fam index a5fdb4360..e02785744 100644 --- a/applications/external/gps_nmea_uart/application.fam +++ b/applications/external/gps_nmea_uart/application.fam @@ -3,7 +3,6 @@ App( name="[NMEA] GPS", apptype=FlipperAppType.EXTERNAL, entry_point="gps_app", - cdefines=["APP_GPS"], requires=["gui"], stack_size=1 * 1024, order=35, diff --git a/applications/external/hc_sr04/application.fam b/applications/external/hc_sr04/application.fam index ad451a102..c10d7c42f 100644 --- a/applications/external/hc_sr04/application.fam +++ b/applications/external/hc_sr04/application.fam @@ -3,7 +3,6 @@ App( name="[HC-SR] Dist. Sensor", apptype=FlipperAppType.EXTERNAL, entry_point="hc_sr04_app", - cdefines=["APP_HC_SR04"], requires=[ "gui", ], diff --git a/applications/external/hex_viewer/application.fam b/applications/external/hex_viewer/application.fam index 7204e07c8..9bb15dc86 100644 --- a/applications/external/hex_viewer/application.fam +++ b/applications/external/hex_viewer/application.fam @@ -3,7 +3,6 @@ App( name="HEX Viewer", apptype=FlipperAppType.EXTERNAL, entry_point="hex_viewer_app", - cdefines=["APP_HEX_VIEWER"], requires=[ "gui", "dialogs", diff --git a/applications/external/ibtn_fuzzer/application.fam b/applications/external/ibtn_fuzzer/application.fam index b27f47ba9..00c244c41 100644 --- a/applications/external/ibtn_fuzzer/application.fam +++ b/applications/external/ibtn_fuzzer/application.fam @@ -3,7 +3,6 @@ App( name="iButton Fuzzer", apptype=FlipperAppType.EXTERNAL, entry_point="ibtnfuzzer_start", - cdefines=["APP_IBTN_FUZZ"], requires=["gui", "storage", "dialogs", "input", "notification"], stack_size=1 * 1024, order=15, diff --git a/applications/external/lightmeter/application.fam b/applications/external/lightmeter/application.fam index 8cd90ee26..7df664517 100644 --- a/applications/external/lightmeter/application.fam +++ b/applications/external/lightmeter/application.fam @@ -3,7 +3,6 @@ App( name="[BH1750] Lightmeter", apptype=FlipperAppType.EXTERNAL, entry_point="lightmeter_app", - cdefines=["APP_LIGHTMETER"], requires=[ "gui", ], diff --git a/applications/external/metronome/application.fam b/applications/external/metronome/application.fam index a3435fd77..67411c5f2 100644 --- a/applications/external/metronome/application.fam +++ b/applications/external/metronome/application.fam @@ -3,7 +3,6 @@ App( name="Metronome", apptype=FlipperAppType.EXTERNAL, entry_point="metronome_app", - cdefines=["APP_METRONOME"], requires=[ "gui", ], diff --git a/applications/external/minesweeper/application.fam b/applications/external/minesweeper/application.fam index 405cb0d1c..c0211282c 100644 --- a/applications/external/minesweeper/application.fam +++ b/applications/external/minesweeper/application.fam @@ -3,7 +3,6 @@ App( name="Minesweeper", apptype=FlipperAppType.EXTERNAL, entry_point="minesweeper_app", - cdefines=["APP_MINESWEEPER"], requires=["gui"], stack_size=8 * 1024, fap_category="Games", diff --git a/applications/external/morse_code/application.fam b/applications/external/morse_code/application.fam index 16a52fbd5..5db0f6a94 100644 --- a/applications/external/morse_code/application.fam +++ b/applications/external/morse_code/application.fam @@ -3,7 +3,6 @@ App( name="Morse Code", apptype=FlipperAppType.EXTERNAL, entry_point="morse_code_app", - cdefines=["APP_MORSE_CODE"], requires=[ "gui", ], diff --git a/applications/external/mousejacker/application.fam b/applications/external/mousejacker/application.fam index 725e81ae8..f65b06cc2 100644 --- a/applications/external/mousejacker/application.fam +++ b/applications/external/mousejacker/application.fam @@ -3,7 +3,6 @@ App( name="[NRF24] Mouse Jacker", apptype=FlipperAppType.EXTERNAL, entry_point="mousejacker_app", - cdefines=["APP_MOUSEJACKER"], requires=[ "gui", "dialogs", diff --git a/applications/external/multi_converter/application.fam b/applications/external/multi_converter/application.fam index b19bce895..9f49f18ad 100644 --- a/applications/external/multi_converter/application.fam +++ b/applications/external/multi_converter/application.fam @@ -3,7 +3,6 @@ App( name="Multi Converter", apptype=FlipperAppType.EXTERNAL, entry_point="multi_converter_app", - cdefines=["APP_DEC_HEX_CONVERTER"], requires=["gui"], stack_size=1 * 1024, order=19, diff --git a/applications/external/nrfsniff/application.fam b/applications/external/nrfsniff/application.fam index 2a2b98ae7..c99b9bf22 100644 --- a/applications/external/nrfsniff/application.fam +++ b/applications/external/nrfsniff/application.fam @@ -3,7 +3,6 @@ App( name="[NRF24] Sniffer", apptype=FlipperAppType.EXTERNAL, entry_point="nrfsniff_app", - cdefines=["APP_NRFSNIFF"], requires=["gui"], stack_size=2 * 1024, order=70, diff --git a/applications/external/playlist/application.fam b/applications/external/playlist/application.fam index 06357e24b..e137cdb39 100644 --- a/applications/external/playlist/application.fam +++ b/applications/external/playlist/application.fam @@ -3,7 +3,6 @@ App( name="Sub-GHz Playlist", apptype=FlipperAppType.EXTERNAL, entry_point="playlist_app", - cdefines=["APP_PLAYLIST"], requires=["storage", "gui", "dialogs", "subghz"], stack_size=2 * 1024, order=14, diff --git a/applications/external/pocsag_pager/application.fam b/applications/external/pocsag_pager/application.fam index b0d1bedd1..3cef05374 100644 --- a/applications/external/pocsag_pager/application.fam +++ b/applications/external/pocsag_pager/application.fam @@ -3,7 +3,6 @@ App( name="POCSAG Pager", apptype=FlipperAppType.EXTERNAL, entry_point="pocsag_pager_app", - cdefines=["APP_POCSAG_PAGER"], requires=["gui"], stack_size=4 * 1024, order=50, diff --git a/applications/external/protoview/application.fam b/applications/external/protoview/application.fam index 234a00bc6..29cef0e10 100644 --- a/applications/external/protoview/application.fam +++ b/applications/external/protoview/application.fam @@ -3,7 +3,6 @@ App( name="ProtoView", apptype=FlipperAppType.EXTERNAL, entry_point="protoview_app_entry", - cdefines=["APP_PROTOVIEW"], requires=["gui"], stack_size=8*1024, order=50, diff --git a/applications/external/sentry_safe/application.fam b/applications/external/sentry_safe/application.fam index 070a677ba..8958dc8bc 100644 --- a/applications/external/sentry_safe/application.fam +++ b/applications/external/sentry_safe/application.fam @@ -3,7 +3,6 @@ App( name="[GPIO] Sentry Safe", apptype=FlipperAppType.EXTERNAL, entry_point="sentry_safe_app", - cdefines=["APP_SENTRY_SAFE"], requires=["gui"], stack_size=1 * 1024, order=80, diff --git a/applications/external/solitaire/application.fam b/applications/external/solitaire/application.fam index 66e4567ec..44152a038 100644 --- a/applications/external/solitaire/application.fam +++ b/applications/external/solitaire/application.fam @@ -3,7 +3,6 @@ App( name="Solitaire", apptype=FlipperAppType.EXTERNAL, entry_point="solitaire_app", - cdefines=["APP_SOLITAIRE"], requires=["gui","storage","canvas"], stack_size=2 * 1024, order=30, diff --git a/applications/external/spectrum_analyzer/application.fam b/applications/external/spectrum_analyzer/application.fam index 344c2244f..286aa64ba 100644 --- a/applications/external/spectrum_analyzer/application.fam +++ b/applications/external/spectrum_analyzer/application.fam @@ -3,7 +3,6 @@ App( name="Spectrum Analyzer", apptype=FlipperAppType.EXTERNAL, entry_point="spectrum_analyzer_app", - cdefines=["APP_SPECTRUM_ANALYZER"], requires=["gui"], stack_size=2 * 1024, order=12, diff --git a/applications/external/subbrute b/applications/external/subbrute index 49d32f8ed..685ac6540 160000 --- a/applications/external/subbrute +++ b/applications/external/subbrute @@ -1 +1 @@ -Subproject commit 49d32f8ed6790d3b9a99f261ad306766325a691a +Subproject commit 685ac6540de1bf5f7f4bd267c808850cf539587e diff --git a/applications/external/swd_probe/application.fam b/applications/external/swd_probe/application.fam index c1aec00ea..c255e6f81 100644 --- a/applications/external/swd_probe/application.fam +++ b/applications/external/swd_probe/application.fam @@ -3,7 +3,6 @@ App( name="SWD Probe", apptype=FlipperAppType.EXTERNAL, entry_point="swd_probe_app_main", - cdefines=["APP_SWD_PROBE"], requires=["notification", "gui", "storage", "dialogs", "cli"], stack_size=2 * 1024, order=10, diff --git a/applications/external/tetris_game/application.fam b/applications/external/tetris_game/application.fam index 69dda3297..998345c0e 100644 --- a/applications/external/tetris_game/application.fam +++ b/applications/external/tetris_game/application.fam @@ -3,7 +3,6 @@ App( name="Tetris", apptype=FlipperAppType.EXTERNAL, entry_point="tetris_game_app", - cdefines=["APP_TETRIS_GAME"], requires=["gui"], stack_size=2 * 1024, order=20, diff --git a/applications/external/text_viewer/application.fam b/applications/external/text_viewer/application.fam index dcd573c9d..518626f41 100644 --- a/applications/external/text_viewer/application.fam +++ b/applications/external/text_viewer/application.fam @@ -3,7 +3,6 @@ App( name="Text Viewer", apptype=FlipperAppType.EXTERNAL, entry_point="text_viewer_app", - cdefines=["APP_TEXT_VIEWER"], requires=[ "gui", "dialogs", diff --git a/applications/external/tictactoe_game/application.fam b/applications/external/tictactoe_game/application.fam index 2e769417e..8b67be874 100644 --- a/applications/external/tictactoe_game/application.fam +++ b/applications/external/tictactoe_game/application.fam @@ -3,7 +3,6 @@ App( name="Tic Tac Toe", apptype=FlipperAppType.EXTERNAL, entry_point="tictactoe_game_app", - cdefines=["APP_TICTACTOE_GAME"], requires=["gui"], stack_size=1 * 1024, order=40, diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index fbd21ddf7..600ed6ca3 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -3,7 +3,6 @@ App( name="Authenticator", apptype=FlipperAppType.EXTERNAL, entry_point="totp_app", - cdefines=["APP_TOTP"], requires=[ "gui", "cli", diff --git a/applications/external/uart_terminal/application.fam b/applications/external/uart_terminal/application.fam index c6cea5362..b167ab83e 100644 --- a/applications/external/uart_terminal/application.fam +++ b/applications/external/uart_terminal/application.fam @@ -3,7 +3,6 @@ App( name="UART Terminal", apptype=FlipperAppType.EXTERNAL, entry_point="uart_terminal_app", - cdefines=["APP_UART_TERMINAL"], requires=["gui"], stack_size=1 * 1024, order=90, diff --git a/applications/external/unitemp/application.fam b/applications/external/unitemp/application.fam index 89292e504..3971f5138 100644 --- a/applications/external/unitemp/application.fam +++ b/applications/external/unitemp/application.fam @@ -3,7 +3,6 @@ App( name="Temp sensors reader", apptype=FlipperAppType.EXTERNAL, entry_point="unitemp_app", - cdefines=["UNITEMP_APP"], requires=[ "gui", ], diff --git a/applications/external/wav_player/application.fam b/applications/external/wav_player/application.fam index 6ab832d03..f76c1cd39 100644 --- a/applications/external/wav_player/application.fam +++ b/applications/external/wav_player/application.fam @@ -3,7 +3,6 @@ App( name="WAV Player", apptype=FlipperAppType.EXTERNAL, entry_point="wav_player_app", - cdefines=["APP_WAV_PLAYER"], stack_size=4 * 1024, order=46, fap_icon="wav_10px.png", diff --git a/applications/external/wifi_marauder_companion/application.fam b/applications/external/wifi_marauder_companion/application.fam index e3185d50a..d8bfc58a6 100644 --- a/applications/external/wifi_marauder_companion/application.fam +++ b/applications/external/wifi_marauder_companion/application.fam @@ -3,7 +3,6 @@ App( name="[ESP32] WiFi Marauder", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_marauder_app", - cdefines=["APP_WIFI_MARAUDER"], requires=["gui"], stack_size=1 * 1024, order=90, diff --git a/applications/external/wifi_scanner/application.fam b/applications/external/wifi_scanner/application.fam index dc229efc0..c160e8aa9 100644 --- a/applications/external/wifi_scanner/application.fam +++ b/applications/external/wifi_scanner/application.fam @@ -3,7 +3,6 @@ App( name="[WiFi] Scanner", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_scanner_app", - cdefines=["APP_WIFI_SCANNER"], requires=["gui"], stack_size=2 * 1024, order=110, diff --git a/applications/external/zombiez/application.fam b/applications/external/zombiez/application.fam index 3245187d2..069e591bc 100644 --- a/applications/external/zombiez/application.fam +++ b/applications/external/zombiez/application.fam @@ -3,7 +3,6 @@ App( name="Zombiez", apptype=FlipperAppType.EXTERNAL, entry_point="zombiez_game_app", - cdefines=["APP_ZOMBIEZ_GAME"], requires=["gui"], stack_size=2 * 1024, order=280, From d12f76dacb8d6266ff784fb6ba3d136288fcabce Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 15 Mar 2023 16:01:33 +0400 Subject: [PATCH 12/34] Nfc: fixes for latest PVS-studio 7.23 (#2490) --- lib/nfc/parsers/plantain_4k_parser.c | 2 +- lib/nfc/parsers/plantain_parser.c | 2 +- lib/nfc/parsers/troika_4k_parser.c | 2 +- lib/nfc/parsers/troika_parser.c | 2 +- lib/nfc/parsers/two_cities.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/nfc/parsers/plantain_4k_parser.c b/lib/nfc/parsers/plantain_4k_parser.c index aed41965c..19da0b5eb 100644 --- a/lib/nfc/parsers/plantain_4k_parser.c +++ b/lib/nfc/parsers/plantain_4k_parser.c @@ -118,7 +118,7 @@ bool plantain_4k_parser_parse(NfcDeviceData* dev_data) { } furi_string_printf( - dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%ld\n", card_number, balance); + dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance); return true; } diff --git a/lib/nfc/parsers/plantain_parser.c b/lib/nfc/parsers/plantain_parser.c index 3a1d17732..2e4091dda 100644 --- a/lib/nfc/parsers/plantain_parser.c +++ b/lib/nfc/parsers/plantain_parser.c @@ -91,7 +91,7 @@ bool plantain_parser_parse(NfcDeviceData* dev_data) { } furi_string_printf( - dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%ld\n", card_number, balance); + dev_data->parsed_data, "\e#Plantain\nN:%llu-\nBalance:%lu\n", card_number, balance); return true; } diff --git a/lib/nfc/parsers/troika_4k_parser.c b/lib/nfc/parsers/troika_4k_parser.c index d87b4eba7..1f1b85a5c 100644 --- a/lib/nfc/parsers/troika_4k_parser.c +++ b/lib/nfc/parsers/troika_4k_parser.c @@ -99,7 +99,7 @@ bool troika_4k_parser_parse(NfcDeviceData* dev_data) { number >>= 4; furi_string_printf( - dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance); + dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance); return true; } diff --git a/lib/nfc/parsers/troika_parser.c b/lib/nfc/parsers/troika_parser.c index 9c16296f3..bfd22364b 100644 --- a/lib/nfc/parsers/troika_parser.c +++ b/lib/nfc/parsers/troika_parser.c @@ -79,7 +79,7 @@ bool troika_parser_parse(NfcDeviceData* dev_data) { number >>= 4; furi_string_printf( - dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance); + dev_data->parsed_data, "\e#Troika\nNum: %lu\nBalance: %u rur.", number, balance); troika_parsed = true; } while(false); diff --git a/lib/nfc/parsers/two_cities.c b/lib/nfc/parsers/two_cities.c index 0e2ed5690..d6d4279dd 100644 --- a/lib/nfc/parsers/two_cities.c +++ b/lib/nfc/parsers/two_cities.c @@ -136,7 +136,7 @@ bool two_cities_parser_parse(NfcDeviceData* dev_data) { furi_string_printf( dev_data->parsed_data, - "\e#Troika+Plantain\nPN: %llu-\nPB: %ld rur.\nTN: %ld\nTB: %d rur.\n", + "\e#Troika+Plantain\nPN: %llu-\nPB: %lu rur.\nTN: %lu\nTB: %u rur.\n", card_number, balance, troika_number, From 3a242e5fc319f28bf7ee2aa02869744cabdc5d86 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 15 Mar 2023 16:16:52 +0400 Subject: [PATCH 13/34] SubGhz: bugfix unable to send, new generated secplus_v2 protocol (#2488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- lib/subghz/protocols/keeloq.c | 4 +++- lib/subghz/protocols/secplus_v2.c | 9 +++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 57d1cd22d..7748da1ee 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -182,7 +182,9 @@ bool subghz_protocol_keeloq_create_data( instance->generic.data_count_bit = 64; bool res = subghz_protocol_keeloq_gen_data(instance, btn); if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(subghz_block_generic_serialize(&instance->generic, flipper_format, preset) != + SubGhzProtocolStatusOk) + res = false; } return res; } diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 4a3815f0d..c8ecbea22 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -599,19 +599,20 @@ bool subghz_protocol_secplus_v2_create_data( instance->generic.data_count_bit = (uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found; subghz_protocol_secplus_v2_encode(instance); - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + SubGhzProtocolStatus 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->secplus_packet_1 >> (i * 8)) & 0xFF; } - if(res && + if((res == SubGhzProtocolStatusOk) && !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); - res = false; + res = SubGhzProtocolStatusErrorParserOthers; } - return res; + return res == SubGhzProtocolStatusOk; } void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) { From e22668e19610ead06697a3b19b2ac88cdd4376d1 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Wed, 15 Mar 2023 05:35:11 -0700 Subject: [PATCH 14/34] Picopass standard KDF dictionary (#2478) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Split iclass dictionaries based on KDF * Allow cancelling during key test Co-authored-by: あく --- .../picopass/helpers/iclass_elite_dict.c | 15 +++++- .../picopass/helpers/iclass_elite_dict.h | 3 +- .../external/picopass/picopass_worker.c | 46 ++++++++---------- .../picopass/assets/iclass_elite_dict.txt | 12 ----- .../picopass/assets/iclass_standard_dict.txt | 47 +++++++++++++++++++ 5 files changed, 82 insertions(+), 41 deletions(-) create mode 100644 assets/resources/apps_data/picopass/assets/iclass_standard_dict.txt diff --git a/applications/external/picopass/helpers/iclass_elite_dict.c b/applications/external/picopass/helpers/iclass_elite_dict.c index e8c13dd1d..f92dce0aa 100644 --- a/applications/external/picopass/helpers/iclass_elite_dict.c +++ b/applications/external/picopass/helpers/iclass_elite_dict.c @@ -5,6 +5,7 @@ #define ICLASS_ELITE_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_elite_dict.txt") #define ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt") +#define ICLASS_STANDARD_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_standard_dict.txt") #define TAG "IclassEliteDict" @@ -25,6 +26,9 @@ bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) { (storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_NAME, NULL) == FSE_OK); } else if(dict_type == IclassEliteDictTypeUser) { dict_present = (storage_common_stat(storage, ICLASS_ELITE_DICT_USER_NAME, NULL) == FSE_OK); + } else if(dict_type == IclassStandardDictTypeFlipper) { + dict_present = + (storage_common_stat(storage, ICLASS_STANDARD_DICT_FLIPPER_NAME, NULL) == FSE_OK); } furi_record_close(RECORD_STORAGE); @@ -52,6 +56,15 @@ IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) { buffered_file_stream_close(dict->stream); break; } + } else if(dict_type == IclassStandardDictTypeFlipper) { + if(!buffered_file_stream_open( + dict->stream, + ICLASS_STANDARD_DICT_FLIPPER_NAME, + FSAM_READ, + FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(dict->stream); + break; + } } // Read total amount of keys @@ -148,4 +161,4 @@ bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) { furi_string_free(key_str); return key_added; -} \ No newline at end of file +} diff --git a/applications/external/picopass/helpers/iclass_elite_dict.h b/applications/external/picopass/helpers/iclass_elite_dict.h index e5ec8dfcb..150cd1b76 100644 --- a/applications/external/picopass/helpers/iclass_elite_dict.h +++ b/applications/external/picopass/helpers/iclass_elite_dict.h @@ -9,6 +9,7 @@ typedef enum { IclassEliteDictTypeUser, IclassEliteDictTypeFlipper, + IclassStandardDictTypeFlipper, } IclassEliteDictType; typedef struct IclassEliteDict IclassEliteDict; @@ -25,4 +26,4 @@ bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key); bool iclass_elite_dict_rewind(IclassEliteDict* dict); -bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key); \ No newline at end of file +bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key); diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c index f2e9e82b8..e61b67d9f 100644 --- a/applications/external/picopass/picopass_worker.c +++ b/applications/external/picopass/picopass_worker.c @@ -172,14 +172,18 @@ ReturnCode picopass_read_preauth(PicopassBlock* AA1) { return ERR_NONE; } -static ReturnCode picopass_auth_dict( - uint8_t* csn, - PicopassPacs* pacs, - uint8_t* div_key, - IclassEliteDictType dict_type, - bool elite) { +static ReturnCode + picopass_auth_dict(PicopassWorker* picopass_worker, IclassEliteDictType dict_type) { rfalPicoPassReadCheckRes rcRes; rfalPicoPassCheckRes chkRes; + bool elite = (dict_type != IclassStandardDictTypeFlipper); + + PicopassDeviceData* dev_data = picopass_worker->dev_data; + PicopassBlock* AA1 = dev_data->AA1; + PicopassPacs* pacs = &dev_data->pacs; + + uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; + uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data; ReturnCode err = ERR_PARAM; @@ -204,7 +208,8 @@ static ReturnCode picopass_auth_dict( while(iclass_elite_dict_get_next_key(dict, key)) { FURI_LOG_D( TAG, - "Try to auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x", + "Try to %s auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x", + elite ? "elite" : "standard", index++, key[0], key[1], @@ -230,6 +235,8 @@ static ReturnCode picopass_auth_dict( memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); break; } + + if(picopass_worker->state != PicopassWorkerStateDetect) break; } iclass_elite_dict_free(dict); @@ -237,38 +244,23 @@ static ReturnCode picopass_auth_dict( return err; } -ReturnCode picopass_auth(PicopassBlock* AA1, PicopassPacs* pacs) { +ReturnCode picopass_auth(PicopassWorker* picopass_worker) { ReturnCode err; FURI_LOG_I(TAG, "Starting system dictionary attack [Standard KDF]"); - err = picopass_auth_dict( - AA1[PICOPASS_CSN_BLOCK_INDEX].data, - pacs, - AA1[PICOPASS_KD_BLOCK_INDEX].data, - IclassEliteDictTypeFlipper, - false); + err = picopass_auth_dict(picopass_worker, IclassStandardDictTypeFlipper); if(err == ERR_NONE) { return ERR_NONE; } FURI_LOG_I(TAG, "Starting user dictionary attack [Elite KDF]"); - err = picopass_auth_dict( - AA1[PICOPASS_CSN_BLOCK_INDEX].data, - pacs, - AA1[PICOPASS_KD_BLOCK_INDEX].data, - IclassEliteDictTypeUser, - true); + err = picopass_auth_dict(picopass_worker, IclassEliteDictTypeUser); if(err == ERR_NONE) { return ERR_NONE; } FURI_LOG_I(TAG, "Starting system dictionary attack [Elite KDF]"); - err = picopass_auth_dict( - AA1[PICOPASS_CSN_BLOCK_INDEX].data, - pacs, - AA1[PICOPASS_KD_BLOCK_INDEX].data, - IclassEliteDictTypeFlipper, - true); + err = picopass_auth_dict(picopass_worker, IclassEliteDictTypeFlipper); if(err == ERR_NONE) { return ERR_NONE; } @@ -520,7 +512,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { } if(nextState == PicopassWorkerEventSuccess) { - err = picopass_auth(AA1, pacs); + err = picopass_auth(picopass_worker); if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_try_auth error %d", err); nextState = PicopassWorkerEventFail; diff --git a/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt b/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt index 5da2a2fa8..d11892372 100644 --- a/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt +++ b/assets/resources/apps_data/picopass/assets/iclass_elite_dict.txt @@ -1,16 +1,10 @@ ## From https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/iclass_default_keys.dic -# AA1 -AEA684A6DAB23278 # key1/Kc from PicoPass 2k documentation 7665544332211000 # SAGEM 0123456789ABCDEF -# from loclass demo file. -5b7c62c491c11b39 -# Kd from PicoPass 2k documentation -F0E1D2C3B4A59687 # PicoPass Default Exchange Key 5CBCF1DA45D5FB4F # From HID multiclassSE reader @@ -19,12 +13,6 @@ F0E1D2C3B4A59687 6EFD46EFCBB3C875 E033CA419AEE43F9 -# iCopy-x DRM keys -# iCL tags -2020666666668888 -# iCS tags reversed from the SOs -6666202066668888 - # default picopass KD / Page 0 / Book 1 FDCB5A52EA8F3090 237FF9079863DF44 diff --git a/assets/resources/apps_data/picopass/assets/iclass_standard_dict.txt b/assets/resources/apps_data/picopass/assets/iclass_standard_dict.txt new file mode 100644 index 000000000..46808ef60 --- /dev/null +++ b/assets/resources/apps_data/picopass/assets/iclass_standard_dict.txt @@ -0,0 +1,47 @@ + +## From https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/iclass_default_keys.dic + +# AA1 +AEA684A6DAB23278 +# key1/Kc from PicoPass 2k documentation +7665544332211000 +# SAGEM +0123456789ABCDEF +# from loclass demo file. +5b7c62c491c11b39 +# Kd from PicoPass 2k documentation +F0E1D2C3B4A59687 +# PicoPass Default Exchange Key +5CBCF1DA45D5FB4F +# From HID multiclassSE reader +31ad7ebd2f282168 +# From pastebin: https://pastebin.com/uHqpjiuU +6EFD46EFCBB3C875 +E033CA419AEE43F9 + +# iCopy-x DRM keys +# iCL tags +2020666666668888 +# iCS tags reversed from the SOs +6666202066668888 + +# default picopass KD / Page 0 / Book 1 +FDCB5A52EA8F3090 +237FF9079863DF44 +5ADC25FB27181D32 +83B881F2936B2E49 +43644E61EE866BA5 +897034143D016080 +82D17B44C0122963 +4895CA7DE65E2025 +DADAD4C57BE271B7 +E41E9EDEF5719ABF +293D275EC3AF9C7F +C3C169251B8A70FB +F41DAF58B20C8B91 +28877A609EC0DD2B +66584C91EE80D5E5 +C1B74D7478053AE2 + +# default iCLASS RFIDeas +6B65797374726B72 From a69ae93871a4bf8cd9a1b5bb2cba06218199c6aa Mon Sep 17 00:00:00 2001 From: Leopold Date: Wed, 15 Mar 2023 21:52:32 +0800 Subject: [PATCH 15/34] Add new nfc apdu cli command (#2482) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/nfc/nfc_cli.c | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/applications/main/nfc/nfc_cli.c b/applications/main/nfc/nfc_cli.c index 23335e299..6e6e04ca9 100644 --- a/applications/main/nfc/nfc_cli.c +++ b/applications/main/nfc/nfc_cli.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -12,6 +13,7 @@ static void nfc_cli_print_usage() { printf("Cmd list:\r\n"); printf("\tdetect\t - detect nfc device\r\n"); printf("\temulate\t - emulate predefined nfca card\r\n"); + printf("\tapdu\t - Send APDU and print response \r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { printf("\tfield\t - turn field on\r\n"); } @@ -98,6 +100,67 @@ static void nfc_cli_field(Cli* cli, FuriString* args) { furi_hal_nfc_sleep(); } +static void nfc_cli_apdu(Cli* cli, FuriString* args) { + UNUSED(cli); + if(furi_hal_nfc_is_busy()) { + printf("Nfc is busy\r\n"); + return; + } + + furi_hal_nfc_exit_sleep(); + FuriString* data = NULL; + data = furi_string_alloc(); + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData dev_data = {}; + uint8_t* req_buffer = NULL; + uint8_t* resp_buffer = NULL; + size_t apdu_size = 0; + size_t resp_size = 0; + + do { + if(!args_read_string_and_trim(args, data)) { + printf( + "Use like `nfc apdu 00a404000e325041592e5359532e444446303100 00a4040008a0000003010102` \r\n"); + break; + } + + printf("detecting tag\r\n"); + if(!furi_hal_nfc_detect(&dev_data, 300)) { + printf("Failed to detect tag\r\n"); + break; + } + do { + apdu_size = furi_string_size(data) / 2; + req_buffer = malloc(apdu_size); + hex_chars_to_uint8(furi_string_get_cstr(data), req_buffer); + + memcpy(tx_rx.tx_data, req_buffer, apdu_size); + tx_rx.tx_bits = apdu_size * 8; + tx_rx.tx_rx_type = FuriHalNfcTxRxTypeDefault; + + printf("Sending APDU:%s to Tag\r\n", furi_string_get_cstr(data)); + if(!furi_hal_nfc_tx_rx(&tx_rx, 300)) { + printf("Failed to tx_rx\r\n"); + break; + } + resp_size = (tx_rx.rx_bits / 8) * 2; + resp_buffer = malloc(resp_size); + uint8_to_hex_chars(tx_rx.rx_data, resp_buffer, resp_size); + resp_buffer[resp_size] = 0; + printf("Response: %s\r\n", resp_buffer); + free(req_buffer); + free(resp_buffer); + req_buffer = NULL; + resp_buffer = NULL; + } while(args_read_string_and_trim(args, data)); + } while(false); + + free(req_buffer); + free(resp_buffer); + furi_string_free(data); + furi_hal_nfc_sleep(); +} + static void nfc_cli(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* cmd; @@ -117,6 +180,11 @@ static void nfc_cli(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "apdu") == 0) { + nfc_cli_apdu(cli, args); + break; + } + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { if(furi_string_cmp_str(cmd, "field") == 0) { nfc_cli_field(cli, args); From c27d4d78f98869ba8ae3fdb01b322c52efe39d5e Mon Sep 17 00:00:00 2001 From: Liam Hays Date: Wed, 15 Mar 2023 08:51:15 -0600 Subject: [PATCH 16/34] Fix auto-capitalization in the keyboard when the text box is empty. (#2483) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: hedger Co-authored-by: あく --- applications/services/gui/modules/text_input.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/services/gui/modules/text_input.c b/applications/services/gui/modules/text_input.c index 32607e884..86b7bca1e 100644 --- a/applications/services/gui/modules/text_input.c +++ b/applications/services/gui/modules/text_input.c @@ -309,9 +309,9 @@ static void text_input_handle_ok(TextInput* text_input, TextInputModel* model, b char selected = get_selected_char(model); size_t text_length = strlen(model->text_buffer); - bool toogle_case = text_length == 0; - if(shift) toogle_case = !toogle_case; - if(toogle_case) { + bool toggle_case = text_length == 0 || model->clear_default_text; + if(shift) toggle_case = !toggle_case; + if(toggle_case) { selected = char_to_uppercase(selected); } From d8385b7f917ba10bd2ba49e5c076374de8b4c2ac Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 15 Mar 2023 19:24:56 +0400 Subject: [PATCH 17/34] gh: use shallow clones whenever possible (#2491) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * gh: use shallow clones whenever possible * gh: reverted submodule checks * gh: lint: joined linting scripts * gh: renamed linter workflow * check python linter output * gh: reworked linter * checking c linter * gh: merged submodule check & lint * gh: renamed step * gh: removed redundant `submodules: false` Co-authored-by: あく --- .github/workflows/build.yml | 6 +-- ...dules.yml => lint_and_submodule_check.yml} | 34 +++++++++++--- .github/workflows/lint_c.yml | 47 ------------------- .github/workflows/lint_python.yml | 33 ------------- .github/workflows/merge_report.yml | 2 +- .github/workflows/pvs_studio.yml | 2 +- .github/workflows/unit_tests.yml | 2 +- .github/workflows/updater_test.yml | 5 +- fbt | 4 +- fbt.cmd | 4 +- 10 files changed, 41 insertions(+), 98 deletions(-) rename .github/workflows/{check_submodules.yml => lint_and_submodule_check.yml} (56%) delete mode 100644 .github/workflows/lint_c.yml delete mode 100644 .github/workflows/lint_python.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 46d95ede5..79535c934 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: - name: 'Checkout code' uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 1 ref: ${{ github.event.pull_request.head.sha }} - name: 'Get commit details' @@ -177,8 +177,8 @@ jobs: - name: 'Checkout code' uses: actions/checkout@v3 with: - fetch-depth: 0 - submodules: true + fetch-depth: 1 + submodules: false ref: ${{ github.event.pull_request.head.sha }} - name: 'Get commit details' diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/lint_and_submodule_check.yml similarity index 56% rename from .github/workflows/check_submodules.yml rename to .github/workflows/lint_and_submodule_check.yml index 2eb2027c9..ede357932 100644 --- a/.github/workflows/check_submodules.yml +++ b/.github/workflows/lint_and_submodule_check.yml @@ -1,4 +1,4 @@ -name: 'Check submodules branch' +name: 'Lint sources & check submodule integrity' on: push: @@ -9,9 +9,14 @@ on: - '*' pull_request: +env: + TARGETS: f7 + FBT_TOOLCHAIN_PATH: /runner/_work + SET_GH_OUTPUT: 1 + jobs: - check_protobuf: - runs-on: [self-hosted, FlipperZeroShell] + lint_sources_check_submodules: + runs-on: [self-hosted,FlipperZeroShell] steps: - name: 'Decontaminate previous build leftovers' run: | @@ -22,9 +27,10 @@ jobs: - name: 'Checkout code' uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 1 ref: ${{ github.event.pull_request.head.sha }} + - name: 'Check protobuf branch' run: | git submodule update --init @@ -36,12 +42,28 @@ jobs: BRANCHES=$(git branch -r --contains "$SUBMODULE_HASH"); COMMITS_IN_BRANCH="$(git rev-list --count dev)"; if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then - echo "name=fails::error" >> $GITHUB_OUTPUT + echo "name=fails::error" >> $GITHUB_OUTPUT; echo "::error::Error: Too low commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; exit 1; fi if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then - echo "name=fails::error" >> $GITHUB_OUTPUT + echo "name=fails::error" >> $GITHUB_OUTPUT; echo "::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH"; exit 1; fi + + - name: 'Check Python code formatting' + id: syntax_check_py + run: ./fbt lint_py 2>&1 >/dev/null || echo "errors=1" >> $GITHUB_OUTPUT + + - name: 'Check C++ code formatting' + if: always() + id: syntax_check_cpp + run: ./fbt lint 2>&1 >/dev/null || echo "errors=1" >> $GITHUB_OUTPUT + + - name: Report code formatting errors + if: ( steps.syntax_check_py.outputs.errors || steps.syntax_check_cpp.outputs.errors ) && github.event.pull_request + run: | + echo "Code formatting errors found"; + echo "Please run './fbt format' or './fbt format_py' to fix them"; + exit 1; diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml deleted file mode 100644 index a6fd5127c..000000000 --- a/.github/workflows/lint_c.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: 'Lint C/C++ with clang-format' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - TARGETS: f7 - FBT_TOOLCHAIN_PATH: /runner/_work - SET_GH_OUTPUT: 1 - -jobs: - lint_c_cpp: - 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: 'Check code formatting' - id: syntax_check - run: ./fbt lint - - - name: Report code formatting errors - if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request - uses: peter-evans/create-or-update-comment@v1 - with: - issue-number: ${{ github.event.pull_request.number }} - body: | - Please fix following code formatting errors: - ``` - ${{ steps.syntax_check.outputs.errors }} - ``` - You might want to run `./fbt format` for an auto-fix. diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml deleted file mode 100644 index 66c36064c..000000000 --- a/.github/workflows/lint_python.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: 'Python Lint' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - FBT_TOOLCHAIN_PATH: /runner/_work - SET_GH_OUTPUT: 1 - -jobs: - lint_python: - 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: 'Check code formatting' - run: ./fbt lint_py diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml index 13fab0948..3b7cd2349 100644 --- a/.github/workflows/merge_report.yml +++ b/.github/workflows/merge_report.yml @@ -21,7 +21,7 @@ jobs: - name: 'Checkout code' uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 1 ref: ${{ github.event.pull_request.head.sha }} - name: 'Get commit details' diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index 65a8b6150..9105a0fd6 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -28,7 +28,7 @@ jobs: - name: 'Checkout code' uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 1 ref: ${{ github.event.pull_request.head.sha }} - name: 'Get commit details' diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 527e9a71e..bed5a470d 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -21,7 +21,7 @@ jobs: - name: Checkout code uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 1 ref: ${{ github.event.pull_request.head.sha }} - name: 'Get flipper from device manager (mock)' diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index 300440aae..eba34e988 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -21,7 +21,8 @@ jobs: - name: Checkout code uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 1 + submodules: false ref: ${{ github.event.pull_request.head.sha }} - name: 'Get flipper from device manager (mock)' @@ -62,7 +63,7 @@ jobs: uses: actions/checkout@v3 if: failure() with: - fetch-depth: 0 + fetch-depth: 1 ref: ${{ steps.release_tag.outputs.tag }} - name: 'Flash last release' diff --git a/fbt b/fbt index f80e802b6..efe625f03 100755 --- a/fbt +++ b/fbt @@ -25,10 +25,10 @@ fi if [ -z "$FBT_NO_SYNC" ]; then if [ ! -d "$SCRIPT_PATH/.git" ]; then - echo "\".git\" directory not found, please clone repo via \"git clone --recursive\""; + echo "\".git\" directory not found, please clone repo via \"git clone\""; exit 1; fi - git submodule update --init; + git submodule update --init --depth 1; fi $SCONS_EP $SCONS_DEFAULT_FLAGS "$@" diff --git a/fbt.cmd b/fbt.cmd index 92c734860..6e839c778 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -5,9 +5,9 @@ set SCONS_EP=python -m SCons if [%FBT_NO_SYNC%] == [] ( if exist ".git" ( - git submodule update --init + git submodule update --init --depth 1 ) else ( - echo Not in a git repo, please clone with git clone --recursive + echo Not in a git repo, please clone with "git clone" exit /b 1 ) ) From 2046ac6604de171df2dcb18f9865f8fa7f2aa879 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Mar 2023 00:00:10 +0300 Subject: [PATCH 18/34] Lets try new changes for marauder companion https://github.com/tcpassos/flipperzero-firmware-with-wifi-marauder-companion --- ReadMe.md | 2 +- .../wifi_marauder_scene_console_output.c | 36 ++++++++++++++++--- .../wifi_marauder_app.c | 33 ++++++++++++++--- .../wifi_marauder_app.h | 2 ++ .../wifi_marauder_app_i.h | 12 ++++++- .../wifi_marauder_pcap.c | 33 +++++++++++++++++ .../wifi_marauder_pcap.h | 11 ++++++ .../wifi_marauder_uart.c | 33 +++++++++++++---- .../wifi_marauder_uart.h | 4 ++- 9 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_pcap.c create mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_pcap.h diff --git a/ReadMe.md b/ReadMe.md index 84b80a1e8..8d48adf5a 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -135,7 +135,7 @@ You can support us by using links or addresses below: - WAV Player [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) - Fixed and improved by [LTVA1](https://github.com/LTVA1/wav_player) - Barcode generator plugin [(original by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) - [EAN-8 and refactoring](https://github.com/DarkFlippers/unleashed-firmware/pull/154) by @msvsergey - GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin) -- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) +- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) - Saving pcap on flipper microSD [by tcpassos](https://github.com/tcpassos/flipperzero-firmware-with-wifi-marauder-companion) - NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker) - Simple Clock (timer by GMMan) [(original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61) - **Sub-GHz Remote** (UniversalRF Remix) [(by @darmiel & @xMasterX)](https://github.com/darmiel/flipper-playlist/tree/feat/unirf-protocols) (original by @ESurge) diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c index 8b9984dbf..77beb733c 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c @@ -14,10 +14,26 @@ void wifi_marauder_console_output_handle_rx_data_cb(uint8_t* buf, size_t len, vo // Null-terminate buf and append to text box store buf[len] = '\0'; furi_string_cat_printf(app->text_box_store, "%s", buf); - view_dispatcher_send_custom_event(app->view_dispatcher, WifiMarauderEventRefreshConsoleOutput); } +void wifi_marauder_console_output_handle_rx_packets_cb(uint8_t* buf, size_t len, void* context) { + furi_assert(context); + WifiMarauderApp* app = context; + + // If it is a sniff function, open the pcap file for recording + if(strncmp("sniff", app->selected_tx_string, strlen("sniff")) == 0 && !app->is_writing) { + app->is_writing = true; + if(!app->capture_file || !storage_file_is_open(app->capture_file)) { + wifi_marauder_create_pcap_file(app); + } + } + + if(app->is_writing) { + storage_file_write(app->capture_file, buf, len); + } +} + void wifi_marauder_scene_console_output_on_enter(void* context) { WifiMarauderApp* app = context; @@ -33,8 +49,8 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { furi_string_reset(app->text_box_store); app->text_box_store_strlen = 0; if(0 == strncmp("help", app->selected_tx_string, strlen("help"))) { - const char* help_msg = - "Marauder companion v0.3.0\nFor app support/feedback,\nreach out to me:\n@cococode#6011 (discord)\n0xchocolate (github)\n"; + const char* help_msg = "Marauder companion " WIFI_MARAUDER_APP_VERSION + "\nby @0xchocolate\nmodified by @tcpassos\n"; furi_string_cat_str(app->text_box_store, help_msg); app->text_box_store_strlen += strlen(help_msg); } @@ -54,7 +70,11 @@ void wifi_marauder_scene_console_output_on_enter(void* context) { // Register callback to receive data wifi_marauder_uart_set_handle_rx_data_cb( - app->uart, wifi_marauder_console_output_handle_rx_data_cb); // setup callback for rx thread + app->uart, + wifi_marauder_console_output_handle_rx_data_cb); // setup callback for general log rx thread + wifi_marauder_uart_set_handle_rx_data_cb( + app->lp_uart, + wifi_marauder_console_output_handle_rx_packets_cb); // setup callback for packets rx thread // Send command with newline '\n' if(app->is_command && app->selected_tx_string) { @@ -84,9 +104,15 @@ void wifi_marauder_scene_console_output_on_exit(void* context) { // Unregister rx callback wifi_marauder_uart_set_handle_rx_data_cb(app->uart, NULL); + wifi_marauder_uart_set_handle_rx_data_cb(app->lp_uart, NULL); // Automatically stop the scan when exiting view if(app->is_command) { wifi_marauder_uart_tx((uint8_t*)("stopscan\n"), strlen("stopscan\n")); } -} \ No newline at end of file + + app->is_writing = false; + if(app->capture_file && storage_file_is_open(app->capture_file)) { + storage_file_close(app->capture_file); + } +} diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index 1deb4e6f2..7563d927a 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -25,6 +25,9 @@ WifiMarauderApp* wifi_marauder_app_alloc() { WifiMarauderApp* app = malloc(sizeof(WifiMarauderApp)); app->gui = furi_record_open(RECORD_GUI); + app->dialogs = furi_record_open(RECORD_DIALOGS); + app->storage = furi_record_open(RECORD_STORAGE); + app->capture_file = storage_file_alloc(app->storage); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&wifi_marauder_scene_handlers, app); @@ -67,6 +70,14 @@ WifiMarauderApp* wifi_marauder_app_alloc() { return app; } +void wifi_marauder_make_app_folder(WifiMarauderApp* app) { + furi_assert(app); + + if(!storage_simply_mkdir(app->storage, MARAUDER_APP_FOLDER)) { + dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder"); + } +} + void wifi_marauder_app_free(WifiMarauderApp* app) { furi_assert(app); @@ -77,33 +88,47 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { text_box_free(app->text_box); furi_string_free(app->text_box_store); text_input_free(app->text_input); + storage_file_free(app->capture_file); // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); wifi_marauder_uart_free(app->uart); + wifi_marauder_uart_free(app->lp_uart); // Close records furi_record_close(RECORD_GUI); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); free(app); } int32_t wifi_marauder_app(void* p) { UNUSED(p); - furi_hal_power_enable_otg(); - furi_delay_ms(300); + + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + furi_delay_ms(200); WifiMarauderApp* wifi_marauder_app = wifi_marauder_app_alloc(); - wifi_marauder_app->uart = wifi_marauder_uart_init(wifi_marauder_app); + wifi_marauder_make_app_folder(wifi_marauder_app); + + wifi_marauder_app->uart = wifi_marauder_usart_init(wifi_marauder_app); + wifi_marauder_app->lp_uart = wifi_marauder_lp_uart_init(wifi_marauder_app); view_dispatcher_run(wifi_marauder_app->view_dispatcher); wifi_marauder_app_free(wifi_marauder_app); - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } return 0; } diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.h b/applications/external/wifi_marauder_companion/wifi_marauder_app.h index 2d41daa55..92dd1dbd9 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.h @@ -4,6 +4,8 @@ extern "C" { #endif +#define WIFI_MARAUDER_APP_VERSION "v0.3.1" + typedef struct WifiMarauderApp WifiMarauderApp; #ifdef __cplusplus diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h index 4df9e326b..1165c6d9d 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -6,6 +6,7 @@ #include "scenes/wifi_marauder_scene.h" #include "wifi_marauder_custom_event.h" #include "wifi_marauder_uart.h" +#include "wifi_marauder_pcap.h" #include #include @@ -14,11 +15,16 @@ #include #include +#include +#include + #define NUM_MENU_ITEMS (16) #define WIFI_MARAUDER_TEXT_BOX_STORE_SIZE (4096) #define WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE (512) +#define MARAUDER_APP_FOLDER EXT_PATH("apps_data/marauder") + struct WifiMarauderApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -29,11 +35,14 @@ struct WifiMarauderApp { size_t text_box_store_strlen; TextBox* text_box; TextInput* text_input; - //Widget* widget; + Storage* storage; + File* capture_file; + DialogsApp* dialogs; VariableItemList* var_item_list; WifiMarauderUart* uart; + WifiMarauderUart* lp_uart; int selected_menu_index; int selected_option_index[NUM_MENU_ITEMS]; const char* selected_tx_string; @@ -41,6 +50,7 @@ struct WifiMarauderApp { bool is_custom_tx_string; bool focus_console_start; bool show_stopscan_tip; + bool is_writing; // For input source and destination MAC in targeted deauth attack int special_case_input_step; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_pcap.c b/applications/external/wifi_marauder_companion/wifi_marauder_pcap.c new file mode 100644 index 000000000..fc5f39022 --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_pcap.c @@ -0,0 +1,33 @@ +#include "wifi_marauder_app_i.h" +#include "wifi_marauder_pcap.h" + +void wifi_marauder_get_prefix_from_cmd(char* dest, const char* command) { + int start, end, delta; + start = strlen("sniff"); + end = strcspn(command, " "); + delta = end - start; + strncpy(dest, command + start, end - start); + dest[delta] = '\0'; +} + +void wifi_marauder_create_pcap_file(WifiMarauderApp* app) { + char prefix[10]; + char capture_file_path[100]; + wifi_marauder_get_prefix_from_cmd(prefix, app->selected_tx_string); + + int i = 0; + do { + snprintf( + capture_file_path, + sizeof(capture_file_path), + "%s/%s_%d.pcap", + MARAUDER_APP_FOLDER, + prefix, + i); + i++; + } while(storage_file_exists(app->storage, capture_file_path)); + + if(!storage_file_open(app->capture_file, capture_file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { + dialog_message_show_storage_error(app->dialogs, "Cannot open pcap file"); + } +} \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_pcap.h b/applications/external/wifi_marauder_companion/wifi_marauder_pcap.h new file mode 100644 index 000000000..29f8fcf8a --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_pcap.h @@ -0,0 +1,11 @@ +#pragma once + +#include "furi_hal.h" + +/** + * Creates a PCAP file to store incoming packets. + * The file name will have a prefix according to the type of scan being performed by the application (Eg: raw_0.pcap) + * + * @param app Application context + */ +void wifi_marauder_create_pcap_file(WifiMarauderApp* app); \ No newline at end of file diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c index 228b0f83d..5ce6480f2 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_uart.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.c @@ -2,10 +2,12 @@ #include "wifi_marauder_uart.h" #define UART_CH (FuriHalUartIdUSART1) +#define LP_UART_CH (FuriHalUartIdLPUART1) #define BAUDRATE (115200) struct WifiMarauderUart { WifiMarauderApp* app; + FuriHalUartId channel; FuriThread* rx_thread; FuriStreamBuffer* rx_stream; uint8_t rx_buf[RX_BUF_SIZE + 1]; @@ -60,25 +62,42 @@ void wifi_marauder_uart_tx(uint8_t* data, size_t len) { furi_hal_uart_tx(UART_CH, data, len); } -WifiMarauderUart* wifi_marauder_uart_init(WifiMarauderApp* app) { +void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len) { + furi_hal_uart_tx(LP_UART_CH, data, len); +} + +WifiMarauderUart* + wifi_marauder_uart_init(WifiMarauderApp* app, FuriHalUartId channel, const char* thread_name) { WifiMarauderUart* uart = malloc(sizeof(WifiMarauderUart)); uart->app = app; + uart->channel = channel; uart->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1); uart->rx_thread = furi_thread_alloc(); - furi_thread_set_name(uart->rx_thread, "WifiMarauderUartRxThread"); + furi_thread_set_name(uart->rx_thread, thread_name); furi_thread_set_stack_size(uart->rx_thread, 1024); furi_thread_set_context(uart->rx_thread, uart); furi_thread_set_callback(uart->rx_thread, uart_worker); furi_thread_start(uart->rx_thread); - - furi_hal_console_disable(); - furi_hal_uart_set_br(UART_CH, BAUDRATE); - furi_hal_uart_set_irq_cb(UART_CH, wifi_marauder_uart_on_irq_cb, uart); + if(channel == FuriHalUartIdUSART1) { + furi_hal_console_disable(); + } else if(channel == FuriHalUartIdLPUART1) { + furi_hal_uart_init(channel, BAUDRATE); + } + furi_hal_uart_set_br(channel, BAUDRATE); + furi_hal_uart_set_irq_cb(channel, wifi_marauder_uart_on_irq_cb, uart); return uart; } +WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app) { + return wifi_marauder_uart_init(app, UART_CH, "WifiMarauderUartRxThread"); +} + +WifiMarauderUart* wifi_marauder_lp_uart_init(WifiMarauderApp* app) { + return wifi_marauder_uart_init(app, LP_UART_CH, "WifiMarauderLPUartRxThread"); +} + void wifi_marauder_uart_free(WifiMarauderUart* uart) { furi_assert(uart); @@ -86,7 +105,7 @@ void wifi_marauder_uart_free(WifiMarauderUart* uart) { furi_thread_join(uart->rx_thread); furi_thread_free(uart->rx_thread); - furi_hal_uart_set_irq_cb(UART_CH, NULL, NULL); + furi_hal_uart_set_irq_cb(uart->channel, NULL, NULL); furi_hal_console_enable(); free(uart); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_uart.h b/applications/external/wifi_marauder_companion/wifi_marauder_uart.h index e38c18dd2..4835d52ce 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_uart.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_uart.h @@ -10,5 +10,7 @@ void wifi_marauder_uart_set_handle_rx_data_cb( WifiMarauderUart* uart, void (*handle_rx_data_cb)(uint8_t* buf, size_t len, void* context)); void wifi_marauder_uart_tx(uint8_t* data, size_t len); -WifiMarauderUart* wifi_marauder_uart_init(WifiMarauderApp* app); +void wifi_marauder_lp_uart_tx(uint8_t* data, size_t len); +WifiMarauderUart* wifi_marauder_usart_init(WifiMarauderApp* app); +WifiMarauderUart* wifi_marauder_lp_uart_init(WifiMarauderApp* app); void wifi_marauder_uart_free(WifiMarauderUart* uart); From 968d6d578f0e7f9924599c378430742c87b20ce4 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Mar 2023 00:07:37 +0300 Subject: [PATCH 19/34] Add retry and delay to avoid power issues with Wifi plugins --- .../external/esp8266_deauth/esp8266_deauth.c | 17 ++++++++++++++--- .../external/wifi_scanner/wifi_scanner.c | 18 +++++++++++++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/applications/external/esp8266_deauth/esp8266_deauth.c b/applications/external/esp8266_deauth/esp8266_deauth.c index d32ca4c18..fc165b932 100644 --- a/applications/external/esp8266_deauth/esp8266_deauth.c +++ b/applications/external/esp8266_deauth/esp8266_deauth.c @@ -352,7 +352,12 @@ int32_t esp8266_deauth_app(void* p) { #else #if ENABLE_MODULE_POWER app->m_context = Initializing; - furi_hal_power_enable_otg(); + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + furi_delay_ms(200); #else app->m_context = ModuleActive; #endif @@ -409,7 +414,11 @@ int32_t esp8266_deauth_app(void* p) { app->m_wifiDeauthModuleAttached = true; #if ENABLE_MODULE_POWER app->m_context = Initializing; - furi_hal_power_enable_otg(); + uint8_t attempts2 = 0; + while(!furi_hal_power_is_otg_enabled() && attempts2++ < 3) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } #else app->m_context = ModuleActive; #endif @@ -533,7 +542,9 @@ int32_t esp8266_deauth_app(void* p) { DEAUTH_APP_LOG_I("App freed"); #if ENABLE_MODULE_POWER - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } #endif return 0; diff --git a/applications/external/wifi_scanner/wifi_scanner.c b/applications/external/wifi_scanner/wifi_scanner.c index 3a79ce16e..341287b54 100644 --- a/applications/external/wifi_scanner/wifi_scanner.c +++ b/applications/external/wifi_scanner/wifi_scanner.c @@ -663,7 +663,12 @@ int32_t wifi_scanner_app(void* p) { #else app->m_context = Initializing; #if ENABLE_MODULE_POWER - furi_hal_power_enable_otg(); + uint8_t attempts = 0; + while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + furi_delay_ms(200); #endif // ENABLE_MODULE_POWER #endif // ENABLE_MODULE_DETECTION @@ -722,7 +727,12 @@ int32_t wifi_scanner_app(void* p) { app->m_wifiModuleAttached = true; app->m_context = Initializing; #if ENABLE_MODULE_POWER - furi_hal_power_enable_otg(); + uint8_t attempts2 = 0; + while(!furi_hal_power_is_otg_enabled() && attempts2++ < 3) { + furi_hal_power_enable_otg(); + furi_delay_ms(10); + } + #endif } } @@ -851,7 +861,9 @@ int32_t wifi_scanner_app(void* p) { WIFI_APP_LOG_I("App freed"); #if ENABLE_MODULE_POWER - furi_hal_power_disable_otg(); + if(furi_hal_power_is_otg_enabled()) { + furi_hal_power_disable_otg(); + } #endif return 0; From 353adf4fb78e9b19a61a363d8616e7b7bd272977 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Mar 2023 00:24:53 +0300 Subject: [PATCH 20/34] Update changelog and readme --- CHANGELOG.md | 11 ++++++++++- ReadMe.md | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2700b486..0b76ede7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,19 @@ ### New changes * SubGHz: New protocols support: CAME Space, Stilmatic / Schellenberg * SubGHz: Add Manually - new protocols -> Beninca, Sommer(FSK), IronLogic, DTM Neo, Gibidi, Elmes Electronic (Elmes Poland), CAME Space -* SubGHz: Fixed external module power issues, added more checks, fixed issues when launching subghz favourites +* SubGHz: Fixed external CC1101 module power issues, added more checks, fixed issues when launching subghz favourites * SubGHz: Removed 330MHz from default freq hopper to make it faster, to change freq hopper settings and remove/add your freqs see -> [Instruction](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md) +* Plugins: Added delay and retries to avoid power issues in plugins -> WiFi Scanner, ESP8266 Deauther +* Plugins: Update for WiFi Marauder companion -> `sniff` saves pcaps in `YourFlippersMicroSD/apps_data/marauder/` -> Only with custom marauder build (It is necessary to uncomment "#define WRITE_PACKETS_SERIAL" in configs.h (in marauder fw) and compile the firmware for the wifi board.) Or download precompiled build -> [Download .bin](https://github.com/tcpassos/ESP32Marauder/releases/download/0.10.1/esp32_marauder_no_sd_20230301.bin) - [(by tcpassos)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/7) +* Plugins: Removed `cdefines` from external plugins as it was not used in any way * Plugins: Updated **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (fixed issue #379) * Plugins: Update for SubGHz Bruteforcer, added - Holtek HT12X 12bit AM 433.920MHz/315MHz/868MHz/915MHz (TE: 433us) +* OFW: gh: use shallow clones whenever possible +* OFW: Add new nfc apdu cli command +* OFW: Picopass standard KDF dictionary +* OFW: Nfc: fixes for latest PVS-studio 7.23 +* OFW: Dolphin: new spring animation, weight adjust, drop winter animation. +* OFW: fbt, faploader: minimal app module implementation -> **All plugins now should have** `apptype=FlipperAppType.EXTERNAL` * OFW: Fbt: fix broken resource deployment * OFW: NFC: Fix 0 block write possibility in Mifare Classic emulation * OFW: BadUSB: updated parser and added stringln, hold and release diff --git a/ReadMe.md b/ReadMe.md index 8d48adf5a..0248535a2 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -135,7 +135,7 @@ You can support us by using links or addresses below: - WAV Player [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) - Fixed and improved by [LTVA1](https://github.com/LTVA1/wav_player) - Barcode generator plugin [(original by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) - [EAN-8 and refactoring](https://github.com/DarkFlippers/unleashed-firmware/pull/154) by @msvsergey - GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin) -- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) - Saving pcap on flipper microSD [by tcpassos](https://github.com/tcpassos/flipperzero-firmware-with-wifi-marauder-companion) +- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) - Saving .pcap on flipper microSD [by tcpassos](https://github.com/tcpassos/flipperzero-firmware-with-wifi-marauder-companion) -> Only with custom marauder build (It is necessary to uncomment "#define WRITE_PACKETS_SERIAL" in configs.h (in marauder fw) and compile the firmware for the wifi board.) Or download precompiled build -> [Download .bin](https://github.com/tcpassos/ESP32Marauder/releases/download/0.10.1/esp32_marauder_no_sd_20230301.bin) - NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker) - Simple Clock (timer by GMMan) [(original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61) - **Sub-GHz Remote** (UniversalRF Remix) [(by @darmiel & @xMasterX)](https://github.com/darmiel/flipper-playlist/tree/feat/unirf-protocols) (original by @ESurge) From 9fbf3270286beff340d8cfc67a219606237ad7d6 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:28:50 +0200 Subject: [PATCH 21/34] [FL-1799] Require the trailing slash for root paths (#2486) * Require the trailing slash * Fix the swapped storages * Fix root paths --- .../services/storage/storage_processing.c | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index 2a335e366..59527e769 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -33,12 +33,22 @@ static StorageType storage_get_type_by_path(FuriString* path) { StorageType type = ST_ERROR; const char* path_cstr = furi_string_get_cstr(path); - if(memcmp(path_cstr, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { - type = ST_EXT; - } else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { - type = ST_INT; - } else if(memcmp(path_cstr, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { - type = ST_ANY; + if(furi_string_size(path) == 4) { + if(memcmp(path_cstr, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { + type = ST_EXT; + } else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { + type = ST_INT; + } else if(memcmp(path_cstr, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { + type = ST_ANY; + } + } else if(furi_string_size(path) > 4) { + if(memcmp(path_cstr, EXT_PATH(""), strlen(EXT_PATH(""))) == 0) { + type = ST_EXT; + } else if(memcmp(path_cstr, INT_PATH(""), strlen(INT_PATH(""))) == 0) { + type = ST_INT; + } else if(memcmp(path_cstr, ANY_PATH(""), strlen(ANY_PATH(""))) == 0) { + type = ST_ANY; + } } return type; From e90042368f941f2c167563444fdb5cf22a5da9ad Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:58:07 +0200 Subject: [PATCH 22/34] [FL-3156] Mark keys as not found when they couldn't auth successfully (#2476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Mark keys as not found when they couldn't auth successfully * Improve logging and fix the reading bug Co-authored-by: あく --- lib/nfc/nfc_worker.c | 13 +++++++++++-- lib/nfc/protocols/mifare_classic.c | 26 ++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 062a39534..4561ff2af 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -739,7 +739,7 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyA, !deactivated, cuid)) { mf_classic_set_key_found(data, i, MfClassicKeyA, key); - FURI_LOG_D(TAG, "Key found"); + FURI_LOG_D(TAG, "Key A found"); nfc_worker->callback(NfcWorkerEventFoundKeyA, nfc_worker->context); uint64_t found_key; @@ -753,22 +753,31 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { } nfc_worker_mf_classic_key_attack(nfc_worker, found_key, &tx_rx, i + 1); + break; } nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); } furi_hal_nfc_sleep(); deactivated = true; + } else { + mf_classic_set_key_not_found(data, i, MfClassicKeyA); + is_key_a_found = false; + FURI_LOG_D(TAG, "Key %dA not found in attack", i); } if(!is_key_b_found) { is_key_b_found = mf_classic_is_key_found(data, i, MfClassicKeyB); if(mf_classic_authenticate_skip_activate( &tx_rx, block_num, key, MfClassicKeyB, !deactivated, cuid)) { - FURI_LOG_D(TAG, "Key found"); + FURI_LOG_D(TAG, "Key B found"); mf_classic_set_key_found(data, i, MfClassicKeyB, key); nfc_worker->callback(NfcWorkerEventFoundKeyB, nfc_worker->context); nfc_worker_mf_classic_key_attack(nfc_worker, key, &tx_rx, i + 1); } deactivated = true; + } else { + mf_classic_set_key_not_found(data, i, MfClassicKeyB); + is_key_b_found = false; + FURI_LOG_D(TAG, "Key %dB not found in attack", i); } if(is_key_a_found && is_key_b_found) break; if(nfc_worker->state != NfcWorkerStateMfClassicDictAttack) break; diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 712428717..d2d7467dc 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -651,7 +651,12 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u if(!key_a_found) break; FURI_LOG_D(TAG, "Try to read blocks with key A"); key = nfc_util_bytes2num(sec_tr->key_a, sizeof(sec_tr->key_a)); - if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto, false, 0)) break; + if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyA, &crypto, false, 0)) { + mf_classic_set_key_not_found(data, sec_num, MfClassicKeyA); + FURI_LOG_D(TAG, "Key %dA not found in read", sec_num); + break; + } + for(size_t i = start_block; i < start_block + total_blocks; i++) { if(!mf_classic_is_block_read(data, i)) { if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { @@ -660,7 +665,11 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u } else if(i > start_block) { // Try to re-auth to read block in case prevous block was protected from read furi_hal_nfc_sleep(); - if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyA, &crypto, false, 0)) break; + if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyA, &crypto, false, 0)) { + mf_classic_set_key_not_found(data, sec_num, MfClassicKeyA); + FURI_LOG_D(TAG, "Key %dA not found in read", sec_num); + break; + } if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { mf_classic_set_block_read(data, i, &block_tmp); blocks_read++; @@ -680,7 +689,12 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u } FURI_LOG_D(TAG, "Try to read blocks with key B"); key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b)); - if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto, false, 0)) break; + if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto, false, 0)) { + mf_classic_set_key_not_found(data, sec_num, MfClassicKeyB); + FURI_LOG_D(TAG, "Key %dB not found in read", sec_num); + break; + } + for(size_t i = start_block; i < start_block + total_blocks; i++) { if(!mf_classic_is_block_read(data, i)) { if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { @@ -689,7 +703,11 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u } else if(i > start_block) { // Try to re-auth to read block in case prevous block was protected from read furi_hal_nfc_sleep(); - if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyB, &crypto, false, 0)) break; + if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyB, &crypto, false, 0)) { + mf_classic_set_key_not_found(data, sec_num, MfClassicKeyB); + FURI_LOG_D(TAG, "Key %dB not found in read", sec_num); + break; + } if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { mf_classic_set_block_read(data, i, &block_tmp); blocks_read++; From 6aa0c08f3a47ea9750f2d28c4386fec61c55efd8 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Thu, 16 Mar 2023 11:06:11 +0200 Subject: [PATCH 23/34] [FL-3064] Skip the read when the card is not present (#2494) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c | 3 ++- applications/main/nfc/views/dict_attack.c | 8 ++++++++ applications/main/nfc/views/dict_attack.h | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c index b82bf5521..cb2f3a82d 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_dict_attack.c @@ -115,7 +115,8 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent consumed = true; } } else if(event.event == NfcWorkerEventAborted) { - if(state == DictAttackStateUserDictInProgress) { + if(state == DictAttackStateUserDictInProgress && + dict_attack_get_card_state(nfc->dict_attack)) { nfc_scene_mf_classic_dict_attack_prepare_view(nfc, state); consumed = true; } else { diff --git a/applications/main/nfc/views/dict_attack.c b/applications/main/nfc/views/dict_attack.c index a539e514b..8f4bd063e 100644 --- a/applications/main/nfc/views/dict_attack.c +++ b/applications/main/nfc/views/dict_attack.c @@ -11,6 +11,7 @@ struct DictAttack { View* view; DictAttackCallback callback; void* context; + bool card_present; }; typedef struct { @@ -162,6 +163,7 @@ void dict_attack_set_header(DictAttack* dict_attack, const char* header) { void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) { furi_assert(dict_attack); + dict_attack->card_present = true; with_view_model( dict_attack->view, DictAttackViewModel * model, @@ -175,6 +177,7 @@ void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type) void dict_attack_set_card_removed(DictAttack* dict_attack) { furi_assert(dict_attack); + dict_attack->card_present = false; with_view_model( dict_attack->view, DictAttackViewModel * model, @@ -182,6 +185,11 @@ void dict_attack_set_card_removed(DictAttack* dict_attack) { true); } +bool dict_attack_get_card_state(DictAttack* dict_attack) { + furi_assert(dict_attack); + return dict_attack->card_present; +} + void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { furi_assert(dict_attack); with_view_model( diff --git a/applications/main/nfc/views/dict_attack.h b/applications/main/nfc/views/dict_attack.h index 2839534a7..73b98a1b8 100644 --- a/applications/main/nfc/views/dict_attack.h +++ b/applications/main/nfc/views/dict_attack.h @@ -25,6 +25,8 @@ void dict_attack_set_card_detected(DictAttack* dict_attack, MfClassicType type); void dict_attack_set_card_removed(DictAttack* dict_attack); +bool dict_attack_get_card_state(DictAttack* dict_attack); + void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); From bb9ac1389ba98120358f72dd4f9f68b6e3845786 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:02:23 +0300 Subject: [PATCH 24/34] Remember last state for CC1101 external + other fixes Reset to internal module if external is not found on all apps, not only in bruteforcer --- applications/external/playlist/playlist.c | 5 + .../external/pocsag_pager/pocsag_pager_app.c | 5 + applications/external/protoview/app.c | 5 + .../spectrum_analyzer/spectrum_analyzer.c | 5 + applications/external/subbrute | 2 +- .../weather_station/weather_station_app.c | 5 + .../scenes/subghz_scene_ext_module_settings.c | 5 +- .../main/subghz/scenes/subghz_scene_start.c | 1 + applications/main/subghz/subghz.c | 105 +++++++++--------- .../main/subghz/subghz_last_settings.c | 23 ++++ .../main/subghz/subghz_last_settings.h | 1 + .../main/subghz_remote/subghz_remote_app.c | 5 + 12 files changed, 114 insertions(+), 53 deletions(-) diff --git a/applications/external/playlist/playlist.c b/applications/external/playlist/playlist.c index b32ed2178..263b296e5 100644 --- a/applications/external/playlist/playlist.c +++ b/applications/external/playlist/playlist.c @@ -713,6 +713,11 @@ int32_t playlist_app(void* p) { // Enable power for External CC1101 if it is connected furi_hal_subghz_enable_ext_power(); + // Auto switch to internal radio if external radio is not available + furi_delay_ms(15); + if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + } furi_hal_power_suppress_charge_enter(); diff --git a/applications/external/pocsag_pager/pocsag_pager_app.c b/applications/external/pocsag_pager/pocsag_pager_app.c index 123b3ee9d..f5e989fba 100644 --- a/applications/external/pocsag_pager/pocsag_pager_app.c +++ b/applications/external/pocsag_pager/pocsag_pager_app.c @@ -124,6 +124,11 @@ POCSAGPagerApp* pocsag_pager_app_alloc() { // Enable power for External CC1101 if it is connected furi_hal_subghz_enable_ext_power(); + // Auto switch to internal radio if external radio is not available + furi_delay_ms(15); + if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + } furi_hal_power_suppress_charge_enter(); diff --git a/applications/external/protoview/app.c b/applications/external/protoview/app.c index a4dab9f40..678c9d75f 100644 --- a/applications/external/protoview/app.c +++ b/applications/external/protoview/app.c @@ -169,6 +169,11 @@ ProtoViewApp* protoview_app_alloc() { // Enable power for External CC1101 if it is connected furi_hal_subghz_enable_ext_power(); + // Auto switch to internal radio if external radio is not available + furi_delay_ms(15); + if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + } furi_hal_power_suppress_charge_enter(); app->running = 1; diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer.c b/applications/external/spectrum_analyzer/spectrum_analyzer.c index 99c12adf7..d38c6bf23 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer.c +++ b/applications/external/spectrum_analyzer/spectrum_analyzer.c @@ -405,6 +405,11 @@ int32_t spectrum_analyzer_app(void* p) { // Enable power for External CC1101 if it is connected furi_hal_subghz_enable_ext_power(); + // Auto switch to internal radio if external radio is not available + furi_delay_ms(15); + if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + } furi_hal_power_suppress_charge_enter(); diff --git a/applications/external/subbrute b/applications/external/subbrute index 685ac6540..f9a4508e5 160000 --- a/applications/external/subbrute +++ b/applications/external/subbrute @@ -1 +1 @@ -Subproject commit 685ac6540de1bf5f7f4bd267c808850cf539587e +Subproject commit f9a4508e508393ce064179bc83b09e4516843400 diff --git a/applications/external/weather_station/weather_station_app.c b/applications/external/weather_station/weather_station_app.c index ffa569f20..a3135a6b0 100644 --- a/applications/external/weather_station/weather_station_app.c +++ b/applications/external/weather_station/weather_station_app.c @@ -107,6 +107,11 @@ WeatherStationApp* weather_station_app_alloc() { // Enable power for External CC1101 if it is connected furi_hal_subghz_enable_ext_power(); + // Auto switch to internal radio if external radio is not available + furi_delay_ms(15); + if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + } furi_hal_power_suppress_charge_enter(); diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c index 163099e69..053a5df8e 100644 --- a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c @@ -37,10 +37,13 @@ const char* const debug_counter_text[DEBUG_COUNTER_COUNT] = { static void subghz_scene_ext_module_changed(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); value_index_exm = variable_item_get_current_value_index(item); - UNUSED(subghz); variable_item_set_current_value_text(item, radio_modules_variables_text[value_index_exm]); + + subghz->last_settings->external_module_enabled = value_index_exm == 1; + subghz_last_settings_save(subghz->last_settings); } + static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) { SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, index); diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index f23e2d0cb..eb14315a8 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -85,6 +85,7 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { if(!furi_hal_subghz_check_radio()) { furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + subghz->last_settings->external_module_enabled = false; furi_string_set(subghz->error_str, "Please connect\nexternal radio"); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); return true; diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 3ca66374b..27a5ce634 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -75,6 +75,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { // Call enable power for external module furi_hal_subghz_enable_ext_power(); + furi_delay_ms(15); // View Dispatcher subghz->view_dispatcher = view_dispatcher_alloc(); @@ -189,66 +190,66 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user")); // Custom Presets load without using config file + if(!alloc_for_tx_only) { + FlipperFormat* temp_fm_preset = flipper_format_string_alloc(); + flipper_format_write_string_cstr( + temp_fm_preset, + (const char*)"Custom_preset_data", + (const char*)"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"); + flipper_format_rewind(temp_fm_preset); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"FM95", temp_fm_preset); - FlipperFormat* temp_fm_preset = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset, - (const char*)"Custom_preset_data", - (const char*)"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"); - flipper_format_rewind(temp_fm_preset); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"FM95", temp_fm_preset); + flipper_format_free(temp_fm_preset); - flipper_format_free(temp_fm_preset); + // #2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping + FlipperFormat* temp_fm_preset2 = flipper_format_string_alloc(); + flipper_format_write_string_cstr( + temp_fm_preset2, + (const char*)"Custom_preset_data", + (const char*)"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"); + flipper_format_rewind(temp_fm_preset2); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"FM15k", temp_fm_preset2); - // #2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping - FlipperFormat* temp_fm_preset2 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset2, - (const char*)"Custom_preset_data", - (const char*)"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"); - flipper_format_rewind(temp_fm_preset2); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"FM15k", temp_fm_preset2); + flipper_format_free(temp_fm_preset2); - flipper_format_free(temp_fm_preset2); + // Pagers + FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc(); + flipper_format_write_string_cstr( + temp_fm_preset3, + (const char*)"Custom_preset_data", + (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*)"Pagers", temp_fm_preset3); - // Pagers - FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset3, - (const char*)"Custom_preset_data", - (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*)"Pagers", temp_fm_preset3); + flipper_format_free(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 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_1", temp_fm_preset4); - // # 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 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_1", temp_fm_preset4); + flipper_format_free(temp_fm_preset4); - flipper_format_free(temp_fm_preset4); - - 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*)"HND_2", temp_fm_preset5); - - flipper_format_free(temp_fm_preset5); + 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*)"HND_2", temp_fm_preset5); + flipper_format_free(temp_fm_preset5); + } // custom presets loading - end // Load last used values for Read, Read RAW, etc. or default + subghz->last_settings = subghz_last_settings_alloc(); + subghz_last_settings_load(subghz->last_settings, 0); if(!alloc_for_tx_only) { - subghz->last_settings = subghz_last_settings_alloc(); - subghz_last_settings_load(subghz->last_settings, 0); #if FURI_DEBUG FURI_LOG_D( TAG, @@ -450,14 +451,16 @@ int32_t subghz_app(void* p) { subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes")); subghz_environment_load_keystore( subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); + + // Auto switch to internal radio if external radio is not available + if(!furi_hal_subghz_check_radio()) { + subghz->last_settings->external_module_enabled = false; + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + } // Check argument and run corresponding scene if(p && strlen(p)) { uint32_t rpc_ctx = 0; - if(!furi_hal_subghz_check_radio()) { - furi_hal_subghz_set_radio_type(SubGhzRadioInternal); - } - if(sscanf(p, "RPC %lX", &rpc_ctx) == 1) { subghz->rpc_ctx = (void*)rpc_ctx; rpc_system_app_set_callback(subghz->rpc_ctx, subghz_rpc_command_callback, subghz); diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 8e7016df7..392070dcd 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -17,6 +17,7 @@ //#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset" #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL "FeedbackLevel" #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" +#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED "External" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -41,6 +42,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count uint32_t temp_frequency = 0; uint32_t temp_frequency_analyzer_feedback_level = 0; float temp_frequency_analyzer_trigger = 0; + bool temp_external_module_enabled = false; //int32_t temp_preset = 0; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; @@ -62,6 +64,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER, (float*)&temp_frequency_analyzer_trigger, 1); + flipper_format_read_bool( + fff_data_file, + SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED, + (bool*)&temp_external_module_enabled, + 1); } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); @@ -74,6 +81,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->frequency_analyzer_feedback_level = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; + instance->external_module_enabled = false; } else { instance->frequency = temp_frequency; @@ -89,6 +97,14 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count /*if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) { FURI_LOG_W(TAG, "Last used preset no found");*/ instance->preset = SUBGHZ_LAST_SETTING_DEFAULT_PRESET; + + instance->external_module_enabled = temp_external_module_enabled; + + // Set selected radio module + if(instance->external_module_enabled) { + furi_hal_subghz_set_radio_type(SubGhzRadioExternal); + } + /*/} else { instance->preset = temp_preset; }*/ @@ -145,6 +161,13 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { 1)) { break; } + if(!flipper_format_insert_or_update_bool( + file, + SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED, + &instance->external_module_enabled, + 1)) { + break; + } saved = true; } while(0); diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index f08d99c81..d5365198a 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -10,6 +10,7 @@ typedef struct { int32_t preset; uint32_t frequency_analyzer_feedback_level; float frequency_analyzer_trigger; + bool external_module_enabled; } SubGhzLastSettings; SubGhzLastSettings* subghz_last_settings_alloc(void); diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index b64c87002..90aa03c9a 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -738,6 +738,11 @@ SubGHzRemote* subghz_remote_alloc(void) { // Enable power for External CC1101 if it is connected furi_hal_subghz_enable_ext_power(); + // Auto switch to internal radio if external radio is not available + furi_delay_ms(15); + if(!furi_hal_subghz_check_radio()) { + furi_hal_subghz_set_radio_type(SubGhzRadioInternal); + } furi_hal_power_suppress_charge_enter(); From 5de507b0c835c592106f765a688139d54890687c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Mar 2023 15:19:51 +0300 Subject: [PATCH 25/34] Update changelog and readme --- CHANGELOG.md | 7 ++++++- ReadMe.md | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b76ede7d..2ed16d578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,18 @@ ### New changes * SubGHz: New protocols support: CAME Space, Stilmatic / Schellenberg * SubGHz: Add Manually - new protocols -> Beninca, Sommer(FSK), IronLogic, DTM Neo, Gibidi, Elmes Electronic (Elmes Poland), CAME Space +* SubGHz: Remember last external module setting, so if you turn off flipper it will remember last external module setting (only for subghz app) +* SubGHz: Fix issues when external module is not found but plugins tries to use it, now they will fallback to internal in that case * SubGHz: Fixed external CC1101 module power issues, added more checks, fixed issues when launching subghz favourites * SubGHz: Removed 330MHz from default freq hopper to make it faster, to change freq hopper settings and remove/add your freqs see -> [Instruction](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md) * Plugins: Added delay and retries to avoid power issues in plugins -> WiFi Scanner, ESP8266 Deauther -* Plugins: Update for WiFi Marauder companion -> `sniff` saves pcaps in `YourFlippersMicroSD/apps_data/marauder/` -> Only with custom marauder build (It is necessary to uncomment "#define WRITE_PACKETS_SERIAL" in configs.h (in marauder fw) and compile the firmware for the wifi board.) Or download precompiled build -> [Download .bin](https://github.com/tcpassos/ESP32Marauder/releases/download/0.10.1/esp32_marauder_no_sd_20230301.bin) - [(by tcpassos)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/7) +* Plugins: Update for WiFi Marauder companion -> `sniff` saves pcaps in `YourFlippersMicroSD/apps_data/marauder/` -> Only with custom marauder build (It is necessary to uncomment "#define WRITE_PACKETS_SERIAL" in configs.h (in marauder fw) and compile the firmware for the wifi board.) Or download precompiled build -> [Download esp32_marauder_ver_flipper_sd_serial.bin](https://github.com/justcallmekoko/ESP32Marauder/releases/latest) - [(by tcpassos)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/7) * Plugins: Removed `cdefines` from external plugins as it was not used in any way * Plugins: Updated **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (fixed issue #379) * Plugins: Update for SubGHz Bruteforcer, added - Holtek HT12X 12bit AM 433.920MHz/315MHz/868MHz/915MHz (TE: 433us) +* OFW: NFC -> Skip the read when the card is not present +* OFW: NFC -> Mark keys as not found when they couldn't auth successfully +* OFW: Storage -> Require the trailing slash for root paths * OFW: gh: use shallow clones whenever possible * OFW: Add new nfc apdu cli command * OFW: Picopass standard KDF dictionary diff --git a/ReadMe.md b/ReadMe.md index 0248535a2..13edfeb64 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -135,7 +135,7 @@ You can support us by using links or addresses below: - WAV Player [(OFW: DrZlo13)](https://github.com/flipperdevices/flipperzero-firmware/tree/zlo/wav-player) - Fixed and improved by [LTVA1](https://github.com/LTVA1/wav_player) - Barcode generator plugin [(original by McAzzaMan)](https://github.com/McAzzaMan/flipperzero-firmware/tree/UPC-A_Barcode_Generator/applications/barcode_generator) - [EAN-8 and refactoring](https://github.com/DarkFlippers/unleashed-firmware/pull/154) by @msvsergey - GPIO: Sentry Safe plugin [(by H4ckd4ddy)](https://github.com/H4ckd4ddy/flipperzero-sentry-safe-plugin) -- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) - Saving .pcap on flipper microSD [by tcpassos](https://github.com/tcpassos/flipperzero-firmware-with-wifi-marauder-companion) -> Only with custom marauder build (It is necessary to uncomment "#define WRITE_PACKETS_SERIAL" in configs.h (in marauder fw) and compile the firmware for the wifi board.) Or download precompiled build -> [Download .bin](https://github.com/tcpassos/ESP32Marauder/releases/download/0.10.1/esp32_marauder_no_sd_20230301.bin) +- ESP32: WiFi Marauder companion plugin [(by 0xchocolate)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion) - Saving .pcap on flipper microSD [by tcpassos](https://github.com/tcpassos/flipperzero-firmware-with-wifi-marauder-companion) -> Only with custom marauder build (It is necessary to uncomment "#define WRITE_PACKETS_SERIAL" in configs.h (in marauder fw) and compile the firmware for the wifi board.) Or download precompiled build -> [Download esp32_marauder_ver_flipper_sd_serial.bin](https://github.com/justcallmekoko/ESP32Marauder/releases/latest) - NRF24: Sniffer & MouseJacker (with changes) [(by mothball187)](https://github.com/mothball187/flipperzero-nrf24/tree/main/mousejacker) - Simple Clock (timer by GMMan) [(original by CompaqDisc)](https://gist.github.com/CompaqDisc/4e329c501bd03c1e801849b81f48ea61) - **Sub-GHz Remote** (UniversalRF Remix) [(by @darmiel & @xMasterX)](https://github.com/darmiel/flipper-playlist/tree/feat/unirf-protocols) (original by @ESurge) From 3c3ac216dc103ca73ea4a594954bd2d488f5ed01 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Mar 2023 16:03:22 +0300 Subject: [PATCH 26/34] Remember if 5v was turned off, save its state --- CHANGELOG.md | 2 +- .../scenes/subghz_scene_ext_module_settings.c | 4 ++++ .../main/subghz/subghz_last_settings.c | 21 +++++++++++++++++++ .../main/subghz/subghz_last_settings.h | 1 + 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ed16d578..71957562d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ### New changes * SubGHz: New protocols support: CAME Space, Stilmatic / Schellenberg * SubGHz: Add Manually - new protocols -> Beninca, Sommer(FSK), IronLogic, DTM Neo, Gibidi, Elmes Electronic (Elmes Poland), CAME Space -* SubGHz: Remember last external module setting, so if you turn off flipper it will remember last external module setting (only for subghz app) +* SubGHz: Remember last external module setting and power setting, so if you turn off flipper it will remember last external module settings (only for subghz app) * SubGHz: Fix issues when external module is not found but plugins tries to use it, now they will fallback to internal in that case * SubGHz: Fixed external CC1101 module power issues, added more checks, fixed issues when launching subghz favourites * SubGHz: Removed 330MHz from default freq hopper to make it faster, to change freq hopper settings and remove/add your freqs see -> [Instruction](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md) diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c index 053a5df8e..a96208074 100644 --- a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c +++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c @@ -88,6 +88,7 @@ static void subghz_scene_receiver_config_set_debug_counter(VariableItem* item) { } static void subghz_scene_receiver_config_set_ext_mod_power(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, ext_mod_power_text[index]); @@ -98,6 +99,9 @@ static void subghz_scene_receiver_config_set_ext_mod_power(VariableItem* item) { } else { furi_hal_subghz_enable_ext_power(); } + + subghz->last_settings->external_module_power_5v_disable = index == 1; + subghz_last_settings_save(subghz->last_settings); } void subghz_scene_ext_module_settings_on_enter(void* context) { diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 392070dcd..6fc51554d 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -18,6 +18,7 @@ #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL "FeedbackLevel" #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" #define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED "External" +#define SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER "ExtPower" SubGhzLastSettings* subghz_last_settings_alloc(void) { SubGhzLastSettings* instance = malloc(sizeof(SubGhzLastSettings)); @@ -43,6 +44,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count uint32_t temp_frequency_analyzer_feedback_level = 0; float temp_frequency_analyzer_trigger = 0; bool temp_external_module_enabled = false; + bool temp_external_module_power_5v_disable = false; //int32_t temp_preset = 0; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; @@ -69,6 +71,11 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_ENABLED, (bool*)&temp_external_module_enabled, 1); + flipper_format_read_bool( + fff_data_file, + SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER, + (bool*)&temp_external_module_power_5v_disable, + 1); } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); @@ -100,6 +107,13 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->external_module_enabled = temp_external_module_enabled; + instance->external_module_power_5v_disable = temp_external_module_power_5v_disable; + + if(instance->external_module_power_5v_disable) { + furi_hal_subghz_set_external_power_disable(true); + furi_hal_subghz_disable_ext_power(); + } + // Set selected radio module if(instance->external_module_enabled) { furi_hal_subghz_set_radio_type(SubGhzRadioExternal); @@ -168,6 +182,13 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { 1)) { break; } + if(!flipper_format_insert_or_update_bool( + file, + SUBGHZ_LAST_SETTING_FIELD_EXTERNAL_MODULE_POWER, + &instance->external_module_power_5v_disable, + 1)) { + break; + } saved = true; } while(0); diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index d5365198a..5e3630468 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -11,6 +11,7 @@ typedef struct { uint32_t frequency_analyzer_feedback_level; float frequency_analyzer_trigger; bool external_module_enabled; + bool external_module_power_5v_disable; } SubGhzLastSettings; SubGhzLastSettings* subghz_last_settings_alloc(void); From 771c47f809eb1e25c2bb3f55ff40780c3709e388 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 16 Mar 2023 17:46:18 +0400 Subject: [PATCH 27/34] fbt: explicitly set dist suffix length, not depending on environment settings. See (#2497) --- scripts/version.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/version.py b/scripts/version.py index 896b58a46..880a97281 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -9,11 +9,16 @@ from datetime import date, datetime class GitVersion: + REVISION_SUFFIX_LENGTH = 8 + def __init__(self, source_dir): self.source_dir = source_dir def get_version_info(self): - commit = self._exec_git("rev-parse --short HEAD") or "unknown" + commit = ( + self._exec_git(f"rev-parse --short={self.REVISION_SUFFIX_LENGTH} HEAD") + or "unknown" + ) dirty = False try: From 25fd3c3400ecaa0e3b66771a895844de8ddd52f9 Mon Sep 17 00:00:00 2001 From: Guido Giorgi Date: Thu, 16 Mar 2023 15:12:43 +0100 Subject: [PATCH 28/34] iButton: Add support for Dallas DS1971 v2 (#2492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * iButton: Add DS1971 Support refactored for v2 * Fix requested by gsurkov * Fix DALLAS_COMMON_CMD_* use, cusotm data field to Eeprom and COPY_SCRAPTCHPAD * Fix Emulation + Memory Info + Docs * Fix docs, strings, refactor code Co-authored-by: Georgii Surkov Co-authored-by: あく --- .../file_formats/iButtonFileFormat.md | 13 +- .../ibutton/protocols/dallas/dallas_common.c | 27 +- .../ibutton/protocols/dallas/dallas_common.h | 5 +- .../protocols/dallas/protocol_ds1971.c | 270 ++++++++++++++++++ .../protocols/dallas/protocol_ds1971.h | 5 + .../protocols/dallas/protocol_ds1992.c | 3 +- .../protocols/dallas/protocol_ds1996.c | 3 +- .../dallas/protocol_group_dallas_defs.c | 2 + .../dallas/protocol_group_dallas_defs.h | 1 + 9 files changed, 311 insertions(+), 18 deletions(-) create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds1971.c create mode 100644 lib/one_wire/ibutton/protocols/dallas/protocol_ds1971.h diff --git a/documentation/file_formats/iButtonFileFormat.md b/documentation/file_formats/iButtonFileFormat.md index adb493e05..d31c297a2 100644 --- a/documentation/file_formats/iButtonFileFormat.md +++ b/documentation/file_formats/iButtonFileFormat.md @@ -24,12 +24,13 @@ Changelog: #### Format fields -| Name | Type | Description | -| --------- | ------ | -------------------------------------------- | -| Protocol | string | Currently supported: DS1990, DS1992, DS1996, DSGeneric*, Cyfral, Metakom | -| Rom Data | hex | Read-only memory data (Dallas protocols only) | -| Sram Data | hex | Static RAM data (DS1992 and DS1996 only) -| Data | hex | Key data (Cyfral & Metakom only) | +| Name | Type | Description | +| ----------- | ------ | -------------------------------------------- | +| Protocol | string | Currently supported: DS1990, DS1992, DS1996, DS1997, DSGeneric*, Cyfral, Metakom | +| Rom Data | hex | Read-only memory data (Dallas protocols only) | +| Sram Data | hex | Static RAM data (DS1992 and DS1996 only) +| Eeprom Data | hex | EEPROM data (DS1971 only) +| Data | hex | Key data (Cyfral & Metakom only) | NOTE 1: DSGeneric is a catch-all protocol for all unknown 1-Wire devices. It reads only the ROM and does not perform any checks on the read data. It can also be used if a key with a deliberately invalid family code or checksum is required. diff --git a/lib/one_wire/ibutton/protocols/dallas/dallas_common.c b/lib/one_wire/ibutton/protocols/dallas/dallas_common.c index 57a873b1d..22b25db84 100644 --- a/lib/one_wire/ibutton/protocols/dallas/dallas_common.c +++ b/lib/one_wire/ibutton/protocols/dallas/dallas_common.c @@ -21,6 +21,7 @@ #define BITS_IN_BYTE 8U #define BITS_IN_KBIT 1024U +#define BITS_IN_MBIT (BITS_IN_KBIT * 1024U) bool dallas_common_skip_rom(OneWireHost* host) { onewire_host_write(host, DALLAS_COMMON_CMD_SKIP_ROM); @@ -210,25 +211,35 @@ bool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data) { void dallas_common_render_brief_data( FuriString* result, const DallasCommonRomData* rom_data, - const uint8_t* sram_data, - size_t sram_data_size) { + const uint8_t* mem_data, + size_t mem_size, + const char* mem_name) { for(size_t i = 0; i < sizeof(rom_data->bytes); ++i) { furi_string_cat_printf(result, "%02X ", rom_data->bytes[i]); } + const char* size_prefix = ""; + size_t mem_size_bits = mem_size * BITS_IN_BYTE; + + if(mem_size_bits >= BITS_IN_MBIT) { + size_prefix = "M"; + mem_size_bits /= BITS_IN_MBIT; + } else if(mem_size_bits >= BITS_IN_KBIT) { + size_prefix = "K"; + mem_size_bits /= BITS_IN_KBIT; + } + furi_string_cat_printf( - result, - "\nInternal SRAM: %zu Kbit\n", - (size_t)(sram_data_size * BITS_IN_BYTE / BITS_IN_KBIT)); + result, "\nInternal %s: %zu %sbit\n", mem_name, mem_size_bits, size_prefix); for(size_t i = 0; i < DALLAS_COMMON_BRIEF_HEAD_COUNT; ++i) { - furi_string_cat_printf(result, "%02X ", sram_data[i]); + furi_string_cat_printf(result, "%02X ", mem_data[i]); } furi_string_cat_printf(result, "[ . . . ]"); - for(size_t i = sram_data_size - DALLAS_COMMON_BRIEF_TAIL_COUNT; i < sram_data_size; ++i) { - furi_string_cat_printf(result, " %02X", sram_data[i]); + for(size_t i = mem_size - DALLAS_COMMON_BRIEF_TAIL_COUNT; i < mem_size; ++i) { + furi_string_cat_printf(result, " %02X", mem_data[i]); } } diff --git a/lib/one_wire/ibutton/protocols/dallas/dallas_common.h b/lib/one_wire/ibutton/protocols/dallas/dallas_common.h index 7ad13eb2c..7991a1f8b 100644 --- a/lib/one_wire/ibutton/protocols/dallas/dallas_common.h +++ b/lib/one_wire/ibutton/protocols/dallas/dallas_common.h @@ -99,8 +99,9 @@ bool dallas_common_is_valid_crc(const DallasCommonRomData* rom_data); void dallas_common_render_brief_data( FuriString* result, const DallasCommonRomData* rom_data, - const uint8_t* sram_data, - size_t sram_data_size); + const uint8_t* mem_data, + size_t mem_size, + const char* mem_name); void dallas_common_render_crc_error(FuriString* result, const DallasCommonRomData* rom_data); diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1971.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1971.c new file mode 100644 index 000000000..eb5b330b7 --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1971.c @@ -0,0 +1,270 @@ +#include "protocol_ds1971.h" + +#include +#include + +#include "dallas_common.h" + +#define DS1971_FAMILY_CODE 0x14U +#define DS1971_FAMILY_NAME "DS1971" + +#define DS1971_EEPROM_DATA_SIZE 32U +#define DS1971_SRAM_PAGE_SIZE 32U +#define DS1971_COPY_SCRATCH_DELAY_US 250U + +#define DS1971_DATA_BYTE_COUNT 4U + +#define DS1971_EEPROM_DATA_KEY "Eeprom Data" +#define DS1971_MEMORY_TYPE "EEPROM" + +#define DS1971_CMD_FINALIZATION 0xA5 + +typedef struct { + OneWireSlave* bus; + DallasCommonCommandState command_state; +} DS1971ProtocolState; + +typedef struct { + DallasCommonRomData rom_data; + uint8_t eeprom_data[DS1971_EEPROM_DATA_SIZE]; + DS1971ProtocolState state; +} DS1971ProtocolData; + +static bool dallas_ds1971_read(OneWireHost*, void*); +static bool dallas_ds1971_write_copy(OneWireHost*, iButtonProtocolData*); +static void dallas_ds1971_emulate(OneWireSlave*, iButtonProtocolData*); +static bool dallas_ds1971_load(FlipperFormat*, uint32_t, iButtonProtocolData*); +static bool dallas_ds1971_save(FlipperFormat*, const iButtonProtocolData*); +static void dallas_ds1971_render_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1971_render_brief_data(FuriString*, const iButtonProtocolData*); +static void dallas_ds1971_render_error(FuriString*, const iButtonProtocolData*); +static bool dallas_ds1971_is_data_valid(const iButtonProtocolData*); +static void dallas_ds1971_get_editable_data(iButtonEditableData*, iButtonProtocolData*); +static void dallas_ds1971_apply_edits(iButtonProtocolData*); +static bool + dallas_ds1971_read_mem(OneWireHost* host, uint8_t address, uint8_t* data, size_t data_size); +static bool ds1971_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size); + +const iButtonProtocolDallasBase ibutton_protocol_ds1971 = { + .family_code = DS1971_FAMILY_CODE, + .features = iButtonProtocolFeatureExtData | iButtonProtocolFeatureWriteCopy, + .data_size = sizeof(DS1971ProtocolData), + .manufacturer = DALLAS_COMMON_MANUFACTURER_NAME, + .name = DS1971_FAMILY_NAME, + + .read = dallas_ds1971_read, + .write_blank = NULL, /* No data to write a blank */ + .write_copy = dallas_ds1971_write_copy, + .emulate = dallas_ds1971_emulate, + .save = dallas_ds1971_save, + .load = dallas_ds1971_load, + .render_data = dallas_ds1971_render_data, + .render_brief_data = dallas_ds1971_render_brief_data, + .render_error = dallas_ds1971_render_error, + .is_valid = dallas_ds1971_is_data_valid, + .get_editable_data = dallas_ds1971_get_editable_data, + .apply_edits = dallas_ds1971_apply_edits, +}; + +bool dallas_ds1971_read(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + return onewire_host_reset(host) && dallas_common_read_rom(host, &data->rom_data) && + dallas_ds1971_read_mem(host, 0, data->eeprom_data, DS1971_EEPROM_DATA_SIZE); +} + +bool dallas_ds1971_write_copy(OneWireHost* host, iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + + onewire_host_reset(host); + onewire_host_skip(host); + // Starting writing from address 0x0000 + onewire_host_write(host, DALLAS_COMMON_CMD_WRITE_SCRATCH); + onewire_host_write(host, 0x00); + // Write data to scratchpad + onewire_host_write_bytes(host, data->eeprom_data, DS1971_EEPROM_DATA_SIZE); + + // Read data from scratchpad and verify + bool pad_valid = false; + if(onewire_host_reset(host)) { + pad_valid = true; + onewire_host_skip(host); + onewire_host_write(host, DALLAS_COMMON_CMD_READ_SCRATCH); + onewire_host_write(host, 0x00); + + for(size_t i = 0; i < DS1971_EEPROM_DATA_SIZE; ++i) { + uint8_t scratch = onewire_host_read(host); + if(data->eeprom_data[i] != scratch) { + pad_valid = false; + break; + } + } + } + + // Copy scratchpad to memory and confirm + if(pad_valid) { + if(onewire_host_reset(host)) { + onewire_host_skip(host); + onewire_host_write(host, DALLAS_COMMON_CMD_COPY_SCRATCH); + onewire_host_write(host, DS1971_CMD_FINALIZATION); + + furi_delay_us(DS1971_COPY_SCRATCH_DELAY_US); + } + } + + return pad_valid; +} + +static void dallas_ds1971_reset_callback(void* context) { + furi_assert(context); + DS1971ProtocolData* data = context; + data->state.command_state = DallasCommonCommandStateIdle; +} + +static bool dallas_ds1971_command_callback(uint8_t command, void* context) { + furi_assert(context); + DS1971ProtocolData* data = context; + OneWireSlave* bus = data->state.bus; + + switch(command) { + case DALLAS_COMMON_CMD_SEARCH_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_search_rom(bus, &data->rom_data); + + } else if(data->state.command_state == DallasCommonCommandStateRomCmd) { + data->state.command_state = DallasCommonCommandStateMemCmd; + ds1971_emulate_read_mem(bus, data->eeprom_data, DS1971_EEPROM_DATA_SIZE); + return false; + + } else { + return false; + } + + case DALLAS_COMMON_CMD_READ_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return dallas_common_emulate_read_rom(bus, &data->rom_data); + } else { + return false; + } + + case DALLAS_COMMON_CMD_SKIP_ROM: + if(data->state.command_state == DallasCommonCommandStateIdle) { + data->state.command_state = DallasCommonCommandStateRomCmd; + return true; + } else { + return false; + } + + default: + return false; + } +} + +void dallas_ds1971_emulate(OneWireSlave* bus, iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + data->state.bus = bus; + + onewire_slave_set_reset_callback(bus, dallas_ds1971_reset_callback, protocol_data); + onewire_slave_set_command_callback(bus, dallas_ds1971_command_callback, protocol_data); +} + +bool dallas_ds1971_load( + FlipperFormat* ff, + uint32_t format_version, + iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + bool success = false; + + do { + if(format_version < 2) break; + if(!dallas_common_load_rom_data(ff, format_version, &data->rom_data)) break; + if(!flipper_format_read_hex( + ff, DS1971_EEPROM_DATA_KEY, data->eeprom_data, DS1971_EEPROM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +bool dallas_ds1971_save(FlipperFormat* ff, const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + bool success = false; + + do { + if(!dallas_common_save_rom_data(ff, &data->rom_data)) break; + if(!flipper_format_write_hex( + ff, DS1971_EEPROM_DATA_KEY, data->eeprom_data, DS1971_EEPROM_DATA_SIZE)) + break; + success = true; + } while(false); + + return success; +} + +void dallas_ds1971_render_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + pretty_format_bytes_hex_canonical( + result, + DS1971_DATA_BYTE_COUNT, + PRETTY_FORMAT_FONT_MONOSPACE, + data->eeprom_data, + DS1971_EEPROM_DATA_SIZE); +} + +void dallas_ds1971_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + dallas_common_render_brief_data( + result, &data->rom_data, data->eeprom_data, DS1971_EEPROM_DATA_SIZE, DS1971_MEMORY_TYPE); +} + +void dallas_ds1971_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + + if(!dallas_common_is_valid_crc(&data->rom_data)) { + dallas_common_render_crc_error(result, &data->rom_data); + } +} + +bool dallas_ds1971_is_data_valid(const iButtonProtocolData* protocol_data) { + const DS1971ProtocolData* data = protocol_data; + return dallas_common_is_valid_crc(&data->rom_data); +} + +void dallas_ds1971_get_editable_data( + iButtonEditableData* editable_data, + iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + editable_data->ptr = data->rom_data.bytes; + editable_data->size = sizeof(DallasCommonRomData); +} + +void dallas_ds1971_apply_edits(iButtonProtocolData* protocol_data) { + DS1971ProtocolData* data = protocol_data; + dallas_common_apply_edits(&data->rom_data, DS1971_FAMILY_CODE); +} + +bool dallas_ds1971_read_mem(OneWireHost* host, uint8_t address, uint8_t* data, size_t data_size) { + onewire_host_write(host, DALLAS_COMMON_CMD_READ_MEM); + + onewire_host_write(host, address); + onewire_host_read_bytes(host, data, (uint8_t)data_size); + + return true; +} + +bool ds1971_emulate_read_mem(OneWireSlave* bus, const uint8_t* data, size_t data_size) { + bool success = false; + + do { + uint8_t address; + if(!onewire_slave_receive(bus, &address, sizeof(address))) break; + if(address >= data_size) break; + if(!onewire_slave_send(bus, data + address, data_size - address)) break; + + success = true; + } while(false); + + return success; +} diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1971.h b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1971.h new file mode 100644 index 000000000..522b612da --- /dev/null +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1971.h @@ -0,0 +1,5 @@ +#pragma once + +#include "protocol_dallas_base.h" + +extern const iButtonProtocolDallasBase ibutton_protocol_ds1971; diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c index 131bc634a..17d631259 100644 --- a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1992.c @@ -17,6 +17,7 @@ #define DS1992_DATA_BYTE_COUNT 4U #define DS1992_SRAM_DATA_KEY "Sram Data" +#define DS1992_MEMORY_TYPE "SRAM" typedef struct { OneWireSlave* bus; @@ -188,7 +189,7 @@ void dallas_ds1992_render_data(FuriString* result, const iButtonProtocolData* pr void dallas_ds1992_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { const DS1992ProtocolData* data = protocol_data; dallas_common_render_brief_data( - result, &data->rom_data, data->sram_data, DS1992_SRAM_DATA_SIZE); + result, &data->rom_data, data->sram_data, DS1992_SRAM_DATA_SIZE, DS1992_MEMORY_TYPE); } void dallas_ds1992_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c index e69145c58..74a5792c6 100644 --- a/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_ds1996.c @@ -15,6 +15,7 @@ #define DS1996_DATA_BYTE_COUNT 4U #define DS1996_SRAM_DATA_KEY "Sram Data" +#define DS1996_MEMORY_TYPE "SRAM" typedef struct { OneWireSlave* bus; @@ -182,7 +183,7 @@ void dallas_ds1996_render_data(FuriString* result, const iButtonProtocolData* pr void dallas_ds1996_render_brief_data(FuriString* result, const iButtonProtocolData* protocol_data) { const DS1996ProtocolData* data = protocol_data; dallas_common_render_brief_data( - result, &data->rom_data, data->sram_data, DS1996_SRAM_DATA_SIZE); + result, &data->rom_data, data->sram_data, DS1996_SRAM_DATA_SIZE, DS1996_MEMORY_TYPE); } void dallas_ds1996_render_error(FuriString* result, const iButtonProtocolData* protocol_data) { diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c index e54c3125d..b4dd51ce7 100644 --- a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.c @@ -3,12 +3,14 @@ #include "protocol_ds1990.h" #include "protocol_ds1992.h" #include "protocol_ds1996.h" +#include "protocol_ds1971.h" #include "protocol_ds_generic.h" const iButtonProtocolDallasBase* ibutton_protocols_dallas[] = { [iButtonProtocolDS1990] = &ibutton_protocol_ds1990, [iButtonProtocolDS1992] = &ibutton_protocol_ds1992, [iButtonProtocolDS1996] = &ibutton_protocol_ds1996, + [iButtonProtocolDS1971] = &ibutton_protocol_ds1971, /* Add new 1-Wire protocols here */ /* Default catch-all 1-Wire protocol */ diff --git a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h index ba74c0c23..2ba1dd39a 100644 --- a/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h +++ b/lib/one_wire/ibutton/protocols/dallas/protocol_group_dallas_defs.h @@ -6,6 +6,7 @@ typedef enum { iButtonProtocolDS1990, iButtonProtocolDS1992, iButtonProtocolDS1996, + iButtonProtocolDS1971, /* Add new 1-Wire protocols here */ /* Default catch-all 1-Wire protocol */ From 2cefc9aab06eb4f0af6b54829552333a46fa492a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Mar 2023 18:14:39 +0300 Subject: [PATCH 29/34] Security+ 2.0 custom buttons --- CHANGELOG.md | 1 + ReadMe.md | 2 +- .../scenes/subghz_scene_receiver_info.c | 2 + .../main/subghz/scenes/subghz_scene_rpc.c | 2 + .../subghz/scenes/subghz_scene_transmitter.c | 3 + applications/main/subghz/views/transmitter.c | 4 + .../main/subghz_remote/subghz_remote_app.c | 2 + firmware/targets/f7/api_symbols.csv | 4 + lib/subghz/protocols/secplus_v2.c | 90 +++++++++++++++++++ lib/subghz/protocols/secplus_v2.h | 8 ++ 10 files changed, 117 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71957562d..4e0837af2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### New changes * SubGHz: New protocols support: CAME Space, Stilmatic / Schellenberg * SubGHz: Add Manually - new protocols -> Beninca, Sommer(FSK), IronLogic, DTM Neo, Gibidi, Elmes Electronic (Elmes Poland), CAME Space +* SubGHz: Security+ 2.0 -> Ability to send custom buttons codes (0x80, 0x81, 0xE2) * SubGHz: Remember last external module setting and power setting, so if you turn off flipper it will remember last external module settings (only for subghz app) * SubGHz: Fix issues when external module is not found but plugins tries to use it, now they will fallback to internal in that case * SubGHz: Fixed external CC1101 module power issues, added more checks, fixed issues when launching subghz favourites diff --git a/ReadMe.md b/ReadMe.md index 13edfeb64..8c1a57142 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -56,7 +56,7 @@ Our Discord Community: * Sub-GHz -> Long press OK button in Sub-GHz Frequency analyzer to switch to Read menu * Sub-GHz -> External CC1101 module support * SubGHz -> **Hold right in received signal list to delete selected signal** -* SubGHz -> **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis** - now you can use arrow buttons to send signal with different button code +* SubGHz -> **Custom buttons for Keeloq / Alutech AT4N / Nice Flor S / Somfy Telis / Security+ 2.0** - now you can use arrow buttons to send signal with different button code * SubGHz -> BFT Mitto / Somfy Telis / Nice Flor S manual creation with programming new remote into receiver (use button 0xF for BFT Mitto, 0x8 (Prog) on Somfy Telis) * SubGHz -> Debug mode counter increase settings (+1 -> +5, +10, default: +1) * SubGHz -> Debug PIN output settings for protocol development diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index 90046eb9d..978954181 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -5,6 +5,7 @@ #include #include #include +#include void subghz_scene_receiver_info_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -240,6 +241,7 @@ void subghz_scene_receiver_info_on_exit(void* context) { alutech_reset_original_btn(); nice_flors_reset_original_btn(); somfy_telis_reset_original_btn(); + secplus2_reset_original_btn(); star_line_reset_mfname(); star_line_reset_kl_type(); } diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index 01804492e..ca3d23685 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -4,6 +4,7 @@ #include #include #include +#include typedef enum { SubGhzRpcStateIdle, @@ -117,6 +118,7 @@ void subghz_scene_rpc_on_exit(void* context) { alutech_reset_original_btn(); nice_flors_reset_original_btn(); somfy_telis_reset_original_btn(); + secplus2_reset_original_btn(); star_line_reset_mfname(); star_line_reset_kl_type(); } diff --git a/applications/main/subghz/scenes/subghz_scene_transmitter.c b/applications/main/subghz/scenes/subghz_scene_transmitter.c index 34685a8e5..e32e2b7b2 100644 --- a/applications/main/subghz/scenes/subghz_scene_transmitter.c +++ b/applications/main/subghz/scenes/subghz_scene_transmitter.c @@ -6,6 +6,7 @@ #include #include #include +#include void subghz_scene_transmitter_callback(SubGhzCustomEvent event, void* context) { furi_assert(context); @@ -95,6 +96,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) { alutech_set_btn(0); nice_flors_set_btn(0); somfy_telis_set_btn(0); + secplus2_set_btn(0); uint8_t tmp_counter = furi_hal_subghz_get_rolling_counter_mult(); furi_hal_subghz_set_rolling_counter_mult(0); // Calling restore! @@ -139,6 +141,7 @@ void subghz_scene_transmitter_on_exit(void* context) { alutech_reset_original_btn(); nice_flors_reset_original_btn(); somfy_telis_reset_original_btn(); + secplus2_reset_original_btn(); star_line_reset_mfname(); star_line_reset_kl_type(); } diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index e6ed16d81..7de0e6495 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -8,6 +8,7 @@ #include #include #include +#include struct SubGhzViewTransmitter { View* view; @@ -163,6 +164,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { alutech_set_btn(1); nice_flors_set_btn(1); somfy_telis_set_btn(1); + secplus2_set_btn(1); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, @@ -199,6 +201,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { alutech_set_btn(2); nice_flors_set_btn(2); somfy_telis_set_btn(2); + secplus2_set_btn(2); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, @@ -235,6 +238,7 @@ bool subghz_view_transmitter_input(InputEvent* event, void* context) { alutech_set_btn(3); nice_flors_set_btn(3); somfy_telis_set_btn(3); + secplus2_set_btn(3); with_view_model( subghz_transmitter->view, SubGhzViewTransmitterModel * model, diff --git a/applications/main/subghz_remote/subghz_remote_app.c b/applications/main/subghz_remote/subghz_remote_app.c index 90aa03c9a..e26d23cdf 100644 --- a/applications/main/subghz_remote/subghz_remote_app.c +++ b/applications/main/subghz_remote/subghz_remote_app.c @@ -22,6 +22,7 @@ #include #include #include +#include #define SUBREMOTEMAP_FOLDER "/ext/subghz_remote" #define SUBREMOTEMAP_EXTENSION ".txt" @@ -491,6 +492,7 @@ void subghz_remote_tx_stop(SubGHzRemote* app) { alutech_reset_original_btn(); nice_flors_reset_original_btn(); somfy_telis_reset_original_btn(); + secplus2_reset_original_btn(); star_line_reset_mfname(); star_line_reset_kl_type(); } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 27a9e6b9d..87efb1199 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2462,6 +2462,10 @@ Function,+,scene_manager_search_and_switch_to_previous_scene_one_of,_Bool,"Scene 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,-,secplus2_get_custom_btn,uint8_t, +Function,-,secplus2_get_original_btn,uint8_t, +Function,-,secplus2_reset_original_btn,void, +Function,-,secplus2_set_btn,void,uint8_t Function,-,secure_getenv,char*,const char* Function,-,seed48,unsigned short*,unsigned short[3] Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*" diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index f6837b571..a0bc2b9f4 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -83,6 +83,25 @@ const SubGhzProtocol subghz_protocol_secplus_v2 = { .encoder = &subghz_protocol_secplus_v2_encoder, }; +static uint8_t sc_btn_temp_id; +static uint8_t sc_btn_temp_id_original; + +void secplus2_set_btn(uint8_t b) { + sc_btn_temp_id = b; +} + +uint8_t secplus2_get_original_btn() { + return sc_btn_temp_id_original; +} + +uint8_t secplus2_get_custom_btn() { + return sc_btn_temp_id; +} + +void secplus2_reset_original_btn() { + sc_btn_temp_id_original = 0; +} + void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolEncoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v2)); @@ -338,6 +357,11 @@ static void instance->btn = 0; instance->serial = 0; } + + // Save original button for later use + if(sc_btn_temp_id_original == 0) { + sc_btn_temp_id_original = instance->btn; + } } /** @@ -373,6 +397,72 @@ static uint64_t subghz_protocol_secplus_v2_encode_half(uint8_t roll_array[], uin */ static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* instance) { + // Save original button for later use + if(sc_btn_temp_id_original == 0) { + sc_btn_temp_id_original = instance->generic.btn; + } + + // Set custom button + if(sc_btn_temp_id == 1) { + switch(sc_btn_temp_id_original) { + case 0x68: + instance->generic.btn = 0x80; + break; + case 0x80: + instance->generic.btn = 0x68; + break; + case 0x81: + instance->generic.btn = 0x80; + break; + case 0xE2: + instance->generic.btn = 0x80; + break; + + default: + break; + } + } + if(sc_btn_temp_id == 2) { + switch(sc_btn_temp_id_original) { + case 0x68: + instance->generic.btn = 0x81; + break; + case 0x80: + instance->generic.btn = 0x81; + break; + case 0x81: + instance->generic.btn = 0x68; + break; + case 0xE2: + instance->generic.btn = 0x81; + break; + + default: + break; + } + } + if(sc_btn_temp_id == 3) { + switch(sc_btn_temp_id_original) { + case 0x68: + instance->generic.btn = 0xE2; + break; + case 0x80: + instance->generic.btn = 0xE2; + break; + case 0x81: + instance->generic.btn = 0xE2; + break; + case 0xE2: + instance->generic.btn = 0x68; + break; + + default: + break; + } + } + if((sc_btn_temp_id == 0) && (sc_btn_temp_id_original != 0)) { + instance->generic.btn = sc_btn_temp_id_original; + } uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20}; uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF}; uint8_t rolling_digits[18] = {0}; diff --git a/lib/subghz/protocols/secplus_v2.h b/lib/subghz/protocols/secplus_v2.h index 0eea732af..155d943b2 100644 --- a/lib/subghz/protocols/secplus_v2.h +++ b/lib/subghz/protocols/secplus_v2.h @@ -10,6 +10,14 @@ extern const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder; extern const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder; extern const SubGhzProtocol subghz_protocol_secplus_v2; +// Custom buttons +void secplus2_set_btn(uint8_t b); + +uint8_t secplus2_get_original_btn(); +uint8_t secplus2_get_custom_btn(); + +void secplus2_reset_original_btn(); + /** * Allocate SubGhzProtocolEncoderSecPlus_v2. * @param environment Pointer to a SubGhzEnvironment instance From 67e7325f436135062bd37c58c72925914d3bb571 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Mar 2023 18:30:00 +0300 Subject: [PATCH 30/34] Update readme and changelog --- CHANGELOG.md | 2 ++ documentation/file_formats/iButtonFileFormat.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e0837af2..c478f2287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ * Plugins: Removed `cdefines` from external plugins as it was not used in any way * Plugins: Updated **TOTP (Authenticator)** [(by akopachov)](https://github.com/akopachov/flipper-zero_authenticator) (fixed issue #379) * Plugins: Update for SubGHz Bruteforcer, added - Holtek HT12X 12bit AM 433.920MHz/315MHz/868MHz/915MHz (TE: 433us) +* OFW: iButton: Add support for Dallas DS1971 +* OFW: fbt: explicitly set dist suffix length, not depending on environment settings * OFW: NFC -> Skip the read when the card is not present * OFW: NFC -> Mark keys as not found when they couldn't auth successfully * OFW: Storage -> Require the trailing slash for root paths diff --git a/documentation/file_formats/iButtonFileFormat.md b/documentation/file_formats/iButtonFileFormat.md index d31c297a2..63743f063 100644 --- a/documentation/file_formats/iButtonFileFormat.md +++ b/documentation/file_formats/iButtonFileFormat.md @@ -26,7 +26,7 @@ Changelog: | Name | Type | Description | | ----------- | ------ | -------------------------------------------- | -| Protocol | string | Currently supported: DS1990, DS1992, DS1996, DS1997, DSGeneric*, Cyfral, Metakom | +| Protocol | string | Currently supported: DS1990, DS1992, DS1996, DS1971, DSGeneric*, Cyfral, Metakom | | Rom Data | hex | Read-only memory data (Dallas protocols only) | | Sram Data | hex | Static RAM data (DS1992 and DS1996 only) | Eeprom Data | hex | EEPROM data (DS1971 only) From 559b0e9b40fc6ea468b02d4c8e2cfa53c0ef7c9c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 16 Mar 2023 21:24:09 +0300 Subject: [PATCH 31/34] Small UI fixes --- CHANGELOG.md | 2 ++ applications/main/subghz/scenes/subghz_scene_set_type.c | 2 +- applications/services/gui/modules/submenu.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c478f2287..cfb0d4fe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * SubGHz: Fix issues when external module is not found but plugins tries to use it, now they will fallback to internal in that case * SubGHz: Fixed external CC1101 module power issues, added more checks, fixed issues when launching subghz favourites * SubGHz: Removed 330MHz from default freq hopper to make it faster, to change freq hopper settings and remove/add your freqs see -> [Instruction](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzSettings.md) +* SubGHz: Small UI text fixes +* GUI: Fix submenu elements text size, now we can fit more symbols * Plugins: Added delay and retries to avoid power issues in plugins -> WiFi Scanner, ESP8266 Deauther * Plugins: Update for WiFi Marauder companion -> `sniff` saves pcaps in `YourFlippersMicroSD/apps_data/marauder/` -> Only with custom marauder build (It is necessary to uncomment "#define WRITE_PACKETS_SERIAL" in configs.h (in marauder fw) and compile the firmware for the wifi board.) Or download precompiled build -> [Download esp32_marauder_ver_flipper_sd_serial.bin](https://github.com/justcallmekoko/ESP32Marauder/releases/latest) - [(by tcpassos)](https://github.com/0xchocolate/flipperzero-firmware-with-wifi-marauder-companion/pull/7) * Plugins: Removed `cdefines` from external plugins as it was not used in any way diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 26065b107..50af12669 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -220,7 +220,7 @@ void subghz_scene_set_type_on_enter(void* context) { subghz); submenu_add_item( subghz->submenu, - "CAME Space 433MHz", + "KL: CAME Space 433MHz", SubmenuIndexCAMESpace, subghz_scene_set_type_submenu_callback, subghz); diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 00e4d68b5..66b264458 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -98,7 +98,7 @@ static void submenu_view_draw_callback(Canvas* canvas, void* _model) { FuriString* disp_str; disp_str = furi_string_alloc_set(SubmenuItemArray_cref(it)->label); - elements_string_fit_width(canvas, disp_str, item_width - 20); + elements_string_fit_width(canvas, disp_str, item_width - 11); canvas_draw_str( canvas, From b0928fb3cd2d907fa7dc88ea6ec035c7ff76da92 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 17 Mar 2023 00:27:01 +0300 Subject: [PATCH 32/34] Fix subghz external module power issues --- applications/main/subghz/subghz.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 27a5ce634..75c6e627a 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -73,10 +73,6 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->in_decoder_scene = false; subghz->in_decoder_scene_skip = false; - // Call enable power for external module - furi_hal_subghz_enable_ext_power(); - furi_delay_ms(15); - // View Dispatcher subghz->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(subghz->view_dispatcher); @@ -425,9 +421,6 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) { // The rest free(subghz); - - // Disable power for External CC1101 if it was enabled and module is connected - furi_hal_subghz_disable_ext_power(); } int32_t subghz_app(void* p) { @@ -452,6 +445,9 @@ int32_t subghz_app(void* p) { subghz_environment_load_keystore( subghz->txrx->environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); + // Call enable power for external module + furi_hal_subghz_enable_ext_power(); + // Auto switch to internal radio if external radio is not available if(!furi_hal_subghz_check_radio()) { subghz->last_settings->external_module_enabled = false; @@ -509,6 +505,8 @@ int32_t subghz_app(void* p) { view_dispatcher_run(subghz->view_dispatcher); furi_hal_power_suppress_charge_exit(); + // Disable power for External CC1101 if it was enabled and module is connected + furi_hal_subghz_disable_ext_power(); subghz_free(subghz, alloc_for_tx); From 7de7fa293b6eb8def92ecfecc1c1aea63a269624 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:45:42 +0200 Subject: [PATCH 33/34] Optimize trailing slash check (#2503) * Optimize trailing slash check --- .../services/storage/storage_processing.c | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index 59527e769..a3076f271 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -2,6 +2,17 @@ #include #include +#define STORAGE_PATH_PREFIX_LEN 4u +_Static_assert( + sizeof(STORAGE_ANY_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1, + "Any path prefix len mismatch"); +_Static_assert( + sizeof(STORAGE_EXT_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1, + "Ext path prefix len mismatch"); +_Static_assert( + sizeof(STORAGE_INT_PATH_PREFIX) == STORAGE_PATH_PREFIX_LEN + 1, + "Int path prefix len mismatch"); + #define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn; static bool storage_type_is_valid(StorageType type) { @@ -26,34 +37,29 @@ static StorageData* get_storage_by_file(File* file, StorageData* storages) { static const char* cstr_path_without_vfs_prefix(FuriString* path) { const char* path_cstr = furi_string_get_cstr(path); - return path_cstr + MIN(4u, strlen(path_cstr)); + return path_cstr + MIN(STORAGE_PATH_PREFIX_LEN, strlen(path_cstr)); } static StorageType storage_get_type_by_path(FuriString* path) { StorageType type = ST_ERROR; const char* path_cstr = furi_string_get_cstr(path); - if(furi_string_size(path) == 4) { - if(memcmp(path_cstr, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { - type = ST_EXT; - } else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { - type = ST_INT; - } else if(memcmp(path_cstr, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { - type = ST_ANY; - } - } else if(furi_string_size(path) > 4) { - if(memcmp(path_cstr, EXT_PATH(""), strlen(EXT_PATH(""))) == 0) { - type = ST_EXT; - } else if(memcmp(path_cstr, INT_PATH(""), strlen(INT_PATH(""))) == 0) { - type = ST_INT; - } else if(memcmp(path_cstr, ANY_PATH(""), strlen(ANY_PATH(""))) == 0) { - type = ST_ANY; + if(furi_string_size(path) > STORAGE_PATH_PREFIX_LEN) { + if(path_cstr[STORAGE_PATH_PREFIX_LEN] != '/') { + return ST_ERROR; } } + if(memcmp(path_cstr, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { + type = ST_EXT; + } else if(memcmp(path_cstr, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { + type = ST_INT; + } else if(memcmp(path_cstr, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { + type = ST_ANY; + } + return type; } - static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) { if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) { switch(real_storage) { From 9941457cdb3fc1000f83260a58bc57569f01f03e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 17 Mar 2023 22:51:59 +0300 Subject: [PATCH 34/34] Fix issue #387 --- applications/external/snake_game/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/snake_game/application.fam b/applications/external/snake_game/application.fam index c736a4ddc..4c15c309b 100644 --- a/applications/external/snake_game/application.fam +++ b/applications/external/snake_game/application.fam @@ -1,5 +1,5 @@ App( - appid="snake_game", + appid="Snake", name="Snake Game", apptype=FlipperAppType.EXTERNAL, entry_point="snake_game_app",