From 263c94d1673f151a6dfb04c1e5a1d528469a3c44 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 3 Jun 2023 15:03:07 +0300 Subject: [PATCH 001/102] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e4a49fb2d..d93dd25baf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ### New changes * If you have copied any apps manually into `apps` folder - remove `apps` folder or that specific apps you copied on your microSD before installing this release to avoid issues due to OFW API version update! If you using regular builds or extra pack builds (e) without your manually added apps, all included apps will be installed automatically, no extra actions needed! ----- +* Only in release 052 -> **Multiple Extra pack apps was fixed!** -> TAMA P1, Flizzer Tracker, Video Player, Music Tracker +### Previous changes * SubGHz Remote: Fixed BinRAW support, + many other fixes (by @gid9798 | PR #492) * SubGHz: Fix KL: Stilmatic support + add manually support * SubGHz: Keeloq mfname refactoring (by @gid9798 | PR #479) From 2008247e658b8f4a60fd29c6593d0fa23e416d81 Mon Sep 17 00:00:00 2001 From: Tiernan Messmer Date: Sat, 3 Jun 2023 22:31:24 +1000 Subject: [PATCH 002/102] Remove delay from emulation loop. This improves compatibility when the reader is Android. --- lib/nfc/nfc_worker.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index f2d07e4c25..2819ea7ad6 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -179,7 +179,6 @@ void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { } } } - furi_delay_ms(10); } nfcv_emu_deinit(nfcv_data); @@ -207,7 +206,6 @@ void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker) { nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); } } - furi_delay_ms(10); } nfcv_emu_deinit(nfcv_data); From a0e39ddb67c0d2376f8a726ca1d5acbc6740b202 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 3 Jun 2023 20:36:20 +0300 Subject: [PATCH 003/102] more checks just in case?? --- lib/digital_signal/digital_signal.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 6ccfcf2803..9dd13886d8 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -243,11 +243,15 @@ static void digital_signal_stop_timer() { LL_TIM_DisableUpdateEvent(TIM2); LL_TIM_DisableDMAReq_UPDATE(TIM2); - furi_hal_bus_disable(FuriHalBusTIM2); + if(furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_disable(FuriHalBusTIM2); + } } static void digital_signal_setup_timer() { - furi_hal_bus_enable(FuriHalBusTIM2); + if(!furi_hal_bus_is_enabled(FuriHalBusTIM2)) { + furi_hal_bus_enable(FuriHalBusTIM2); + } LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1); From 5a2fbf4af3e65369a6beab12af86971e9e3dc49e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:36:19 +0300 Subject: [PATCH 004/102] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d93dd25baf..d92b032a4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * If you have copied any apps manually into `apps` folder - remove `apps` folder or that specific apps you copied on your microSD before installing this release to avoid issues due to OFW API version update! If you using regular builds or extra pack builds (e) without your manually added apps, all included apps will be installed automatically, no extra actions needed! ----- * Only in release 052 -> **Multiple Extra pack apps was fixed!** -> TAMA P1, Flizzer Tracker, Video Player, Music Tracker +* NFC V: Remove delay from emulation loop. This improves compatibility when the reader is Android. ### Previous changes * SubGHz Remote: Fixed BinRAW support, + many other fixes (by @gid9798 | PR #492) * SubGHz: Fix KL: Stilmatic support + add manually support From 75ee4efa31b3cb38b55b031be1da0c42ff557890 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:46:55 +0300 Subject: [PATCH 005/102] Fix ibtn fuzzer file loading (temp) --- CHANGELOG.md | 1 + .../scene/ibtnfuzzer_scene_load_file.c | 173 +++++++++++++++--- 2 files changed, 144 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d92b032a4b..f81527b72e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ----- * Only in release 052 -> **Multiple Extra pack apps was fixed!** -> TAMA P1, Flizzer Tracker, Video Player, Music Tracker * NFC V: Remove delay from emulation loop. This improves compatibility when the reader is Android. +* Plugins: iButton Fuzzer -> Fix v2 key files load (all new saved files) ### Previous changes * SubGHz Remote: Fixed BinRAW support, + many other fixes (by @gid9798 | PR #492) * SubGHz: Fix KL: Stilmatic support + add manually support diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c index 47d5122abf..92f79a424c 100644 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c +++ b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c @@ -10,6 +10,7 @@ bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); FuriString* temp_str; temp_str = furi_string_alloc(); + bool key_v2 = false; do { if(!flipper_format_file_open_existing(fff_data_file, file_path)) { FURI_LOG_E(TAG, "Error open file %s", file_path); @@ -30,10 +31,43 @@ bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { // Key type if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key type"); - break; + FURI_LOG_E(TAG, "Missing or incorrect Key type, checking for typ2.."); + + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Failed to rewind file"); + break; + } + if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { + furi_string_reset(context->notification_msg); + furi_string_set( + context->notification_msg, "Missing or incorrect Protocol or Key type"); + break; + } + FURI_LOG_I(TAG, "Key type V2: %s", furi_string_get_cstr(temp_str)); + key_v2 = true; + + if(context->proto == DS1990) { + if(strcmp(furi_string_get_cstr(temp_str), "DS1990") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Unsupported Key type"); + break; + } + } else if(context->proto == Cyfral) { + if(strcmp(furi_string_get_cstr(temp_str), "Cyfral") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Unsupported Key type"); + break; + } + } else { + if(strcmp(furi_string_get_cstr(temp_str), "Metakom") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Unsupported Key type"); + break; + } + } } else { FURI_LOG_I(TAG, "Key type: %s", furi_string_get_cstr(temp_str)); @@ -60,46 +94,125 @@ bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { } } } + if(!key_v2) { + // Data + if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Data"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Missing or incorrect Key"); + break; + } else { + FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - // Data - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); + if(context->proto == DS1990) { + if(furi_string_size(context->data_str) != 23) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + } else if(context->proto == Cyfral) { + if(furi_string_size(context->data_str) != 5) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + } else { + if(furi_string_size(context->data_str) != 11) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + } + // String to uint8_t + for(uint8_t i = 0; i < 8; i++) { + char temp_str2[3]; + temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + } + } + } else { + // Data if(context->proto == DS1990) { - if(furi_string_size(context->data_str) != 23) { - FURI_LOG_E(TAG, "Incorrect Key length"); + if(!flipper_format_read_string(fff_data_file, "Rom Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Rom Data"); furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); + furi_string_set(context->notification_msg, "Missing or incorrect Rom Data"); break; + } else { + FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); + + if(furi_string_size(context->data_str) != 23) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + + // String to uint8_t + for(uint8_t i = 0; i < 8; i++) { + char temp_str2[3]; + temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + } } } else if(context->proto == Cyfral) { - if(furi_string_size(context->data_str) != 5) { - FURI_LOG_E(TAG, "Incorrect Key length"); + if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Data"); furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); + furi_string_set(context->notification_msg, "Missing or incorrect Data"); break; + } else { + FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); + + if(furi_string_size(context->data_str) != 5) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + + // String to uint8_t + for(uint8_t i = 0; i < 8; i++) { + char temp_str2[3]; + temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + } } } else { - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); + if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Data"); furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); + furi_string_set(context->notification_msg, "Missing or incorrect Data"); break; - } - } + } else { + FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + if(furi_string_size(context->data_str) != 11) { + FURI_LOG_E(TAG, "Incorrect Key length"); + furi_string_reset(context->notification_msg); + furi_string_set(context->notification_msg, "Incorrect Key length"); + break; + } + + // String to uint8_t + for(uint8_t i = 0; i < 8; i++) { + char temp_str2[3]; + temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + } + } } } From 6c6a4816836efb4b591900fbb366c96b802114d9 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:59:49 +0100 Subject: [PATCH 006/102] Fix switching nsfw mode --- lib/xtreme/assets.c | 1 + lib/xtreme/settings.c | 1 - lib/xtreme/xtreme.h | 3 ++- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/xtreme/assets.c b/lib/xtreme/assets.c index e291d60744..59df1206d9 100644 --- a/lib/xtreme/assets.c +++ b/lib/xtreme/assets.c @@ -105,6 +105,7 @@ void XTREME_ASSETS_LOAD() { if(!furi_hal_is_normal_boot()) return; const char* pack = XTREME_SETTINGS()->asset_pack; + XTREME_SETTINGS()->is_nsfw = !strncmp(pack, "NSFW", strlen("NSFW")); if(pack[0] == '\0') return; Storage* storage = furi_record_open(RECORD_STORAGE); diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index f87e30f5f4..fb2a48d1be 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -43,7 +43,6 @@ void XTREME_SETTINGS_LOAD() { FuriString* string = furi_string_alloc(); if(flipper_format_read_string(file, "asset_pack", string)) { strlcpy(x->asset_pack, furi_string_get_cstr(string), XTREME_ASSETS_PACK_NAME_LEN); - x->is_nsfw = strncmp(x->asset_pack, "NSFW", strlen("NSFW")) == 0; } furi_string_free(string); uint32_t u; diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index a827730f87..7a30adf8bc 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -13,8 +13,9 @@ extern "C" { #define XTREME_ASSETS_PACK_NAME_LEN 32 typedef struct { - char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; bool is_nsfw; // TODO: replace with packs text support + + char asset_pack[XTREME_ASSETS_PACK_NAME_LEN]; uint32_t anim_speed; int32_t cycle_anims; bool unlock_anims; From e50c8f496bae17574f042640b9575d136fa7f175 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:00:04 +0100 Subject: [PATCH 007/102] Fix weird format --- lib/xtreme/settings.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index fb2a48d1be..ecf1c905bc 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -118,9 +118,7 @@ void XTREME_SETTINGS_LOAD() { } flipper_format_rewind(file); if(flipper_format_read_bool(file, "dark_mode", &b, 1)) { - { - x->dark_mode = b; - } + x->dark_mode = b; } flipper_format_rewind(file); if(flipper_format_read_uint32(file, "favorite_timeout", &u, 1)) { From eda2ab4e5dda65384799825d328ae797a54da63b Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sat, 3 Jun 2023 23:11:59 +0100 Subject: [PATCH 008/102] Fix battery icon cast (#286) --- lib/xtreme/settings.c | 2 +- lib/xtreme/xtreme.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index ecf1c905bc..f3e3cd6c2a 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -169,7 +169,7 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "lockscreen_date", &x->lockscreen_date, 1); flipper_format_write_bool(file, "lockscreen_statusbar", &x->lockscreen_statusbar, 1); flipper_format_write_bool(file, "lockscreen_prompt", &x->lockscreen_prompt, 1); - flipper_format_write_uint32(file, "battery_icon", (uint32_t*)&x->battery_icon, 1); + flipper_format_write_uint32(file, "battery_icon", &x->battery_icon, 1); flipper_format_write_bool(file, "status_icons", &x->status_icons, 1); flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 7a30adf8bc..ea60763549 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -28,7 +28,7 @@ typedef struct { bool lockscreen_date; bool lockscreen_statusbar; bool lockscreen_prompt; - BatteryIcon battery_icon; + uint32_t battery_icon; bool status_icons; bool bar_borders; bool bar_background; From 6d433edc3887d007b20c9dc6ee05dfcc1b459b1f Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 10:48:46 +0200 Subject: [PATCH 009/102] Add furi_hal_version_uid_default (Fix TOTP) --- applications/external/totp/services/crypto/crypto.c | 2 +- firmware/targets/f7/api_symbols.csv | 3 ++- firmware/targets/f7/furi_hal/furi_hal_version.c | 4 ++++ firmware/targets/furi_hal_include/furi_hal_version.h | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 03d9c9d518..d01fe5c11a 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -90,7 +90,7 @@ CryptoSeedIVResult max_i = uid_size; } - const uint8_t* uid = furi_hal_version_uid(); + const uint8_t* uid = furi_hal_version_uid_default(); for(uint8_t i = 0; i < max_i; i++) { plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index c033cf3ea4..5128fdaced 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,+,28.2,, +Version,+,28.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1481,6 +1481,7 @@ Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, Function,-,furi_hal_version_init,void, Function,-,furi_hal_version_set_name,void,const char* Function,+,furi_hal_version_uid,const uint8_t*, +Function,+,furi_hal_version_uid_default,const uint8_t*, Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index d0857f288d..0e5f428baf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -314,6 +314,10 @@ size_t furi_hal_version_uid_size() { return 64 / 8; } +const uint8_t* furi_hal_version_uid_default() { + return (const uint8_t*)UID64_BASE; +} + const uint8_t* furi_hal_version_uid() { if(version_get_custom_name(NULL) != NULL) { return (const uint8_t*)&(*((uint32_t*)version_get_custom_name(NULL))); diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index f5e5ca49ad..4a3f4c1707 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -204,6 +204,8 @@ size_t furi_hal_version_uid_size(); */ const uint8_t* furi_hal_version_uid(); +const uint8_t* furi_hal_version_uid_default(); + #ifdef __cplusplus } #endif From b7f91fe5a5bad433481f9a48540e61e78fc1dfbe Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 10:48:51 +0200 Subject: [PATCH 010/102] Add furi_hal_version_uid_default (Fix TOTP) --- applications/external/totp/services/crypto/crypto.c | 2 +- firmware/targets/f7/api_symbols.csv | 1 + firmware/targets/f7/furi_hal/furi_hal_version.c | 4 ++++ firmware/targets/furi_hal_include/furi_hal_version.h | 2 ++ lib/STM32CubeWB | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) create mode 160000 lib/STM32CubeWB diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 03d9c9d518..d01fe5c11a 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -90,7 +90,7 @@ CryptoSeedIVResult max_i = uid_size; } - const uint8_t* uid = furi_hal_version_uid(); + const uint8_t* uid = furi_hal_version_uid_default(); for(uint8_t i = 0; i < max_i; i++) { plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 863cbcb6fb..a8a32069b6 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1531,6 +1531,7 @@ Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, Function,-,furi_hal_version_init,void, Function,-,furi_hal_version_set_name,void,const char* Function,+,furi_hal_version_uid,const uint8_t*, +Function,+,furi_hal_version_uid_default,const uint8_t*, Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index 814ec39267..66580945e3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -318,6 +318,10 @@ size_t furi_hal_version_uid_size() { return 64 / 8; } +const uint8_t* furi_hal_version_uid_default() { + return (const uint8_t*)UID64_BASE; +} + const uint8_t* furi_hal_version_uid() { if(version_get_custom_name(NULL) != NULL) { return (const uint8_t*)&(*((uint32_t*)version_get_custom_name(NULL))); diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index df97a988a7..351a4da10c 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -204,6 +204,8 @@ size_t furi_hal_version_uid_size(); */ const uint8_t* furi_hal_version_uid(); +const uint8_t* furi_hal_version_uid_default(); + #ifdef __cplusplus } #endif diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB new file mode 160000 index 0000000000..a9e29b431f --- /dev/null +++ b/lib/STM32CubeWB @@ -0,0 +1 @@ +Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 From a12f563d1c44b9bd6b71035bc50f6a4b8cfea4bf Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 10:56:53 +0200 Subject: [PATCH 011/102] revert in app, dumb idea, needs some more work. --- applications/external/totp/services/crypto/crypto.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index d01fe5c11a..03d9c9d518 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -90,7 +90,7 @@ CryptoSeedIVResult max_i = uid_size; } - const uint8_t* uid = furi_hal_version_uid_default(); + const uint8_t* uid = furi_hal_version_uid(); for(uint8_t i = 0; i < max_i; i++) { plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; } From d339adc8abe1f9cd5e2c223782ec83815c71c9e3 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 11:08:47 +0200 Subject: [PATCH 012/102] where tf did this came from, lol? --- lib/STM32CubeWB | 1 - 1 file changed, 1 deletion(-) delete mode 160000 lib/STM32CubeWB diff --git a/lib/STM32CubeWB b/lib/STM32CubeWB deleted file mode 160000 index a9e29b431f..0000000000 --- a/lib/STM32CubeWB +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a9e29b431f6dac95b6fc860a717834f35b7f78e5 From 0f8ca0a56347571963ade5af580e843eff946543 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 4 Jun 2023 12:52:24 +0300 Subject: [PATCH 013/102] Apply required TOTP changes --- applications/external/totp/services/crypto/crypto.c | 5 ++++- firmware/targets/f7/api_symbols.csv | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/applications/external/totp/services/crypto/crypto.c b/applications/external/totp/services/crypto/crypto.c index 03d9c9d518..4485291007 100644 --- a/applications/external/totp/services/crypto/crypto.c +++ b/applications/external/totp/services/crypto/crypto.c @@ -89,8 +89,11 @@ CryptoSeedIVResult } else { max_i = uid_size; } - +#if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_UL_XFW + const uint8_t* uid = furi_hal_version_uid_default(); +#else const uint8_t* uid = furi_hal_version_uid(); +#endif for(uint8_t i = 0; i < max_i; i++) { plugin_state->iv[i] = plugin_state->iv[i] ^ uid[i]; } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 5128fdaced..58f439b3a6 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,+,28.3,, +Version,+,28.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, From d7b5eac90b13eb1dc66181c7ea5b35af06096297 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 4 Jun 2023 11:57:00 +0200 Subject: [PATCH 014/102] Add ko-fi --- ReadMe.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 82cbfd7a0a..c2cdaf59e1 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -222,12 +222,13 @@ $ ./fbt resources icons dolphin_ext ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, just specify amount and hit send -- **[Patreon](https://patreon.com/CynthiaLabs)** -- **[Paypal](https://paypal.me/RdX2020)** +- **[Patreon](https://patreon.com/CynthiaLabs)** ❤️ Account needed, subscription with perks across my entire org. +- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)** Account needed, one-time +- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` **Thanks for all your support <3** ---- -

"What we do for ourselves dies with us. What we do for others and the world remains and is immortal.” ― Albert Pine

\ No newline at end of file +

"What we do for ourselves dies with us. What we do for others and the world remains and is immortal.” ― Albert Pine

From ec2c6467caf5712961674a9800cb0b0e9e2f524d Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 11:58:06 +0200 Subject: [PATCH 015/102] Also add ko-fi to release script --- .github/workflow_data/release.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index 0e716526af..88daf5d9bd 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -21,9 +21,10 @@ ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Direct Wire-transfer](https://bunq.me/ClaraK)**: No account needed, just specify amount and hit send -- **[Patreon](https://patreon.com/CynthiaLabs)** -- **[Paypal](https://paypal.me/RdX2020)** +- **[Patreon](https://patreon.com/CynthiaLabs)** ❤️ Account needed, subscription with perks across my entire org. +- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)** Account needed, one-time +- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` **Thanks for all your support <3** From f5b7ce5a0865d7134d9c0a383a7bd7a04b84be30 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 12:00:06 +0200 Subject: [PATCH 016/102] consistency is power --- .github/workflow_data/release.md | 4 ++-- ReadMe.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index 88daf5d9bd..77316bae78 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -21,9 +21,9 @@ ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Patreon](https://patreon.com/CynthiaLabs)** ❤️ Account needed, subscription with perks across my entire org. +- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. - **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time -- **[Paypal](https://paypal.me/RdX2020)** Account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time - **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` diff --git a/ReadMe.md b/ReadMe.md index c2cdaf59e1..e99e1b9dda 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -222,9 +222,9 @@ $ ./fbt resources icons dolphin_ext ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! -- **[Patreon](https://patreon.com/CynthiaLabs)** ❤️ Account needed, subscription with perks across my entire org. +- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. - **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time -- **[Paypal](https://paypal.me/RdX2020)** Account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time - **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time - **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` From 2e47c5a2acbcf5e4cfd614fe209043760e90e9a3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:27:59 +0300 Subject: [PATCH 017/102] lets try patch --- .ci_files/rgb.patch | 556 ++++++++++++++++++++++++++++++++++++++++++++ .drone.yml | 96 +++++++- 2 files changed, 648 insertions(+), 4 deletions(-) create mode 100644 .ci_files/rgb.patch diff --git a/.ci_files/rgb.patch b/.ci_files/rgb.patch new file mode 100644 index 0000000000..804034bab3 --- /dev/null +++ b/.ci_files/rgb.patch @@ -0,0 +1,556 @@ +diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c +index f91a73f32..b559a79ad 100644 +--- a/applications/services/notification/notification_app.c ++++ b/applications/services/notification/notification_app.c +@@ -6,6 +6,7 @@ + #include "notification.h" + #include "notification_messages.h" + #include "notification_app.h" ++#include "applications/settings/notification_settings/rgb_backlight.h" + + #define TAG "NotificationSrv" + +@@ -564,6 +565,7 @@ int32_t notification_srv(void* p) { + break; + case SaveSettingsMessage: + notification_save_settings(app); ++ rgb_backlight_save_settings(); + break; + } + +diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c +index f5d7a82ca..930c0bd1f 100644 +--- a/applications/settings/notification_settings/notification_settings_app.c ++++ b/applications/settings/notification_settings/notification_settings_app.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + + #define MAX_NOTIFICATION_SETTINGS 4 + +@@ -73,7 +74,6 @@ const bool vibro_value[VIBRO_COUNT] = {false, true}; + static void backlight_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); +- + variable_item_set_current_value_text(item, backlight_text[index]); + app->notification->settings.display_brightness = backlight_value[index]; + notification_message(app->notification, &sequence_display_backlight_on); +@@ -125,6 +125,14 @@ static void vibro_changed(VariableItem* item) { + notification_message(app->notification, &sequence_single_vibro); + } + ++static void color_changed(VariableItem* item) { ++ NotificationAppSettings* app = variable_item_get_context(item); ++ uint8_t index = variable_item_get_current_value_index(item); ++ rgb_backlight_set_color(index); ++ variable_item_set_current_value_text(item, rgb_backlight_get_color_text(index)); ++ notification_message(app->notification, &sequence_display_backlight_on); ++} ++ + static uint32_t notification_app_settings_exit(void* context) { + UNUSED(context); + return VIEW_NONE; +@@ -143,7 +151,13 @@ static NotificationAppSettings* alloc_settings() { + uint8_t value_index; + + item = variable_item_list_add( +- app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); ++ app->variable_item_list, "LCD Color", rgb_backlight_get_color_count(), color_changed, app); ++ value_index = rgb_backlight_get_settings()->display_color_index; ++ variable_item_set_current_value_index(item, value_index); ++ variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); ++ ++ item = variable_item_list_add( ++ app->variable_item_list, "LCD Brightness", BACKLIGHT_COUNT, backlight_changed, app); + value_index = value_index_float( + app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT); + variable_item_set_current_value_index(item, value_index); +@@ -215,6 +229,7 @@ int32_t notification_settings_app(void* p) { + NotificationAppSettings* app = alloc_settings(); + view_dispatcher_run(app->view_dispatcher); + notification_message_save_settings(app->notification); ++ + free_settings(app); + return 0; + } +diff --git a/applications/settings/notification_settings/rgb_backlight.c b/applications/settings/notification_settings/rgb_backlight.c +new file mode 100644 +index 000000000..269b544ae +--- /dev/null ++++ b/applications/settings/notification_settings/rgb_backlight.c +@@ -0,0 +1,171 @@ ++/* ++ RGB backlight FlipperZero driver ++ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "rgb_backlight.h" ++#include ++#include ++ ++#define RGB_BACKLIGHT_SETTINGS_VERSION 5 ++#define RGB_BACKLIGHT_SETTINGS_FILE_NAME ".rgb_backlight.settings" ++#define RGB_BACKLIGHT_SETTINGS_PATH EXT_PATH(RGB_BACKLIGHT_SETTINGS_FILE_NAME) ++ ++#define COLOR_COUNT (sizeof(colors) / sizeof(RGBBacklightColor)) ++ ++#define TAG "RGB Backlight" ++ ++static RGBBacklightSettings rgb_settings = { ++ .version = RGB_BACKLIGHT_SETTINGS_VERSION, ++ .display_color_index = 0, ++ .settings_is_loaded = false}; ++ ++static const RGBBacklightColor colors[] = { ++ {"Orange", 255, 79, 0}, ++ {"Yellow", 255, 170, 0}, ++ {"Spring", 167, 255, 0}, ++ {"Lime", 0, 255, 0}, ++ {"Aqua", 0, 255, 127}, ++ {"Cyan", 0, 210, 210}, ++ {"Azure", 0, 127, 255}, ++ {"Blue", 0, 0, 255}, ++ {"Purple", 127, 0, 255}, ++ {"Magenta", 210, 0, 210}, ++ {"Pink", 255, 0, 127}, ++ {"Red", 255, 0, 0}, ++ {"White", 140, 140, 140}, ++}; ++ ++uint8_t rgb_backlight_get_color_count(void) { ++ return COLOR_COUNT; ++} ++ ++const char* rgb_backlight_get_color_text(uint8_t index) { ++ return colors[index].name; ++} ++ ++void rgb_backlight_load_settings(void) { ++ //Не загружать данные из внутренней памяти при загрузке в режиме DFU ++ FuriHalRtcBootMode bm = furi_hal_rtc_get_boot_mode(); ++ if(bm == FuriHalRtcBootModeDfu) { ++ rgb_settings.settings_is_loaded = true; ++ return; ++ } ++ ++ RGBBacklightSettings settings; ++ File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); ++ const size_t settings_size = sizeof(RGBBacklightSettings); ++ ++ FURI_LOG_I(TAG, "loading settings from \"%s\"", RGB_BACKLIGHT_SETTINGS_PATH); ++ bool fs_result = ++ storage_file_open(file, RGB_BACKLIGHT_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING); ++ ++ if(fs_result) { ++ uint16_t bytes_count = storage_file_read(file, &settings, settings_size); ++ ++ if(bytes_count != settings_size) { ++ fs_result = false; ++ } ++ } ++ ++ if(fs_result) { ++ FURI_LOG_I(TAG, "load success"); ++ if(settings.version != RGB_BACKLIGHT_SETTINGS_VERSION) { ++ FURI_LOG_E( ++ TAG, ++ "version(%d != %d) mismatch", ++ settings.version, ++ RGB_BACKLIGHT_SETTINGS_VERSION); ++ } else { ++ memcpy(&rgb_settings, &settings, settings_size); ++ } ++ } else { ++ FURI_LOG_E(TAG, "load failed, %s", storage_file_get_error_desc(file)); ++ } ++ ++ storage_file_close(file); ++ storage_file_free(file); ++ furi_record_close(RECORD_STORAGE); ++ rgb_settings.settings_is_loaded = true; ++}; ++ ++void rgb_backlight_save_settings(void) { ++ RGBBacklightSettings settings; ++ File* file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); ++ const size_t settings_size = sizeof(RGBBacklightSettings); ++ ++ FURI_LOG_I(TAG, "saving settings to \"%s\"", RGB_BACKLIGHT_SETTINGS_PATH); ++ ++ memcpy(&settings, &rgb_settings, settings_size); ++ ++ bool fs_result = ++ storage_file_open(file, RGB_BACKLIGHT_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS); ++ ++ if(fs_result) { ++ uint16_t bytes_count = storage_file_write(file, &settings, settings_size); ++ ++ if(bytes_count != settings_size) { ++ fs_result = false; ++ } ++ } ++ ++ if(fs_result) { ++ FURI_LOG_I(TAG, "save success"); ++ } else { ++ FURI_LOG_E(TAG, "save failed, %s", storage_file_get_error_desc(file)); ++ } ++ ++ storage_file_close(file); ++ storage_file_free(file); ++ furi_record_close(RECORD_STORAGE); ++}; ++ ++RGBBacklightSettings* rgb_backlight_get_settings(void) { ++ if(!rgb_settings.settings_is_loaded) { ++ rgb_backlight_load_settings(); ++ } ++ return &rgb_settings; ++} ++ ++void rgb_backlight_set_color(uint8_t color_index) { ++ if(color_index > (rgb_backlight_get_color_count() - 1)) color_index = 0; ++ rgb_settings.display_color_index = color_index; ++} ++ ++void rgb_backlight_update(uint8_t brightness) { ++ if(!rgb_settings.settings_is_loaded) { ++ rgb_backlight_load_settings(); ++ } ++ ++ static uint8_t last_color_index = 255; ++ static uint8_t last_brightness = 123; ++ ++ if(last_brightness == brightness && last_color_index == rgb_settings.display_color_index) ++ return; ++ ++ last_brightness = brightness; ++ last_color_index = rgb_settings.display_color_index; ++ ++ for(uint8_t i = 0; i < SK6805_get_led_count(); i++) { ++ uint8_t r = colors[rgb_settings.display_color_index].red * (brightness / 255.0f); ++ uint8_t g = colors[rgb_settings.display_color_index].green * (brightness / 255.0f); ++ uint8_t b = colors[rgb_settings.display_color_index].blue * (brightness / 255.0f); ++ ++ SK6805_set_led_color(i, r, g, b); ++ } ++ ++ SK6805_update(); ++} +diff --git a/applications/settings/notification_settings/rgb_backlight.h b/applications/settings/notification_settings/rgb_backlight.h +new file mode 100644 +index 000000000..b63d223e6 +--- /dev/null ++++ b/applications/settings/notification_settings/rgb_backlight.h +@@ -0,0 +1,79 @@ ++/* ++ RGB backlight FlipperZero driver ++ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include ++#include "SK6805.h" ++ ++typedef struct { ++ char* name; ++ uint8_t red; ++ uint8_t green; ++ uint8_t blue; ++} RGBBacklightColor; ++ ++typedef struct { ++ uint8_t version; ++ uint8_t display_color_index; ++ bool settings_is_loaded; ++} RGBBacklightSettings; ++ ++/** ++ * @brief Получить текущие настройки RGB-подсветки ++ * ++ * @return Указатель на структуру настроек ++ */ ++RGBBacklightSettings* rgb_backlight_get_settings(void); ++ ++/** ++ * @brief Загрузить настройки подсветки с SD-карты ++ */ ++void rgb_backlight_load_settings(void); ++ ++/** ++ * @brief Сохранить текущие настройки RGB-подсветки ++ */ ++void rgb_backlight_save_settings(void); ++ ++/** ++ * @brief Применить текущие настройки RGB-подсветки ++ * ++ * @param brightness Яркость свечения (0-255) ++ */ ++void rgb_backlight_update(uint8_t brightness); ++ ++/** ++ * @brief Установить цвет RGB-подсветки ++ * ++ * @param color_index Индекс цвета (0 - rgb_backlight_get_color_count()) ++ */ ++void rgb_backlight_set_color(uint8_t color_index); ++ ++/** ++ * @brief Получить количество доступных цветов ++ * ++ * @return Число доступных вариантов цвета ++ */ ++uint8_t rgb_backlight_get_color_count(void); ++ ++/** ++ * @brief Получить текстовое название цвета ++ * ++ * @param index Индекс из доступных вариантов цвета ++ * @return Указатель на строку с названием цвета ++ */ ++const char* rgb_backlight_get_color_text(uint8_t index); +\ No newline at end of file +diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c +index 83e1603b7..cad5b86cb 100644 +--- a/firmware/targets/f7/furi_hal/furi_hal_light.c ++++ b/firmware/targets/f7/furi_hal/furi_hal_light.c +@@ -3,6 +3,7 @@ + #include + #include + #include ++#include + + #define LED_CURRENT_RED 50 + #define LED_CURRENT_GREEN 50 +@@ -31,22 +32,21 @@ void furi_hal_light_init() { + } + + void furi_hal_light_set(Light light, uint8_t value) { +- furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); +- if(light & LightRed) { +- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); +- } +- if(light & LightGreen) { +- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); +- } +- if(light & LightBlue) { +- lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); +- } + if(light & LightBacklight) { +- uint8_t prev = lp5562_get_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelWhite); +- lp5562_execute_ramp( +- &furi_hal_i2c_handle_power, LP5562Engine1, LP5562ChannelWhite, prev, value, 100); ++ rgb_backlight_update(value); ++ } else { ++ furi_hal_i2c_acquire(&furi_hal_i2c_handle_power); ++ if(light & LightRed) { ++ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelRed, value); ++ } ++ if(light & LightGreen) { ++ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelGreen, value); ++ } ++ if(light & LightBlue) { ++ lp5562_set_channel_value(&furi_hal_i2c_handle_power, LP5562ChannelBlue, value); ++ } ++ furi_hal_i2c_release(&furi_hal_i2c_handle_power); + } +- furi_hal_i2c_release(&furi_hal_i2c_handle_power); + } + + void furi_hal_light_blink_start(Light light, uint8_t brightness, uint16_t on_time, uint16_t period) { +diff --git a/lib/drivers/SK6805.c b/lib/drivers/SK6805.c +new file mode 100644 +index 000000000..572e1df97 +--- /dev/null ++++ b/lib/drivers/SK6805.c +@@ -0,0 +1,101 @@ ++/* ++ SK6805 FlipperZero driver ++ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#include "SK6805.h" ++#include ++ ++/* Настройки */ ++#define SK6805_LED_COUNT 3 //Количество светодиодов на плате подсветки ++#define SK6805_LED_PIN &led_pin //Порт подключения светодиодов ++ ++#ifdef FURI_DEBUG ++#define DEBUG_PIN &gpio_ext_pa7 ++#define DEBUG_INIT() \ ++ furi_hal_gpio_init(DEBUG_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh) ++#define DEBUG_SET_HIGH() furi_hal_gpio_write(DEBUG_PIN, true) ++#define DEBUG_SET_LOW() furi_hal_gpio_write(DEBUG_PIN, false) ++#else ++#define DEBUG_INIT() ++#define DEBUG_SET_HIGH() ++#define DEBUG_SET_LOW() ++#endif ++ ++static const GpioPin led_pin = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; ++static uint8_t led_buffer[SK6805_LED_COUNT][3]; ++ ++void SK6805_init(void) { ++ DEBUG_INIT(); ++ furi_hal_gpio_write(SK6805_LED_PIN, false); ++ furi_hal_gpio_init(SK6805_LED_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); ++} ++ ++uint8_t SK6805_get_led_count(void) { ++ return (const uint8_t)SK6805_LED_COUNT; ++} ++void SK6805_set_led_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b) { ++ furi_check(led_index < SK6805_LED_COUNT); ++ ++ led_buffer[led_index][0] = g; ++ led_buffer[led_index][1] = r; ++ led_buffer[led_index][2] = b; ++} ++ ++void SK6805_update(void) { ++ SK6805_init(); ++ furi_kernel_lock(); ++ uint32_t end; ++ /* Последовательная отправка цветов светодиодов */ ++ for(uint8_t lednumber = 0; lednumber < SK6805_LED_COUNT; lednumber++) { ++ //Последовательная отправка цветов светодиода ++ for(uint8_t color = 0; color < 3; color++) { ++ //Последовательная отправка битов цвета ++ uint8_t i = 0b10000000; ++ while(i != 0) { ++ if(led_buffer[lednumber][color] & (i)) { ++ furi_hal_gpio_write(SK6805_LED_PIN, true); ++ DEBUG_SET_HIGH(); ++ end = DWT->CYCCNT + 30; ++ //T1H 600 us (615 us) ++ while(DWT->CYCCNT < end) { ++ } ++ furi_hal_gpio_write(SK6805_LED_PIN, false); ++ DEBUG_SET_LOW(); ++ end = DWT->CYCCNT + 26; ++ //T1L 600 us (587 us) ++ while(DWT->CYCCNT < end) { ++ } ++ } else { ++ furi_hal_gpio_write(SK6805_LED_PIN, true); ++ DEBUG_SET_HIGH(); ++ end = DWT->CYCCNT + 11; ++ //T0H 300 ns (312 ns) ++ while(DWT->CYCCNT < end) { ++ } ++ furi_hal_gpio_write(SK6805_LED_PIN, false); ++ DEBUG_SET_LOW(); ++ end = DWT->CYCCNT + 43; ++ //T0L 900 ns (890 ns) ++ while(DWT->CYCCNT < end) { ++ } ++ } ++ i >>= 1; ++ } ++ } ++ } ++ furi_kernel_unlock(); ++} +diff --git a/lib/drivers/SK6805.h b/lib/drivers/SK6805.h +new file mode 100644 +index 000000000..7c58956fa +--- /dev/null ++++ b/lib/drivers/SK6805.h +@@ -0,0 +1,51 @@ ++/* ++ SK6805 FlipperZero driver ++ Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) ++ ++ This program is free software: you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation, either version 3 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program. If not, see . ++*/ ++ ++#ifndef SK6805_H_ ++#define SK6805_H_ ++ ++#include ++ ++/** ++ * @brief Инициализация линии управления подсветкой ++ */ ++void SK6805_init(void); ++ ++/** ++ * @brief Получить количество светодиодов в подсветке ++ * ++ * @return Количество светодиодов ++ */ ++uint8_t SK6805_get_led_count(void); ++ ++/** ++ * @brief Установить цвет свечения светодиода ++ * ++ * @param led_index номер светодиода (от 0 до SK6805_get_led_count()) ++ * @param r значение красного (0-255) ++ * @param g значение зелёного (0-255) ++ * @param b значение синего (0-255) ++ */ ++void SK6805_set_led_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b); ++ ++/** ++ * @brief Обновление состояния подсветки дисплея ++ */ ++void SK6805_update(void); ++ ++#endif /* SK6805_H_ */ +\ No newline at end of file + +\ No newline at end of file diff --git a/.drone.yml b/.drone.yml index a030635283..c82921c9dc 100644 --- a/.drone.yml +++ b/.drone.yml @@ -31,7 +31,9 @@ steps: - echo '' >> CHANGELOG.md - echo '### [Version without custom animations - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)' >> CHANGELOG.md - echo '' >> CHANGELOG.md - - echo '### [Version with extra apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' >> CHANGELOG.md + - echo '### [Version with RGB patch - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)' >> CHANGELOG.md + - echo '' >> CHANGELOG.md + - echo '## [Version with Extra apps - Install via Web Updater](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' >> CHANGELOG.md environment: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link @@ -56,10 +58,30 @@ steps: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link + - name: "Build with RGB patch" + image: hfdj/fztools + pull: never + commands: + - git apply .ci_files/rgb.patch + - export DIST_SUFFIX=${DRONE_TAG}r + - export WORKFLOW_BRANCH_OR_TAG=release-cfw + - export FORCE_NO_DIRTY=yes + - rm -f build/f7-firmware-C/toolbox/version.* + - ./fbt COMPACT=1 DEBUG=0 updater_package + - mkdir artifacts-rgb-patch + - mv dist/f7-C/* artifacts-rgb-patch/ + - ls -laS artifacts-rgb-patch + - ls -laS artifacts-rgb-patch/f7-update-${DRONE_TAG}r + environment: + FBT_TOOLS_CUSTOM_LINK: + from_secret: fbt_link + - name: "Build with ofw anims" image: hfdj/fztools pull: never commands: + - git clean -df + - git checkout -- . - rm -f assets/dolphin/external/manifest.txt - cp .ci_files/anims_ofw.txt assets/dolphin/external/manifest.txt - rm -rf assets/resources/apps/ @@ -80,16 +102,20 @@ steps: image: kramos/alpine-zip commands: - cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.tgz . + - cp artifacts-rgb-patch/flipper-z-f7-update-${DRONE_TAG}r.tgz . - cp artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.tgz . - cp artifacts-default/flipper-z-f7-update-${DRONE_TAG}.tgz . - zip -r artifacts-extra-apps/flipper-z-f7-update-${DRONE_TAG}e.zip artifacts-extra-apps/f7-update-${DRONE_TAG}e + - zip -r artifacts-rgb-patch/flipper-z-f7-update-${DRONE_TAG}r.zip artifacts-rgb-patch/f7-update-${DRONE_TAG}r - zip -r artifacts-ofw-anims/flipper-z-f7-update-${DRONE_TAG}n.zip artifacts-ofw-anims/f7-update-${DRONE_TAG}n - zip -r artifacts-default/flipper-z-f7-update-${DRONE_TAG}.zip artifacts-default/f7-update-${DRONE_TAG} - tar czpf artifacts-default/flipper-z-any-scripts-${DRONE_TAG}.tgz scripts - rm -rf artifacts-extra-apps/f7-update-${DRONE_TAG} + - rm -rf artifacts-rgb-patch/f7-update-${DRONE_TAG} - rm -rf artifacts-ofw-anims/f7-update-${DRONE_TAG} - rm -rf artifacts-default/f7-update-${DRONE_TAG} - ls -laS artifacts-extra-apps + - ls -laS artifacts-rgb-patch - ls -laS artifacts-ofw-anims - ls -laS artifacts-default - mv artifacts-default/ ${DRONE_TAG} @@ -146,6 +172,21 @@ steps: from_secret: dep_target_extra source: flipper-z-f7-update-${DRONE_TAG}e.tgz + - name: "Upload rgb patch version to updates srv" + image: appleboy/drone-scp:linux-amd64 + settings: + host: + from_secret: dep_host + username: + from_secret: dep_user + password: + from_secret: dep_passwd + port: + from_secret: dep_port + target: + from_secret: dep_target_extra + source: flipper-z-f7-update-${DRONE_TAG}r.tgz + - name: "Do Github release" image: ddplugins/github-release pull: never @@ -160,6 +201,7 @@ steps: - ${DRONE_TAG}/*.zip - artifacts-ofw-anims/*.tgz - artifacts-extra-apps/*.tgz + - artifacts-rgb-patch/*.tgz title: ${DRONE_TAG} note: CHANGELOG.md checksum: @@ -210,7 +252,13 @@ steps: [-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-${DRONE_TAG}n.tgz&channel=release-cfw&version=${DRONE_TAG}n) - [-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}e.tgz&channel=release-cfw&version=${DRONE_TAG}e)" + [-Version with RGB patch - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz&channel=release-cfw&version=${DRONE_TAG}r) + + + [-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}r.tgz) + + + [-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_TAG}e.tgz&channel=release-cfw&version=${DRONE_TAG}e)" document: - ${DRONE_TAG}/flipper-z-f7-update-${DRONE_TAG}.tgz @@ -223,7 +271,7 @@ steps: commands: - wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - chmod +x ./discord.sh - - ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' + - ./discord.sh --text 'New Unleashed firmware released!\n\nVersion - '${DRONE_TAG}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[[Github - Changelog]](https://github.com/DarkFlippers/unleashed-firmware/releases/tag/'${DRONE_TAG}')\n\n[-How to install firmware-](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/HowToInstall.md)\n\n[-Download latest extra apps pack-](https://github.com/xMasterX/all-the-plugins/archive/refs/heads/main.zip)\n\n[-Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/'${DRONE_TAG}'/flipper-z-f7-update-'${DRONE_TAG}'.tgz&channel=release-cfw&version='${DRONE_TAG}')\n\n[-Version without custom animations - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_no_anim/flipper-z-f7-update-'${DRONE_TAG}'n.tgz&channel=release-cfw&version='${DRONE_TAG}'n)\n\n[-Version with RGB patch - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz&channel=release-cfw&version='${DRONE_TAG}'r)\n\n[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'r.tgz)\n\n[-Version with Extra apps - Install FW via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_TAG}'e.tgz&channel=release-cfw&version='${DRONE_TAG}'e)' - name: "Send extra pack build to telegram" image: appleboy/drone-telegram @@ -297,10 +345,29 @@ steps: FBT_TOOLS_CUSTOM_LINK: from_secret: fbt_link + - name: "Build dev with rgb patch" + image: hfdj/fztools + pull: never + commands: + - git apply .ci_files/rgb.patch + - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}r + - export WORKFLOW_BRANCH_OR_TAG=dev-cfw + - export FORCE_NO_DIRTY=yes + - rm -f build/f7-firmware-C/toolbox/version.* + - ./fbt COMPACT=1 DEBUG=0 updater_package + - mkdir artifacts-rgb-patch + - mv dist/f7-C/* artifacts-rgb-patch/ + - ls -laS artifacts-rgb-patch + - ls -laS artifacts-rgb-patch/f7-update-${DRONE_BUILD_NUMBER}r + environment: + FBT_TOOLS_CUSTOM_LINK: + from_secret: fbt_link + - name: "Bundle self-update packages" image: kramos/alpine-zip commands: - cp artifacts-extra-apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz . + - cp artifacts-rgb-patch/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz . - cp artifacts-default/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz . - rm -rf artifacts-default/f7-update-${DRONE_BUILD_NUMBER} - ls -laS artifacts-default @@ -358,6 +425,21 @@ steps: from_secret: dep_target_extra source: flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz + - name: "Upload rgb patch version to updates srv" + image: appleboy/drone-scp:linux-amd64 + settings: + host: + from_secret: dep_host + username: + from_secret: dep_user + password: + from_secret: dep_passwd + port: + from_secret: dep_port + target: + from_secret: dep_target_extra + source: flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz + - name: "Trigger update server reindex" image: hfdj/fztools pull: never @@ -392,6 +474,12 @@ steps: [-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-${DRONE_BUILD_NUMBER}.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}) + [-Version with RGB patch - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}r) + + + [-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz) + + [-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)" - name: "Send build to telegram" @@ -427,7 +515,7 @@ steps: commands: - wget "https://raw.githubusercontent.com/fieu/discord.sh/e1dc1a7595efad2cad8f072f0b3531c470f5b7c8/discord.sh" - chmod +x ./discord.sh - - ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' + - ./discord.sh --text 'Unleashed firmware dev build successful!\n\nBuild - '${DRONE_BUILD_NUMBER}'\n\nCommit - https://github.com/DarkFlippers/unleashed-firmware/commit/'${DRONE_COMMIT_SHA}'\n\n[-> Sponsor our project](https://boosty.to/mmxdev)\n\n[-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'e.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'e)\n\n[-Version with RGB patch - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}'r)\n\n[-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'r.tgz)\n\n[-Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw/dev/flipper-z-f7-update-'${DRONE_BUILD_NUMBER}'.tgz&channel=dev-cfw&version='${DRONE_BUILD_NUMBER}')' trigger: branch: From b16357494dd28e62e158032b622cc04e74bd295a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:31:39 +0300 Subject: [PATCH 018/102] fix branch --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index c82921c9dc..bb7d4fcd9d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -64,7 +64,7 @@ steps: commands: - git apply .ci_files/rgb.patch - export DIST_SUFFIX=${DRONE_TAG}r - - export WORKFLOW_BRANCH_OR_TAG=release-cfw + - export WORKFLOW_BRANCH_OR_TAG=release-cfw-rgb - export FORCE_NO_DIRTY=yes - rm -f build/f7-firmware-C/toolbox/version.* - ./fbt COMPACT=1 DEBUG=0 updater_package @@ -351,7 +351,7 @@ steps: commands: - git apply .ci_files/rgb.patch - export DIST_SUFFIX=${DRONE_BUILD_NUMBER}r - - export WORKFLOW_BRANCH_OR_TAG=dev-cfw + - export WORKFLOW_BRANCH_OR_TAG=dev-cfw-rgb - export FORCE_NO_DIRTY=yes - rm -f build/f7-firmware-C/toolbox/version.* - ./fbt COMPACT=1 DEBUG=0 updater_package From 73d4fc6d2fa0f65fc050e525157688e6abb8de25 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:35:43 +0300 Subject: [PATCH 019/102] Fix E --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index bb7d4fcd9d..f7c6d07dee 100644 --- a/.drone.yml +++ b/.drone.yml @@ -480,7 +480,7 @@ steps: [-Version with RGB patch - Direct download-](https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}r.tgz) - [-Version with extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)" + [-Version with Extra apps - Install via Web Updater-](https://lab.flipper.net/?url=https://unleashedflip.com/fw_extra_apps/flipper-z-f7-update-${DRONE_BUILD_NUMBER}e.tgz&channel=dev-cfw&version=${DRONE_BUILD_NUMBER}e)" - name: "Send build to telegram" image: appleboy/drone-telegram From 14fbc3648edbc9b4bcafb26bf990234b01b72541 Mon Sep 17 00:00:00 2001 From: ClaraCrazy Date: Sun, 4 Jun 2023 16:36:17 +0200 Subject: [PATCH 020/102] move support up --- .github/workflow_data/release.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index 77316bae78..f540953235 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -1,3 +1,14 @@ +## ❤️ Support +If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! + +- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. +- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time +- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time +- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time +- **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` + +**Thanks for all your support <3** + ## ⬇️ Download >### [🖥️ Web Updater (chrome)](https://flipper-xtre.me/update) [recommended] @@ -17,14 +28,3 @@ **If you have any of the above issues, please re-download and re-install!** --> - -## ❤️ Support -If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! - -- **[Patreon](https://patreon.com/CynthiaLabs)**: ❤️ Account needed, subscription with perks across my entire org. -- **[Wire-transfer](https://bunq.me/ClaraK)**: No account needed, one-time -- **[Paypal](https://paypal.me/RdX2020)**: Account needed, one-time -- **[ko-fi](https://ko-fi.com/cynthialabs)**: No account needed, one-time -- **Monero**: `41kyWeeoVdK4quzQ4M9ikVGs6tCQCLfdx8jLExTNsAu2SF1QAyDqRdjfGM6EL8L9NpXwt89HJeAoGf1aoArk7nDr4AMMV4T` - -**Thanks for all your support <3** From b5aa4afd9f53e31c557b31719a19adc390900860 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 15:40:52 +0100 Subject: [PATCH 021/102] Move download up and fix hotfix --- .github/workflow_data/hotfix.py | 2 +- .github/workflow_data/release.md | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflow_data/hotfix.py b/.github/workflow_data/hotfix.py index b35cf7a073..5d09d9f9cd 100644 --- a/.github/workflow_data/hotfix.py +++ b/.github/workflow_data/hotfix.py @@ -82,6 +82,6 @@ print(f"{req.url = }\n{req.status_code = }\n{req.content = }") sys.exit(1) - changelog = body.split("## 🚀 Changelog", 1)[1].rsplit("## ❤️ Support", 1)[0] + changelog = body.split("## 🚀 Changelog", 1)[1] with open(os.environ["ARTIFACT_TGZ"].removesuffix(".tgz") + ".md", "w") as f: f.write(changelog.strip() + "\n\n") diff --git a/.github/workflow_data/release.md b/.github/workflow_data/release.md index f540953235..7128cb4549 100644 --- a/.github/workflow_data/release.md +++ b/.github/workflow_data/release.md @@ -1,3 +1,12 @@ +## ⬇️ Download +>### [🖥️ Web Updater (chrome)](https://flipper-xtre.me/update) [recommended] + +>### [🐬 qFlipper Package (.tgz)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_TGZ}) + +>### [📦 Zipped Archive (.zip)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_ZIP}) + +**Check the [install guide](https://github.com/ClaraCrazy/Flipper-Xtreme#install) if you're not sure, or [join our Discord](https://discord.gg/flipper-xtreme) if you have questions or encounter issues!** + ## ❤️ Support If you like what you're seeing, **please consider donating to us**. We won't ever put this behind a paywall, but we'd still appreciate a few bucks! @@ -9,15 +18,6 @@ If you like what you're seeing, **please consider donating to us**. We won't eve **Thanks for all your support <3** -## ⬇️ Download ->### [🖥️ Web Updater (chrome)](https://flipper-xtre.me/update) [recommended] - ->### [🐬 qFlipper Package (.tgz)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_TGZ}) - ->### [📦 Zipped Archive (.zip)](https://github.com/ClaraCrazy/Flipper-Xtreme/releases/download/{VERSION_TAG}/{ARTIFACT_ZIP}) - -**Check the [install guide](https://github.com/ClaraCrazy/Flipper-Xtreme#install) if you're not sure, or [join our Discord](https://discord.gg/flipper-xtreme) if you have questions or encounter issues!** - ## 🚀 Changelog {CHANGELOG} From 945bc46837ed39707ca4ffb5c7a37bee759758d8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:09:37 +0100 Subject: [PATCH 022/102] Archive fix unpinning last item --- .../main/archive/helpers/archive_favorites.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index c7b058235e..799b29034d 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -112,7 +112,9 @@ static bool archive_favourites_rescan() { furi_string_free(buffer); storage_file_close(file); - storage_common_move(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(storage, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(storage, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -215,7 +217,9 @@ bool archive_favorites_delete(const char* format, ...) { furi_string_free(filename); storage_file_close(file); - storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -298,7 +302,9 @@ bool archive_favorites_rename(const char* src, const char* dst) { furi_string_free(path); storage_file_close(file); - storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -324,7 +330,9 @@ void archive_favorites_save(void* context) { archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", furi_string_get_cstr(item->path)); } - storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH); + if(storage_common_move(fs_api, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH) == FSE_NOT_EXIST) { + storage_common_remove(fs_api, ARCHIVE_FAV_PATH); + } storage_file_free(file); furi_record_close(RECORD_STORAGE); From f2ce2ad95c8eb4540758c3140b7997af36e95b53 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:25:35 +0100 Subject: [PATCH 023/102] Archive fix internal and external browsers --- .../main/archive/helpers/archive_browser.c | 22 ++++++++----------- .../main/archive/helpers/archive_browser.h | 16 +++++++------- .../main/archive/scenes/archive_scene_info.c | 5 ----- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 15c9d97f42..57219e05ba 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -449,7 +449,8 @@ void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { } static bool archive_is_dir_exists(FuriString* path) { - if(furi_string_equal(path, STORAGE_ANY_PATH_PREFIX)) { + if(furi_string_equal(path, STORAGE_INT_PATH_PREFIX) || + furi_string_equal(path, STORAGE_EXT_PATH_PREFIX)) { return true; } bool state = false; @@ -475,13 +476,6 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { } else { tab = (tab + 1) % ArchiveTabTotal; } - if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; - } - } browser->is_root = true; archive_set_tab(browser, tab); @@ -502,18 +496,20 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { } else { tab = archive_get_tab(browser); if(archive_is_dir_exists(browser->path)) { - bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; + bool is_browser = !strcmp(archive_get_tab_ext(tab), "*"); + bool skip_assets = is_browser; // Hide dot files everywhere except Browser if in debug mode - bool hide_dot_files = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? - !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) : - true; + bool hide_dot_files = !is_browser ? true : + tab == ArchiveTabInternal ? + false : + !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); archive_file_browser_set_path( browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files); tab_empty = false; // Empty check will be performed later } } - if((tab_empty) && (tab != ArchiveTabBrowser)) { + if((tab_empty) && (tab != ArchiveTabBrowser) && (tab != ArchiveTabInternal)) { archive_switch_tab(browser, key); } else { with_view_model( diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index ec3ef00cea..dfc572a2b1 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -9,16 +9,16 @@ static const char* tab_default_paths[] = { [ArchiveTabFavorites] = "/app:favorites", - [ArchiveTabIButton] = ANY_PATH("ibutton"), - [ArchiveTabNFC] = ANY_PATH("nfc"), - [ArchiveTabSubGhz] = ANY_PATH("subghz"), - [ArchiveTabLFRFID] = ANY_PATH("lfrfid"), - [ArchiveTabInfrared] = ANY_PATH("infrared"), - [ArchiveTabBadKb] = ANY_PATH("badkb"), + [ArchiveTabIButton] = EXT_PATH("ibutton"), + [ArchiveTabNFC] = EXT_PATH("nfc"), + [ArchiveTabSubGhz] = EXT_PATH("subghz"), + [ArchiveTabLFRFID] = EXT_PATH("lfrfid"), + [ArchiveTabInfrared] = EXT_PATH("infrared"), + [ArchiveTabBadKb] = EXT_PATH("badkb"), [ArchiveTabU2f] = "/app:u2f", - [ArchiveTabApplications] = ANY_PATH("apps"), + [ArchiveTabApplications] = EXT_PATH("apps"), [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, - [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, + [ArchiveTabBrowser] = STORAGE_EXT_PATH_PREFIX, }; static const char* known_ext[] = { diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 07b5aeadb5..6a470ffaff 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -38,11 +38,6 @@ void archive_scene_info_on_enter(void* context) { // Directory path path_extract_dirname(furi_string_get_cstr(current->path), dirname); - if(strcmp(furi_string_get_cstr(dirname), "/any") == 0) { - furi_string_replace(dirname, STORAGE_ANY_PATH_PREFIX, "/"); - } else { - furi_string_replace(dirname, STORAGE_ANY_PATH_PREFIX, ""); - } // File size FileInfo fileinfo; From 5cc5115a186aef193fba8f3c87bb790c84e016c8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:27:33 +0100 Subject: [PATCH 024/102] Archive fix bad file sizes --- applications/main/archive/scenes/archive_scene_info.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 6a470ffaff..6f8d40a568 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -41,8 +41,13 @@ void archive_scene_info_on_enter(void* context) { // File size FileInfo fileinfo; - storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo); - if(fileinfo.size <= 1024) { + if(storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo) != FSE_OK) { + snprintf( + file_info_message, + sizeof(file_info_message), + "Size: \e#N/A\e#\n%s", + furi_string_get_cstr(dirname)); + } else if(fileinfo.size <= 1024) { furi_string_printf(str_size, "%lld", fileinfo.size); snprintf( file_info_message, From 2b2708e635fae366f99d3958ff0b71199dda7c68 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:28:41 +0100 Subject: [PATCH 025/102] Archive fix favorites, u2f and app tabs --- .../main/archive/helpers/archive_apps.c | 13 +++---- .../main/archive/helpers/archive_browser.c | 8 ++--- .../main/archive/views/archive_browser_view.c | 34 ++++++++++--------- .../main/archive/views/archive_browser_view.h | 1 + 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/applications/main/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index c8ad67625f..b7037e6b70 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -1,6 +1,7 @@ #include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" +#include static const char* known_apps[] = { [ArchiveAppTypeU2f] = "u2f", @@ -28,13 +29,9 @@ bool archive_app_is_available(void* context, const char* path) { ArchiveAppTypeEnum app = archive_get_app_type(path); if(app == ArchiveAppTypeU2f) { - bool file_exists = false; Storage* storage = furi_record_open(RECORD_STORAGE); - - if(storage_file_exists(storage, ANY_PATH("u2f/key.u2f"))) { - file_exists = storage_file_exists(storage, ANY_PATH("u2f/cnt.u2f")); - } - + bool file_exists = storage_file_exists(storage, U2F_KEY_FILE) && + storage_file_exists(storage, U2F_CNT_FILE); furi_record_close(RECORD_STORAGE); return file_exists; } else { @@ -69,8 +66,8 @@ void archive_app_delete_file(void* context, const char* path) { if(app == ArchiveAppTypeU2f) { Storage* fs_api = furi_record_open(RECORD_STORAGE); - res = (storage_common_remove(fs_api, ANY_PATH("u2f/key.u2f")) == FSE_OK); - res |= (storage_common_remove(fs_api, ANY_PATH("u2f/cnt.u2f")) == FSE_OK); + res = (storage_common_remove(fs_api, U2F_KEY_FILE) == FSE_OK); + res |= (storage_common_remove(fs_api, U2F_CNT_FILE) == FSE_OK); furi_record_close(RECORD_STORAGE); if(archive_is_favorite("/app:u2f/U2F Token")) { diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 57219e05ba..b0781d8430 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -482,11 +482,10 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { furi_string_set(browser->path, archive_get_default_path(tab)); bool tab_empty = true; + bool is_app_tab = furi_string_start_with_str(browser->path, "/app:"); if(tab == ArchiveTabFavorites) { - if(archive_favorites_count(browser) > 0) { - tab_empty = false; - } - } else if(furi_string_start_with_str(browser->path, "/app:")) { + tab_empty = false; + } else if(is_app_tab) { char* app_name = strchr(furi_string_get_cstr(browser->path), ':'); if(app_name != NULL) { if(archive_app_is_available(browser, furi_string_get_cstr(browser->path))) { @@ -518,6 +517,7 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { { model->item_idx = 0; model->array_offset = 0; + model->is_app_tab = is_app_tab; }, false); archive_get_items(browser, furi_string_get_cstr(browser->path)); diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index d4a52ba9e2..33e1eace61 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -64,25 +64,27 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { FuriString* item_new_dir = furi_string_alloc_set("New Dir"); FuriString* item_rename = furi_string_alloc_set("Rename"); FuriString* item_delete = furi_string_alloc_set("Delete"); - if(model->clipboard != NULL) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_paste, - ArchiveBrowserEventFileMenuPaste); - } else if(selected) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_cut, - ArchiveBrowserEventFileMenuCut); + if(!model->is_app_tab) { + if(model->clipboard != NULL) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_paste, + ArchiveBrowserEventFileMenuPaste); + } else if(selected) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_cut, + ArchiveBrowserEventFileMenuCut); + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_copy, + ArchiveBrowserEventFileMenuCopy); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_copy, - ArchiveBrowserEventFileMenuCopy); + item_new_dir, + ArchiveBrowserEventFileMenuNewDir); } - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_new_dir, - ArchiveBrowserEventFileMenuNewDir); if(selected) { if(!selected->is_app) { archive_menu_add_item( diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 1b4c0f5259..ed18df3876 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -101,6 +101,7 @@ typedef struct { bool clipboard_copy; menu_array_t context_menu; + bool is_app_tab; bool move_fav; bool list_loading; bool folder_loading; From cfa75bb7125a2817b852cc9e717ab615a2694182 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 17:31:34 +0100 Subject: [PATCH 026/102] Archive favorites loop cleanup --- .../main/archive/helpers/archive_favorites.c | 30 ++++--------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index 799b29034d..7e32292839 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -59,10 +59,7 @@ uint16_t archive_favorites_count(void* context) { uint16_t lines = 0; if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; // Skip empty lines } @@ -87,10 +84,7 @@ static bool archive_favourites_rescan() { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -141,10 +135,7 @@ bool archive_favorites_read(void* context) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -199,10 +190,7 @@ bool archive_favorites_delete(const char* format, ...) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -243,10 +231,7 @@ bool archive_is_favorite(const char* format, ...) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } @@ -283,10 +268,7 @@ bool archive_favorites_rename(const char* src, const char* dst) { bool result = storage_file_open(file, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING); if(result) { - while(1) { - if(!archive_favorites_read_line(file, buffer)) { - break; - } + while(archive_favorites_read_line(file, buffer)) { if(!furi_string_size(buffer)) { continue; } From b63fe76a71805a217d0d66f624bc3c1e982dc9bc Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 18:54:39 +0100 Subject: [PATCH 027/102] Properly hide favs tab when no favs are set --- applications/main/archive/helpers/archive_browser.c | 4 +++- applications/main/archive/helpers/archive_favorites.c | 4 +--- applications/main/archive/helpers/archive_favorites.h | 2 +- applications/main/archive/scenes/archive_scene_browser.c | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index b0781d8430..43ab8c9505 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -484,7 +484,9 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { bool tab_empty = true; bool is_app_tab = furi_string_start_with_str(browser->path, "/app:"); if(tab == ArchiveTabFavorites) { - tab_empty = false; + if(archive_favorites_count() > 0) { + tab_empty = false; + } } else if(is_app_tab) { char* app_name = strchr(furi_string_get_cstr(browser->path), ':'); if(app_name != NULL) { diff --git a/applications/main/archive/helpers/archive_favorites.c b/applications/main/archive/helpers/archive_favorites.c index 7e32292839..7efeeb4d19 100644 --- a/applications/main/archive/helpers/archive_favorites.c +++ b/applications/main/archive/helpers/archive_favorites.c @@ -46,9 +46,7 @@ static bool archive_favorites_read_line(File* file, FuriString* str_result) { return result; } -uint16_t archive_favorites_count(void* context) { - furi_assert(context); - +uint16_t archive_favorites_count() { Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); diff --git a/applications/main/archive/helpers/archive_favorites.h b/applications/main/archive/helpers/archive_favorites.h index e45af92e74..dfeb18e763 100644 --- a/applications/main/archive/helpers/archive_favorites.h +++ b/applications/main/archive/helpers/archive_favorites.h @@ -6,7 +6,7 @@ #define ARCHIVE_FAV_PATH CFG_PATH("favorites.txt") #define ARCHIVE_FAV_TEMP_PATH CFG_PATH("favorites.tmp") -uint16_t archive_favorites_count(void* context); +uint16_t archive_favorites_count(); bool archive_favorites_read(void* context); bool archive_favorites_delete(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); bool archive_is_favorite(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index bf9079be4b..0854c2d3fa 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -77,6 +77,9 @@ void archive_scene_browser_on_enter(void* context) { browser->is_root = true; archive_browser_set_callback(browser, archive_scene_browser_callback, archive); + if(archive_get_tab(browser) == ArchiveTabFavorites && archive_favorites_count() < 1) { + archive_switch_tab(browser, TAB_LEFT); + } archive_update_focus(browser, archive->text_store); view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewBrowser); From 82ad081a481356d705fdb0316b7d1e2fbaced19f Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 22:01:14 +0100 Subject: [PATCH 028/102] Fix hiding assets in archive --- applications/main/archive/helpers/archive_browser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 43ab8c9505..aaef1c9e53 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -498,7 +498,7 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { tab = archive_get_tab(browser); if(archive_is_dir_exists(browser->path)) { bool is_browser = !strcmp(archive_get_tab_ext(tab), "*"); - bool skip_assets = is_browser; + bool skip_assets = !is_browser; // Hide dot files everywhere except Browser if in debug mode bool hide_dot_files = !is_browser ? true : tab == ArchiveTabInternal ? From b89f93e9a3452bdfb3a6df16f7e8b875279f171a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 22:57:31 +0100 Subject: [PATCH 029/102] Archive fix actions menu retardedness --- .../main/archive/helpers/archive_browser.h | 2 +- .../main/archive/scenes/archive_scene_info.c | 3 +- .../main/archive/views/archive_browser_view.c | 88 ++++--------------- 3 files changed, 22 insertions(+), 71 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index dfc572a2b1..c2d4c59f8d 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -62,7 +62,7 @@ static inline const char* archive_get_default_path(ArchiveTabEnum tab) { } inline bool archive_is_known_app(ArchiveFileTypeEnum type) { - return (type != ArchiveFileTypeUnknown); + return (type < ArchiveFileTypeUnknown); } bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx); diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 6f8d40a568..7af7d0db53 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -41,7 +41,8 @@ void archive_scene_info_on_enter(void* context) { // File size FileInfo fileinfo; - if(storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo) != FSE_OK) { + if(storage_common_stat(fs_api, furi_string_get_cstr(current->path), &fileinfo) != FSE_OK || + file_info_is_dir(&fileinfo)) { snprintf( file_info_message, sizeof(file_info_message), diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 33e1eace61..6495ad76fc 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -108,97 +108,47 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { FuriString* item_pin = furi_string_alloc_set("Pin"); FuriString* item_info = furi_string_alloc_set("Info"); FuriString* item_show = furi_string_alloc_set("Show"); + FuriString* item_move = furi_string_alloc_set("Move"); if(selected->fav || favorites) { furi_string_set(item_pin, "Unpin"); } - if(favorites) { - //FURI_LOG_D(TAG, "ArchiveTabFavorites"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); + if(archive_is_known_app(selected->type)) { + if(selected->type != ArchiveFileTypeFolder) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_run, + ArchiveBrowserEventFileMenuRun); + } archive_menu_add_item( menu_array_push_raw(model->context_menu), item_pin, ArchiveBrowserEventFileMenuPin); - if(selected->type <= ArchiveFileTypeBadKb) { + } + if(!selected->is_app) { + archive_menu_add_item( + menu_array_push_raw(model->context_menu), + item_info, + ArchiveBrowserEventFileMenuInfo); + if(selected->type != ArchiveFileTypeFolder) { archive_menu_add_item( menu_array_push_raw(model->context_menu), item_show, ArchiveBrowserEventFileMenuShow); } - furi_string_set(item_info, "Move"); + } + if(favorites) { archive_menu_add_item( menu_array_push_raw(model->context_menu), - item_info, + item_move, ArchiveBrowserEventFileMenuRename); - } else { - if(selected->type == ArchiveFileTypeFolder) { - //FURI_LOG_D(TAG, "Directory type"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - } else if(!archive_is_known_app(selected->type)) { - //FURI_LOG_D(TAG, "Unknown type"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); - if(selected->is_text_file) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - } else if(selected->is_app) { - //FURI_LOG_D(TAG, "3 types"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); - if(selected->type <= ArchiveFileTypeBadKb) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - } else { - //FURI_LOG_D(TAG, "All menu"); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_run, - ArchiveBrowserEventFileMenuRun); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_pin, - ArchiveBrowserEventFileMenuPin); - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_info, - ArchiveBrowserEventFileMenuInfo); - if(selected->type <= ArchiveFileTypeBadKb) { - archive_menu_add_item( - menu_array_push_raw(model->context_menu), - item_show, - ArchiveBrowserEventFileMenuShow); - } - } } furi_string_free(item_run); furi_string_free(item_pin); furi_string_free(item_info); furi_string_free(item_show); + furi_string_free(item_move); } } /*else { FURI_LOG_D(TAG, "menu_array_size already set: %d", menu_array_size(model->context_menu)); From 90f06037bf1111736337c451880323ad325ed5e5 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:30:24 +0100 Subject: [PATCH 030/102] Fix xtreme app code naming consistency --- .../xtreme_app_scene_interface_common.c | 5 ++-- .../xtreme_app_scene_interface_graphics.c | 1 + .../xtreme_app_scene_interface_lockscreen.c | 28 +++++++++++-------- .../xtreme_app_scene_interface_mainmenu.c | 2 +- .../scenes/xtreme_app_scene_protocols.c | 12 +++++--- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c index 54b0241d97..b0f5a1d4d7 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c @@ -4,6 +4,7 @@ enum VarItemListIndex { VarItemListIndexSortDirsFirst, VarItemListIndexDarkMode, VarItemListIndexLeftHanded, + VarItemListIndexFavoriteTimeout, }; void xtreme_app_scene_interface_common_var_item_list_callback(void* context, uint32_t index) { @@ -27,7 +28,7 @@ static void xtreme_app_scene_interface_common_dark_mode_changed(VariableItem* it app->save_settings = true; } -static void xtreme_app_scene_interface_common_left_handed_changed(VariableItem* item) { +static void xtreme_app_scene_interface_common_hand_orient_changed(VariableItem* item) { bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); if(value) { @@ -71,7 +72,7 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { var_item_list, "Left Handed", 2, - xtreme_app_scene_interface_common_left_handed_changed, + xtreme_app_scene_interface_common_hand_orient_changed, app); bool value = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient); variable_item_set_current_value_index(item, value); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c index 7b0d54b39e..5b016815f9 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_graphics.c @@ -5,6 +5,7 @@ enum VarItemListIndex { VarItemListIndexAnimSpeed, VarItemListIndexCycleAnims, VarItemListIndexUnlockAnims, + VarItemListIndexFallbackAnim, }; void xtreme_app_scene_interface_graphics_var_item_list_callback(void* context, uint32_t index) { diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c index 5ae21cb3ff..632401a7c2 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_lockscreen.c @@ -1,8 +1,13 @@ #include "../xtreme_app.h" enum VarItemListIndex { - VarItemListIndexShowClock, + VarItemListIndexLockOnBoot, + VarItemListIndexFormatOn10BadPins, + VarItemListIndexShowTime, + VarItemListIndexShowSeconds, VarItemListIndexShowDate, + VarItemListIndexShowStatusbar, + VarItemListIndexUnlockPrompt, }; void xtreme_app_scene_interface_lockscreen_var_item_list_callback(void* context, uint32_t index) { @@ -26,7 +31,7 @@ static void xtreme_app_scene_interface_lockscreen_bad_pins_format_changed(Variab app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_time_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_time_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -34,7 +39,7 @@ static void xtreme_app_scene_interface_lockscreen_show_time_changed(VariableItem app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_seconds_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_seconds_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -42,7 +47,7 @@ static void xtreme_app_scene_interface_lockscreen_show_seconds_changed(VariableI app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_date_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_date_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -50,7 +55,8 @@ static void xtreme_app_scene_interface_lockscreen_show_date_changed(VariableItem app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_show_statusbar_changed(VariableItem* item) { +static void + xtreme_app_scene_interface_lockscreen_lockscreen_statusbar_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -58,7 +64,7 @@ static void xtreme_app_scene_interface_lockscreen_show_statusbar_changed(Variabl app->save_settings = true; } -static void xtreme_app_scene_interface_lockscreen_unlock_prompt_changed(VariableItem* item) { +static void xtreme_app_scene_interface_lockscreen_lockscreen_prompt_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -94,7 +100,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Time", 2, - xtreme_app_scene_interface_lockscreen_show_time_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_time_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_time); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_time ? "ON" : "OFF"); @@ -103,7 +109,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Seconds", 2, - xtreme_app_scene_interface_lockscreen_show_seconds_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_seconds_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_seconds); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_seconds ? "ON" : "OFF"); @@ -112,7 +118,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Date", 2, - xtreme_app_scene_interface_lockscreen_show_date_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_date_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_date); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_date ? "ON" : "OFF"); @@ -121,7 +127,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Show Statusbar", 2, - xtreme_app_scene_interface_lockscreen_show_statusbar_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_statusbar_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_statusbar); variable_item_set_current_value_text( @@ -131,7 +137,7 @@ void xtreme_app_scene_interface_lockscreen_on_enter(void* context) { var_item_list, "Unlock Prompt", 2, - xtreme_app_scene_interface_lockscreen_unlock_prompt_changed, + xtreme_app_scene_interface_lockscreen_lockscreen_prompt_changed, app); variable_item_set_current_value_index(item, xtreme_settings->lockscreen_prompt); variable_item_set_current_value_text(item, xtreme_settings->lockscreen_prompt ? "ON" : "OFF"); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c index 8d4a6185e4..4a5b5779d4 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_mainmenu.c @@ -1,7 +1,7 @@ #include "../xtreme_app.h" enum VarItemListIndex { - VarItemListIndexWiiMenu, + VarItemListIndexMenuStyle, VarItemListIndexApp, VarItemListIndexRemoveApp, VarItemListIndexAddApp, diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c index 28b97618e0..b1322b0dad 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -12,7 +12,7 @@ void xtreme_app_scene_protocols_var_item_list_callback(void* context, uint32_t i view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_badkb_mode_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_bad_bt_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "BT" : "USB"); @@ -20,7 +20,7 @@ static void xtreme_app_scene_protocols_badkb_mode_changed(VariableItem* item) { app->save_settings = true; } -static void xtreme_app_scene_protocols_badbt_remember_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_bad_bt_remember_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -42,12 +42,16 @@ void xtreme_app_scene_protocols_on_enter(void* context) { VariableItem* item; item = variable_item_list_add( - var_item_list, "BadKB Mode", 2, xtreme_app_scene_protocols_badkb_mode_changed, app); + var_item_list, "BadKB Mode", 2, xtreme_app_scene_protocols_bad_bt_changed, app); variable_item_set_current_value_index(item, xtreme_settings->bad_bt); variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB"); item = variable_item_list_add( - var_item_list, "BadBT Remember", 2, xtreme_app_scene_protocols_badbt_remember_changed, app); + var_item_list, + "BadBT Remember", + 2, + xtreme_app_scene_protocols_bad_bt_remember_changed, + app); variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember); variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF"); From f292bbe4f63c95383326a8394f09a67eb33d18e8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:33:38 +0100 Subject: [PATCH 031/102] More precision for frequency editor + change text --- .../scenes/xtreme_app_scene_protocols_frequencies_add.c | 4 ++-- applications/main/xtreme_app/xtreme_app.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c index 88e4211475..a9efa8b541 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c @@ -9,7 +9,7 @@ static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* XtremeApp* app = context; char* end; - uint32_t value = strtol(app->subghz_freq_buffer, &end, 0) * 10000; + uint32_t value = strtol(app->subghz_freq_buffer, &end, 0) * 1000; if(*end || !furi_hal_subghz_is_frequency_valid(value)) { view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultError); return; @@ -29,7 +29,7 @@ void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) { XtremeApp* app = context; TextInput* text_input = app->text_input; - text_input_set_header_text(text_input, "Format: 12356"); + text_input_set_header_text(text_input, "Ex: 123456 for 123.456 MHz"); strlcpy(app->subghz_freq_buffer, "", XTREME_SUBGHZ_FREQ_BUFFER_SIZE); diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index d02c5e9f08..ce50facdde 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -25,7 +25,7 @@ #include #include -#define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 6 +#define XTREME_SUBGHZ_FREQ_BUFFER_SIZE 7 ARRAY_DEF(CharList, char*) From 5c0b07c7b452fb49d2e8a94710d04307ac3aedb4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:34:50 +0100 Subject: [PATCH 032/102] Retry when failing to re-invoke first slideshow --- .../xtreme_app/scenes/xtreme_app_scene_start.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c index fabd7ce6cc..7cb1153df5 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c @@ -48,12 +48,15 @@ bool xtreme_app_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc); break; case VarItemListIndexVersion: { - if(storage_common_copy( - furi_record_open(RECORD_STORAGE), - EXT_PATH("dolphin/xfwfirstboot.bin"), - EXT_PATH(".slideshow"))) { - app->show_slideshow = true; - xtreme_app_apply(app); + for(int i = 0; i < 10; i++) { + if(storage_common_copy( + furi_record_open(RECORD_STORAGE), + EXT_PATH("dolphin/xfwfirstboot.bin"), + EXT_PATH(".slideshow"))) { + app->show_slideshow = true; + xtreme_app_apply(app); + break; + } } break; } From 2c1834f1f3ca9d4b703e53ecab654fd84aa0563d Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:58:05 +0100 Subject: [PATCH 033/102] Add submenu indicator to xtreme app --- .../scenes/xtreme_app_scene_interface.c | 20 ++++++++++++++----- .../scenes/xtreme_app_scene_protocols.c | 3 ++- .../xtreme_app_scene_protocols_frequencies.c | 6 ++++-- .../scenes/xtreme_app_scene_start.c | 13 +++++++++--- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c index a033e47464..5e04fc4b8f 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c @@ -16,12 +16,22 @@ void xtreme_app_scene_interface_var_item_list_callback(void* context, uint32_t i void xtreme_app_scene_interface_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; - variable_item_list_add(var_item_list, "Graphics", 0, NULL, app); - variable_item_list_add(var_item_list, "Mainmenu", 0, NULL, app); - variable_item_list_add(var_item_list, "Lockscreen", 0, NULL, app); - variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app); - variable_item_list_add(var_item_list, "Common", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Graphics", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Mainmenu", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Lockscreen", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Common", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_interface_var_item_list_callback, app); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c index b1322b0dad..497d3f1ecb 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -55,7 +55,8 @@ void xtreme_app_scene_protocols_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember); variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF"); - variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); item = variable_item_list_add( var_item_list, "SubGHz Extend", 2, xtreme_app_scene_protocols_subghz_extend_changed, app); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c index 6b92d1eb01..668fe83272 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c @@ -33,9 +33,11 @@ void xtreme_app_scene_protocols_frequencies_on_enter(void* context) { variable_item_set_current_value_index(item, app->subghz_use_defaults); variable_item_set_current_value_text(item, app->subghz_use_defaults ? "ON" : "OFF"); - variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); - variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_protocols_frequencies_var_item_list_callback, app); diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c index 7cb1153df5..1f1c1de707 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_start.c @@ -15,10 +15,17 @@ void xtreme_app_scene_start_var_item_list_callback(void* context, uint32_t index void xtreme_app_scene_start_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add(var_item_list, "Interface", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Protocols", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + + item = variable_item_list_add(var_item_list, "Misc", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); - variable_item_list_add(var_item_list, "Interface", 0, NULL, app); - variable_item_list_add(var_item_list, "Protocols", 0, NULL, app); - variable_item_list_add(var_item_list, "Misc", 0, NULL, app); variable_item_list_add(var_item_list, furi_string_get_cstr(app->version_tag), 0, NULL, app); variable_item_list_set_enter_callback( From 14a0fd0a314c7814460c26f557de4450f7b3bc1a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 4 Jun 2023 23:58:46 +0100 Subject: [PATCH 034/102] Move screen options to own section --- .../scenes/xtreme_app_scene_config.h | 1 + .../xtreme_app_scene_interface_common.c | 35 ----- .../xtreme_app/scenes/xtreme_app_scene_misc.c | 59 +------- .../scenes/xtreme_app_scene_misc_screen.c | 130 ++++++++++++++++++ 4 files changed, 138 insertions(+), 87 deletions(-) create mode 100644 applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h index dfe115522e..23459cd150 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -12,4 +12,5 @@ ADD_SCENE(xtreme_app, protocols_frequencies_static, ProtocolsFrequenciesStatic) ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper) ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd) ADD_SCENE(xtreme_app, misc, Misc) +ADD_SCENE(xtreme_app, misc_screen, MiscScreen) ADD_SCENE(xtreme_app, misc_rename, MiscRename) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c index b0f5a1d4d7..3efa4fffa7 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c @@ -2,8 +2,6 @@ enum VarItemListIndex { VarItemListIndexSortDirsFirst, - VarItemListIndexDarkMode, - VarItemListIndexLeftHanded, VarItemListIndexFavoriteTimeout, }; @@ -20,24 +18,6 @@ static void xtreme_app_scene_interface_common_sort_dirs_first_changed(VariableIt app->save_settings = true; } -static void xtreme_app_scene_interface_common_dark_mode_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - XTREME_SETTINGS()->dark_mode = value; - app->save_settings = true; -} - -static void xtreme_app_scene_interface_common_hand_orient_changed(VariableItem* item) { - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - if(value) { - furi_hal_rtc_set_flag(FuriHalRtcFlagHandOrient); - } else { - furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); - } -} - static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint32_t value = variable_item_get_current_value_index(item); @@ -63,21 +43,6 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first); variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF"); - item = variable_item_list_add( - var_item_list, "Dark Mode", 2, xtreme_app_scene_interface_common_dark_mode_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->dark_mode); - variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, - "Left Handed", - 2, - xtreme_app_scene_interface_common_hand_orient_changed, - app); - bool value = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient); - variable_item_set_current_value_index(item, value); - variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - item = variable_item_list_add( var_item_list, "Favorite Timeout", diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c index 84bb378869..62f9af2a51 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c @@ -1,13 +1,12 @@ #include "../xtreme_app.h" enum VarItemListIndex { + VarItemListIndexScreen, VarItemListIndexChangeDeviceName, VarItemListIndexDolphinLevel, VarItemListIndexDolphinAngry, VarItemListIndexButthurtTimer, VarItemListIndexChargeCap, - VarItemListIndexRgbBacklight, - VarItemListIndexLcdColor, }; void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) { @@ -57,15 +56,6 @@ static void xtreme_app_scene_misc_charge_cap_changed(VariableItem* item) { app->save_settings = true; } -static void xtreme_app_scene_misc_lcd_color_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, rgb_backlight_get_color_text(index)); - rgb_backlight_set_color(index); - app->save_backlight = true; - notification_message(app->notification, &sequence_display_backlight_on); -} - void xtreme_app_scene_misc_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); @@ -73,6 +63,9 @@ void xtreme_app_scene_misc_on_enter(void* context) { VariableItem* item; uint8_t value_index; + item = variable_item_list_add(var_item_list, "Screen", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); + variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app); char level_str[4]; @@ -120,20 +113,6 @@ void xtreme_app_scene_misc_on_enter(void* context) { variable_item_set_current_value_index(item, value_index - 1); variable_item_set_current_value_text(item, cap_str); - item = variable_item_list_add(var_item_list, "RGB Backlight", 1, NULL, app); - variable_item_set_current_value_text(item, xtreme_settings->rgb_backlight ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, - "LCD Color", - rgb_backlight_get_color_count(), - xtreme_app_scene_misc_lcd_color_changed, - app); - value_index = rgb_backlight_get_settings()->display_color_index; - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); - variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); - variable_item_list_set_enter_callback( var_item_list, xtreme_app_scene_misc_var_item_list_callback, app); @@ -151,36 +130,12 @@ bool xtreme_app_scene_misc_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMisc, event.event); consumed = true; switch(event.event) { + case VarItemListIndexScreen: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); + break; case VarItemListIndexChangeDeviceName: scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscRename); break; - case VarItemListIndexRgbBacklight: { - bool change = XTREME_SETTINGS()->rgb_backlight; - if(!change) { - DialogMessage* msg = dialog_message_alloc(); - dialog_message_set_header(msg, "RGB Backlight", 64, 0, AlignCenter, AlignTop); - dialog_message_set_buttons(msg, "No", NULL, "Yes"); - dialog_message_set_text( - msg, - "This option requires installing\na hardware modification!\nIs it installed?", - 64, - 32, - AlignCenter, - AlignCenter); - if(dialog_message_show(app->dialogs, msg) == DialogMessageButtonRight) { - change = true; - } - dialog_message_free(msg); - } - if(change) { - XTREME_SETTINGS()->rgb_backlight = !XTREME_SETTINGS()->rgb_backlight; - app->save_settings = true; - notification_message(app->notification, &sequence_display_backlight_on); - scene_manager_previous_scene(app->scene_manager); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneMisc); - } - break; - } default: break; } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c new file mode 100644 index 0000000000..b86326aae5 --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_screen.c @@ -0,0 +1,130 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexDarkMode, + VarItemListIndexLeftHanded, + VarItemListIndexRgbBacklight, + VarItemListIndexLcdColor, +}; + +void xtreme_app_scene_misc_screen_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_misc_screen_dark_mode_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->dark_mode = value; + app->save_settings = true; +} + +static void xtreme_app_scene_misc_screen_hand_orient_changed(VariableItem* item) { + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + if(value) { + furi_hal_rtc_set_flag(FuriHalRtcFlagHandOrient); + } else { + furi_hal_rtc_reset_flag(FuriHalRtcFlagHandOrient); + } +} + +static void xtreme_app_scene_misc_screen_lcd_color_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, rgb_backlight_get_color_text(index)); + rgb_backlight_set_color(index); + app->save_backlight = true; + notification_message(app->notification, &sequence_display_backlight_on); +} + +void xtreme_app_scene_misc_screen_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + uint8_t value_index; + + item = variable_item_list_add( + var_item_list, "Dark Mode", 2, xtreme_app_scene_misc_screen_dark_mode_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->dark_mode); + variable_item_set_current_value_text(item, xtreme_settings->dark_mode ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "Left Handed", 2, xtreme_app_scene_misc_screen_hand_orient_changed, app); + bool value = furi_hal_rtc_is_flag_set(FuriHalRtcFlagHandOrient); + variable_item_set_current_value_index(item, value); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + + item = variable_item_list_add(var_item_list, "RGB Backlight", 1, NULL, app); + variable_item_set_current_value_text(item, xtreme_settings->rgb_backlight ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, + "LCD Color", + rgb_backlight_get_color_count(), + xtreme_app_scene_misc_screen_lcd_color_changed, + app); + value_index = rgb_backlight_get_settings()->display_color_index; + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, rgb_backlight_get_color_text(value_index)); + variable_item_set_locked(item, !xtreme_settings->rgb_backlight, "Needs RGB\nBacklight!"); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_misc_screen_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMiscScreen)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_misc_screen_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMiscScreen, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexRgbBacklight: { + bool change = XTREME_SETTINGS()->rgb_backlight; + if(!change) { + DialogMessage* msg = dialog_message_alloc(); + dialog_message_set_header(msg, "RGB Backlight", 64, 0, AlignCenter, AlignTop); + dialog_message_set_buttons(msg, "No", NULL, "Yes"); + dialog_message_set_text( + msg, + "This option requires installing\na hardware modification!\nIs it installed?", + 64, + 32, + AlignCenter, + AlignCenter); + if(dialog_message_show(app->dialogs, msg) == DialogMessageButtonRight) { + change = true; + } + dialog_message_free(msg); + } + if(change) { + XTREME_SETTINGS()->rgb_backlight = !XTREME_SETTINGS()->rgb_backlight; + app->save_settings = true; + notification_message(app->notification, &sequence_display_backlight_on); + scene_manager_previous_scene(app->scene_manager); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); + } + break; + } + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_misc_screen_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} From 022e686e4a804e8a1a5f73b18b24b2304010462a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:06:24 +0100 Subject: [PATCH 035/102] Move dolphin options to own submenu --- .../scenes/xtreme_app_scene_config.h | 1 + .../xtreme_app/scenes/xtreme_app_scene_misc.c | 74 ++---------- .../scenes/xtreme_app_scene_misc_dolphin.c | 114 ++++++++++++++++++ 3 files changed, 122 insertions(+), 67 deletions(-) create mode 100644 applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h index 23459cd150..15c25f6651 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -13,4 +13,5 @@ ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper) ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd) ADD_SCENE(xtreme_app, misc, Misc) ADD_SCENE(xtreme_app, misc_screen, MiscScreen) +ADD_SCENE(xtreme_app, misc_dolphin, MiscDolphin) ADD_SCENE(xtreme_app, misc_rename, MiscRename) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c index 62f9af2a51..1e5e61fc05 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc.c @@ -2,10 +2,8 @@ enum VarItemListIndex { VarItemListIndexScreen, + VarItemListIndexDolphin, VarItemListIndexChangeDeviceName, - VarItemListIndexDolphinLevel, - VarItemListIndexDolphinAngry, - VarItemListIndexButthurtTimer, VarItemListIndexChargeCap, }; @@ -14,37 +12,6 @@ void xtreme_app_scene_misc_var_item_list_callback(void* context, uint32_t index) view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_misc_dolphin_level_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->dolphin_level = variable_item_get_current_value_index(item) + 1; - char level_str[4]; - snprintf(level_str, 4, "%li", app->dolphin_level); - variable_item_set_current_value_text(item, level_str); - app->save_level = true; -} - -static void xtreme_app_scene_misc_dolphin_angry_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->dolphin_angry = variable_item_get_current_value_index(item); - char angry_str[4]; - snprintf(angry_str, 4, "%li", app->dolphin_angry); - variable_item_set_current_value_text(item, angry_str); - app->save_angry = true; -} - -const char* const butthurt_timer_names[] = - {"OFF", "30 M", "1 H", "2 H", "4 H", "6 H", "8 H", "12 H", "24 H", "48 H"}; -const uint32_t butthurt_timer_values[COUNT_OF(butthurt_timer_names)] = - {0, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 172800}; -static void xtreme_app_scene_misc_butthurt_timer_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, butthurt_timer_names[index]); - XTREME_SETTINGS()->butthurt_timer = butthurt_timer_values[index]; - app->save_settings = true; - app->require_reboot = true; -} - #define CHARGE_CAP_INTV 5 static void xtreme_app_scene_misc_charge_cap_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); @@ -66,40 +33,10 @@ void xtreme_app_scene_misc_on_enter(void* context) { item = variable_item_list_add(var_item_list, "Screen", 0, NULL, app); variable_item_set_current_value_text(item, ">"); - variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app); - - char level_str[4]; - snprintf(level_str, 4, "%li", app->dolphin_level); - item = variable_item_list_add( - var_item_list, - "Dolphin Level", - DOLPHIN_LEVEL_COUNT + 1, - xtreme_app_scene_misc_dolphin_level_changed, - app); - variable_item_set_current_value_index(item, app->dolphin_level - 1); - variable_item_set_current_value_text(item, level_str); - - char angry_str[4]; - snprintf(angry_str, 4, "%li", app->dolphin_angry); - item = variable_item_list_add( - var_item_list, - "Dolphin Angry", - BUTTHURT_MAX + 1, - xtreme_app_scene_misc_dolphin_angry_changed, - app); - variable_item_set_current_value_index(item, app->dolphin_angry); - variable_item_set_current_value_text(item, angry_str); + item = variable_item_list_add(var_item_list, "Dolphin", 0, NULL, app); + variable_item_set_current_value_text(item, ">"); - item = variable_item_list_add( - var_item_list, - "Butthurt Timer", - COUNT_OF(butthurt_timer_names), - xtreme_app_scene_misc_butthurt_timer_changed, - app); - value_index = value_index_uint32( - xtreme_settings->butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_values)); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, butthurt_timer_names[value_index]); + variable_item_list_add(var_item_list, "Change Device Name", 0, NULL, app); char cap_str[6]; value_index = xtreme_settings->charge_cap / CHARGE_CAP_INTV; @@ -133,6 +70,9 @@ bool xtreme_app_scene_misc_on_event(void* context, SceneManagerEvent event) { case VarItemListIndexScreen: scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscScreen); break; + case VarItemListIndexDolphin: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscDolphin); + break; case VarItemListIndexChangeDeviceName: scene_manager_next_scene(app->scene_manager, XtremeAppSceneMiscRename); break; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c new file mode 100644 index 0000000000..e6425d20bd --- /dev/null +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_misc_dolphin.c @@ -0,0 +1,114 @@ +#include "../xtreme_app.h" + +enum VarItemListIndex { + VarItemListIndexDolphinLevel, + VarItemListIndexDolphinAngry, + VarItemListIndexButthurtTimer, +}; + +void xtreme_app_scene_misc_dolphin_var_item_list_callback(void* context, uint32_t index) { + XtremeApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void xtreme_app_scene_misc_dolphin_dolphin_level_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->dolphin_level = variable_item_get_current_value_index(item) + 1; + char level_str[4]; + snprintf(level_str, 4, "%li", app->dolphin_level); + variable_item_set_current_value_text(item, level_str); + app->save_level = true; +} + +static void xtreme_app_scene_misc_dolphin_dolphin_angry_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->dolphin_angry = variable_item_get_current_value_index(item); + char angry_str[4]; + snprintf(angry_str, 4, "%li", app->dolphin_angry); + variable_item_set_current_value_text(item, angry_str); + app->save_angry = true; +} + +const char* const butthurt_timer_names[] = + {"OFF", "30 M", "1 H", "2 H", "4 H", "6 H", "8 H", "12 H", "24 H", "48 H"}; +const uint32_t butthurt_timer_values[COUNT_OF(butthurt_timer_names)] = + {0, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 172800}; +static void xtreme_app_scene_misc_dolphin_butthurt_timer_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, butthurt_timer_names[index]); + XTREME_SETTINGS()->butthurt_timer = butthurt_timer_values[index]; + app->save_settings = true; + app->require_reboot = true; +} + +void xtreme_app_scene_misc_dolphin_on_enter(void* context) { + XtremeApp* app = context; + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + uint8_t value_index; + + char level_str[4]; + snprintf(level_str, 4, "%li", app->dolphin_level); + item = variable_item_list_add( + var_item_list, + "Dolphin Level", + DOLPHIN_LEVEL_COUNT + 1, + xtreme_app_scene_misc_dolphin_dolphin_level_changed, + app); + variable_item_set_current_value_index(item, app->dolphin_level - 1); + variable_item_set_current_value_text(item, level_str); + + char angry_str[4]; + snprintf(angry_str, 4, "%li", app->dolphin_angry); + item = variable_item_list_add( + var_item_list, + "Dolphin Angry", + BUTTHURT_MAX + 1, + xtreme_app_scene_misc_dolphin_dolphin_angry_changed, + app); + variable_item_set_current_value_index(item, app->dolphin_angry); + variable_item_set_current_value_text(item, angry_str); + + item = variable_item_list_add( + var_item_list, + "Butthurt Timer", + COUNT_OF(butthurt_timer_names), + xtreme_app_scene_misc_dolphin_butthurt_timer_changed, + app); + value_index = value_index_uint32( + xtreme_settings->butthurt_timer, butthurt_timer_values, COUNT_OF(butthurt_timer_values)); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, butthurt_timer_names[value_index]); + + variable_item_list_set_enter_callback( + var_item_list, xtreme_app_scene_misc_dolphin_var_item_list_callback, app); + + variable_item_list_set_selected_item( + var_item_list, + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneMiscDolphin)); + + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); +} + +bool xtreme_app_scene_misc_dolphin_on_event(void* context, SceneManagerEvent event) { + XtremeApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneMiscDolphin, event.event); + consumed = true; + switch(event.event) { + default: + break; + } + } + + return consumed; +} + +void xtreme_app_scene_misc_dolphin_on_exit(void* context) { + XtremeApp* app = context; + variable_item_list_reset(app->var_item_list); +} From fff830b4633ca7a02e23f65c0031b66cb4026999 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:10:47 +0100 Subject: [PATCH 036/102] Rename frequencies menus to freqs to avoid scroll --- .../scenes/xtreme_app_scene_config.h | 8 ++--- .../scenes/xtreme_app_scene_protocols.c | 8 ++--- ...s.c => xtreme_app_scene_protocols_freqs.c} | 36 +++++++++---------- ...=> xtreme_app_scene_protocols_freqs_add.c} | 14 ++++---- ...xtreme_app_scene_protocols_freqs_hopper.c} | 29 +++++++-------- ...xtreme_app_scene_protocols_freqs_static.c} | 29 +++++++-------- applications/main/xtreme_app/xtreme_app.c | 2 +- applications/main/xtreme_app/xtreme_app.h | 2 +- 8 files changed, 61 insertions(+), 67 deletions(-) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_protocols_frequencies.c => xtreme_app_scene_protocols_freqs.c} (59%) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_protocols_frequencies_add.c => xtreme_app_scene_protocols_freqs_add.c} (82%) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_protocols_frequencies_hopper.c => xtreme_app_scene_protocols_freqs_hopper.c} (74%) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_protocols_frequencies_static.c => xtreme_app_scene_protocols_freqs_static.c} (74%) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h index 15c25f6651..8e283e9eb7 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -7,10 +7,10 @@ ADD_SCENE(xtreme_app, interface_lockscreen, InterfaceLockscreen) ADD_SCENE(xtreme_app, interface_statusbar, InterfaceStatusbar) ADD_SCENE(xtreme_app, interface_common, InterfaceCommon) ADD_SCENE(xtreme_app, protocols, Protocols) -ADD_SCENE(xtreme_app, protocols_frequencies, ProtocolsFrequencies) -ADD_SCENE(xtreme_app, protocols_frequencies_static, ProtocolsFrequenciesStatic) -ADD_SCENE(xtreme_app, protocols_frequencies_hopper, ProtocolsFrequenciesHopper) -ADD_SCENE(xtreme_app, protocols_frequencies_add, ProtocolsFrequenciesAdd) +ADD_SCENE(xtreme_app, protocols_freqs, ProtocolsFreqs) +ADD_SCENE(xtreme_app, protocols_freqs_static, ProtocolsFreqsStatic) +ADD_SCENE(xtreme_app, protocols_freqs_hopper, ProtocolsFreqsHopper) +ADD_SCENE(xtreme_app, protocols_freqs_add, ProtocolsFreqsAdd) ADD_SCENE(xtreme_app, misc, Misc) ADD_SCENE(xtreme_app, misc_screen, MiscScreen) ADD_SCENE(xtreme_app, misc_dolphin, MiscDolphin) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c index 497d3f1ecb..67c80b14eb 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols.c @@ -3,7 +3,7 @@ enum VarItemListIndex { VarItemListIndexBadkbMode, VarItemListIndexBadbtRemember, - VarItemListIndexSubghzFrequencies, + VarItemListIndexSubghzFreqs, VarItemListIndexSubghzExtend, }; @@ -55,7 +55,7 @@ void xtreme_app_scene_protocols_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->bad_bt_remember); variable_item_set_current_value_text(item, xtreme_settings->bad_bt_remember ? "ON" : "OFF"); - item = variable_item_list_add(var_item_list, "SubGHz Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "SubGHz Freqs", 0, NULL, app); variable_item_set_current_value_text(item, ">"); item = variable_item_list_add( @@ -80,8 +80,8 @@ bool xtreme_app_scene_protocols_on_event(void* context, SceneManagerEvent event) scene_manager_set_scene_state(app->scene_manager, XtremeAppSceneProtocols, event.event); consumed = true; switch(event.event) { - case VarItemListIndexSubghzFrequencies: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequencies); + case VarItemListIndexSubghzFreqs: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqs); break; default: break; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c similarity index 59% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c index 668fe83272..f8c779cc58 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs.c @@ -2,24 +2,24 @@ enum VarItemListIndex { VarItemListIndexUseDefaults, - VarItemListIndexStaticFrequencies, - VarItemListIndexHopperFrequencies, + VarItemListIndexStaticFreqs, + VarItemListIndexHopperFreqs, }; -void xtreme_app_scene_protocols_frequencies_var_item_list_callback(void* context, uint32_t index) { +void xtreme_app_scene_protocols_freqs_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_frequencies_use_defaults_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_freqs_use_defaults_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); app->subghz_use_defaults = value; - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; } -void xtreme_app_scene_protocols_frequencies_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -28,41 +28,41 @@ void xtreme_app_scene_protocols_frequencies_on_enter(void* context) { var_item_list, "Use Defaults", 2, - xtreme_app_scene_protocols_frequencies_use_defaults_changed, + xtreme_app_scene_protocols_freqs_use_defaults_changed, app); variable_item_set_current_value_index(item, app->subghz_use_defaults); variable_item_set_current_value_text(item, app->subghz_use_defaults ? "ON" : "OFF"); - item = variable_item_list_add(var_item_list, "Static Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Static Freqs", 0, NULL, app); variable_item_set_current_value_text(item, ">"); - item = variable_item_list_add(var_item_list, "Hopper Frequencies", 0, NULL, app); + item = variable_item_list_add(var_item_list, "Hopper Freqs", 0, NULL, app); variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_protocols_frequencies_var_item_list_callback, app); + var_item_list, xtreme_app_scene_protocols_freqs_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequencies)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqs)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_protocols_frequencies_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequencies, event.event); + app->scene_manager, XtremeAppSceneProtocolsFreqs, event.event); consumed = true; switch(event.event) { - case VarItemListIndexStaticFrequencies: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic); + case VarItemListIndexStaticFreqs: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic); break; - case VarItemListIndexHopperFrequencies: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper); + case VarItemListIndexHopperFreqs: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper); break; default: break; @@ -72,7 +72,7 @@ bool xtreme_app_scene_protocols_frequencies_on_event(void* context, SceneManager return consumed; } -void xtreme_app_scene_protocols_frequencies_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_add.c similarity index 82% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_add.c index a9efa8b541..314a69e1e7 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_add.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_add.c @@ -5,7 +5,7 @@ enum TextInputResult { TextInputResultError, }; -static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* context) { +static void xtreme_app_scene_protocols_freqs_add_text_input_callback(void* context) { XtremeApp* app = context; char* end; @@ -15,17 +15,17 @@ static void xtreme_app_scene_protocols_frequencies_add_text_input_callback(void* return; } bool is_hopper = - scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqsAdd); if(is_hopper) { FrequencyList_push_back(app->subghz_hopper_freqs, value); } else { FrequencyList_push_back(app->subghz_static_freqs, value); } - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; view_dispatcher_send_custom_event(app->view_dispatcher, TextInputResultOk); } -void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_add_on_enter(void* context) { XtremeApp* app = context; TextInput* text_input = app->text_input; @@ -35,7 +35,7 @@ void xtreme_app_scene_protocols_frequencies_add_on_enter(void* context) { text_input_set_result_callback( text_input, - xtreme_app_scene_protocols_frequencies_add_text_input_callback, + xtreme_app_scene_protocols_freqs_add_text_input_callback, app, app->subghz_freq_buffer, XTREME_SUBGHZ_FREQ_BUFFER_SIZE, @@ -49,7 +49,7 @@ void callback_return(void* context) { scene_manager_previous_scene(app->scene_manager); } -bool xtreme_app_scene_protocols_frequencies_add_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_add_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; @@ -77,7 +77,7 @@ bool xtreme_app_scene_protocols_frequencies_add_on_event(void* context, SceneMan return consumed; } -void xtreme_app_scene_protocols_frequencies_add_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_add_on_exit(void* context) { XtremeApp* app = context; text_input_reset(app->text_input); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_hopper.c similarity index 74% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_hopper.c index b9e387252a..13fdc0ac22 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_hopper.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_hopper.c @@ -6,14 +6,12 @@ enum VarItemListIndex { VarItemListIndexAddHopperFreq, }; -void xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback( - void* context, - uint32_t index) { +void xtreme_app_scene_protocols_freqs_hopper_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_frequencies_hopper_frequency_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_freqs_hopper_frequency_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); app->subghz_hopper_index = variable_item_get_current_value_index(item); uint32_t value = *FrequencyList_get(app->subghz_hopper_freqs, app->subghz_hopper_index); @@ -22,7 +20,7 @@ static void xtreme_app_scene_protocols_frequencies_hopper_frequency_changed(Vari variable_item_set_current_value_text(item, text); } -void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_hopper_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -31,7 +29,7 @@ void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { var_item_list, "Hopper Freq", FrequencyList_size(app->subghz_hopper_freqs), - xtreme_app_scene_protocols_frequencies_hopper_frequency_changed, + xtreme_app_scene_protocols_freqs_hopper_frequency_changed, app); app->subghz_hopper_index = 0; variable_item_set_current_value_index(item, app->subghz_hopper_index); @@ -49,23 +47,22 @@ void xtreme_app_scene_protocols_frequencies_hopper_on_enter(void* context) { variable_item_list_add(var_item_list, "Add Hopper Freq", 0, NULL, app); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_protocols_frequencies_hopper_var_item_list_callback, app); + var_item_list, xtreme_app_scene_protocols_freqs_hopper_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_hopper_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper, event.event); + app->scene_manager, XtremeAppSceneProtocolsFreqsHopper, event.event); consumed = true; switch(event.event) { case VarItemListIndexRemoveHopperFreq: @@ -81,14 +78,14 @@ bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, Scene FrequencyList_next(it); } } - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; scene_manager_previous_scene(app->scene_manager); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesHopper); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsHopper); break; case VarItemListIndexAddHopperFreq: scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, true); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + app->scene_manager, XtremeAppSceneProtocolsFreqsAdd, true); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsAdd); break; default: break; @@ -98,7 +95,7 @@ bool xtreme_app_scene_protocols_frequencies_hopper_on_event(void* context, Scene return consumed; } -void xtreme_app_scene_protocols_frequencies_hopper_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_hopper_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_static.c similarity index 74% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_static.c index b50c1f8966..2f139b2b29 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_frequencies_static.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_protocols_freqs_static.c @@ -6,14 +6,12 @@ enum VarItemListIndex { VarItemListIndexAddStaticFreq, }; -void xtreme_app_scene_protocols_frequencies_static_var_item_list_callback( - void* context, - uint32_t index) { +void xtreme_app_scene_protocols_freqs_static_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_protocols_frequencies_static_frequency_changed(VariableItem* item) { +static void xtreme_app_scene_protocols_freqs_static_frequency_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); app->subghz_static_index = variable_item_get_current_value_index(item); uint32_t value = *FrequencyList_get(app->subghz_static_freqs, app->subghz_static_index); @@ -22,7 +20,7 @@ static void xtreme_app_scene_protocols_frequencies_static_frequency_changed(Vari variable_item_set_current_value_text(item, text); } -void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { +void xtreme_app_scene_protocols_freqs_static_on_enter(void* context) { XtremeApp* app = context; VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -31,7 +29,7 @@ void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { var_item_list, "Static Freq", FrequencyList_size(app->subghz_static_freqs), - xtreme_app_scene_protocols_frequencies_static_frequency_changed, + xtreme_app_scene_protocols_freqs_static_frequency_changed, app); app->subghz_static_index = 0; variable_item_set_current_value_index(item, app->subghz_static_index); @@ -49,23 +47,22 @@ void xtreme_app_scene_protocols_frequencies_static_on_enter(void* context) { variable_item_list_add(var_item_list, "Add Static Freq", 0, NULL, app); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_protocols_frequencies_static_var_item_list_callback, app); + var_item_list, xtreme_app_scene_protocols_freqs_static_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_protocols_freqs_static_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic, event.event); + app->scene_manager, XtremeAppSceneProtocolsFreqsStatic, event.event); consumed = true; switch(event.event) { case VarItemListIndexRemoveStaticFreq: @@ -81,14 +78,14 @@ bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, Scene FrequencyList_next(it); } } - app->save_subghz_frequencies = true; + app->save_subghz_freqs = true; scene_manager_previous_scene(app->scene_manager); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesStatic); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsStatic); break; case VarItemListIndexAddStaticFreq: scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd, false); - scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFrequenciesAdd); + app->scene_manager, XtremeAppSceneProtocolsFreqsAdd, false); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneProtocolsFreqsAdd); break; default: break; @@ -98,7 +95,7 @@ bool xtreme_app_scene_protocols_frequencies_static_on_event(void* context, Scene return consumed; } -void xtreme_app_scene_protocols_frequencies_static_on_exit(void* context) { +void xtreme_app_scene_protocols_freqs_static_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } diff --git a/applications/main/xtreme_app/xtreme_app.c b/applications/main/xtreme_app/xtreme_app.c index dedd9fcdeb..3d57609236 100644 --- a/applications/main/xtreme_app/xtreme_app.c +++ b/applications/main/xtreme_app/xtreme_app.c @@ -27,7 +27,7 @@ bool xtreme_app_apply(XtremeApp* app) { stream_free(stream); } - if(app->save_subghz_frequencies) { + if(app->save_subghz_freqs) { FlipperFormat* file = flipper_format_file_alloc(storage); do { FrequencyList_it_t it; diff --git a/applications/main/xtreme_app/xtreme_app.h b/applications/main/xtreme_app/xtreme_app.h index ce50facdde..321ad167c8 100644 --- a/applications/main/xtreme_app/xtreme_app.h +++ b/applications/main/xtreme_app/xtreme_app.h @@ -57,7 +57,7 @@ typedef struct { FuriString* version_tag; bool save_mainmenu_apps; - bool save_subghz_frequencies; + bool save_subghz_freqs; bool save_subghz; bool save_name; bool save_level; From 4111ff4e045dc4ba349d859e4751c94c7ce91cf1 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:17:28 +0100 Subject: [PATCH 037/102] Rename common section to file browser --- .../scenes/xtreme_app_scene_config.h | 2 +- .../scenes/xtreme_app_scene_interface.c | 8 +++---- ... xtreme_app_scene_interface_filebrowser.c} | 22 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) rename applications/main/xtreme_app/scenes/{xtreme_app_scene_interface_common.c => xtreme_app_scene_interface_filebrowser.c} (72%) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h index 8e283e9eb7..77ba439911 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_config.h @@ -5,7 +5,7 @@ ADD_SCENE(xtreme_app, interface_mainmenu, InterfaceMainmenu) ADD_SCENE(xtreme_app, interface_mainmenu_add, InterfaceMainmenuAdd) ADD_SCENE(xtreme_app, interface_lockscreen, InterfaceLockscreen) ADD_SCENE(xtreme_app, interface_statusbar, InterfaceStatusbar) -ADD_SCENE(xtreme_app, interface_common, InterfaceCommon) +ADD_SCENE(xtreme_app, interface_filebrowser, InterfaceFilebrowser) ADD_SCENE(xtreme_app, protocols, Protocols) ADD_SCENE(xtreme_app, protocols_freqs, ProtocolsFreqs) ADD_SCENE(xtreme_app, protocols_freqs_static, ProtocolsFreqsStatic) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c index 5e04fc4b8f..e6b62d0346 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface.c @@ -5,7 +5,7 @@ enum VarItemListIndex { VarItemListIndexMainmenu, VarItemListIndexLockscreen, VarItemListIndexStatusbar, - VarItemListIndexCommon, + VarItemListIndexFileBrowser, }; void xtreme_app_scene_interface_var_item_list_callback(void* context, uint32_t index) { @@ -30,7 +30,7 @@ void xtreme_app_scene_interface_on_enter(void* context) { item = variable_item_list_add(var_item_list, "Statusbar", 0, NULL, app); variable_item_set_current_value_text(item, ">"); - item = variable_item_list_add(var_item_list, "Common", 0, NULL, app); + item = variable_item_list_add(var_item_list, "File Browser", 0, NULL, app); variable_item_set_current_value_text(item, ">"); variable_item_list_set_enter_callback( @@ -62,8 +62,8 @@ bool xtreme_app_scene_interface_on_event(void* context, SceneManagerEvent event) case VarItemListIndexStatusbar: scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceStatusbar); break; - case VarItemListIndexCommon: - scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceCommon); + case VarItemListIndexFileBrowser: + scene_manager_next_scene(app->scene_manager, XtremeAppSceneInterfaceFilebrowser); break; default: break; diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c similarity index 72% rename from applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c rename to applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c index 3efa4fffa7..2df77cdf3e 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_common.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c @@ -5,12 +5,12 @@ enum VarItemListIndex { VarItemListIndexFavoriteTimeout, }; -void xtreme_app_scene_interface_common_var_item_list_callback(void* context, uint32_t index) { +void xtreme_app_scene_interface_filebrowser_var_item_list_callback(void* context, uint32_t index) { XtremeApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } -static void xtreme_app_scene_interface_common_sort_dirs_first_changed(VariableItem* item) { +static void xtreme_app_scene_interface_filebrowser_sort_dirs_first_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); @@ -18,7 +18,7 @@ static void xtreme_app_scene_interface_common_sort_dirs_first_changed(VariableIt app->save_settings = true; } -static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableItem* item) { +static void xtreme_app_scene_interface_filebrowser_favorite_timeout_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint32_t value = variable_item_get_current_value_index(item); char text[6]; @@ -28,7 +28,7 @@ static void xtreme_app_scene_interface_common_favorite_timeout_changed(VariableI app->save_settings = true; } -void xtreme_app_scene_interface_common_on_enter(void* context) { +void xtreme_app_scene_interface_filebrowser_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); VariableItemList* var_item_list = app->var_item_list; @@ -38,7 +38,7 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { var_item_list, "Sort Dirs First", 2, - xtreme_app_scene_interface_common_sort_dirs_first_changed, + xtreme_app_scene_interface_filebrowser_sort_dirs_first_changed, app); variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first); variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF"); @@ -47,7 +47,7 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { var_item_list, "Favorite Timeout", 61, - xtreme_app_scene_interface_common_favorite_timeout_changed, + xtreme_app_scene_interface_filebrowser_favorite_timeout_changed, app); variable_item_set_current_value_index(item, xtreme_settings->favorite_timeout); char text[4]; @@ -55,22 +55,22 @@ void xtreme_app_scene_interface_common_on_enter(void* context) { variable_item_set_current_value_text(item, xtreme_settings->favorite_timeout ? text : "OFF"); variable_item_list_set_enter_callback( - var_item_list, xtreme_app_scene_interface_common_var_item_list_callback, app); + var_item_list, xtreme_app_scene_interface_filebrowser_var_item_list_callback, app); variable_item_list_set_selected_item( var_item_list, - scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneInterfaceCommon)); + scene_manager_get_scene_state(app->scene_manager, XtremeAppSceneInterfaceFilebrowser)); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_app_scene_interface_common_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_interface_filebrowser_on_event(void* context, SceneManagerEvent event) { XtremeApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { scene_manager_set_scene_state( - app->scene_manager, XtremeAppSceneInterfaceCommon, event.event); + app->scene_manager, XtremeAppSceneInterfaceFilebrowser, event.event); consumed = true; switch(event.event) { default: @@ -81,7 +81,7 @@ bool xtreme_app_scene_interface_common_on_event(void* context, SceneManagerEvent return consumed; } -void xtreme_app_scene_interface_common_on_exit(void* context) { +void xtreme_app_scene_interface_filebrowser_on_exit(void* context) { XtremeApp* app = context; variable_item_list_reset(app->var_item_list); } From c4624ea209f2065644b44273a58474446c721798 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:21:42 +0100 Subject: [PATCH 038/102] Sort settings file and struct --- lib/xtreme/settings.c | 24 ++++++++++++------------ lib/xtreme/xtreme.h | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index f3e3cd6c2a..d698001546 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -24,13 +24,13 @@ XtremeSettings xtreme_settings = { .bar_borders = true, // ON .bar_background = false, // OFF .sort_dirs_first = true, // ON - .dark_mode = false, // OFF .favorite_timeout = 0, // OFF .bad_bt = false, // USB .bad_bt_remember = false, // OFF + .dark_mode = false, // OFF + .rgb_backlight = false, // OFF .butthurt_timer = 21600, // 6 H .charge_cap = 100, // 100% - .rgb_backlight = false, // OFF }; void XTREME_SETTINGS_LOAD() { @@ -117,10 +117,6 @@ void XTREME_SETTINGS_LOAD() { x->sort_dirs_first = b; } flipper_format_rewind(file); - if(flipper_format_read_bool(file, "dark_mode", &b, 1)) { - x->dark_mode = b; - } - flipper_format_rewind(file); if(flipper_format_read_uint32(file, "favorite_timeout", &u, 1)) { x->favorite_timeout = CLAMP(u, 60U, 0U); } @@ -133,6 +129,14 @@ void XTREME_SETTINGS_LOAD() { x->bad_bt_remember = b; } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "dark_mode", &b, 1)) { + x->dark_mode = b; + } + flipper_format_rewind(file); + if(flipper_format_read_bool(file, "rgb_backlight", &b, 1)) { + x->rgb_backlight = b; + } + flipper_format_rewind(file); if(flipper_format_read_uint32(file, "butthurt_timer", &u, 1)) { x->butthurt_timer = CLAMP(u, 172800U, 0U); } @@ -140,10 +144,6 @@ void XTREME_SETTINGS_LOAD() { if(flipper_format_read_uint32(file, "charge_cap", &u, 1)) { x->charge_cap = CLAMP(u, 100U, 5U); } - flipper_format_rewind(file); - if(flipper_format_read_bool(file, "rgb_backlight", &b, 1)) { - x->rgb_backlight = b; - } } flipper_format_free(file); furi_record_close(RECORD_STORAGE); @@ -174,13 +174,13 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); flipper_format_write_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); - flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); flipper_format_write_uint32(file, "favorite_timeout", &x->favorite_timeout, 1); flipper_format_write_bool(file, "bad_bt", &x->bad_bt, 1); flipper_format_write_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); + flipper_format_write_bool(file, "dark_mode", &x->dark_mode, 1); + flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); flipper_format_write_uint32(file, "butthurt_timer", &x->butthurt_timer, 1); flipper_format_write_uint32(file, "charge_cap", &x->charge_cap, 1); - flipper_format_write_bool(file, "rgb_backlight", &x->rgb_backlight, 1); } flipper_format_free(file); furi_record_close(RECORD_STORAGE); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index ea60763549..4a52cd7cf7 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -33,13 +33,13 @@ typedef struct { bool bar_borders; bool bar_background; bool sort_dirs_first; - bool dark_mode; uint32_t favorite_timeout; bool bad_bt; bool bad_bt_remember; + bool dark_mode; + bool rgb_backlight; uint32_t butthurt_timer; uint32_t charge_cap; - bool rgb_backlight; } XtremeSettings; void XTREME_SETTINGS_SAVE(); From 1e299c1b74e95ca87f08c251a0f8736354a4f481 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 00:39:52 +0100 Subject: [PATCH 039/102] Add hidden files and internal tab settings --- .../main/archive/helpers/archive_browser.c | 16 +++++---- .../xtreme_app_scene_interface_filebrowser.c | 36 +++++++++++++++++++ lib/xtreme/settings.c | 14 +++++++- lib/xtreme/xtreme.h | 2 ++ 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index aaef1c9e53..5c6f486d1b 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -471,10 +471,14 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { browser->last_tab_switch_dir = key; - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; + for(int i = 0; i < 2; i++) { + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; + } + if(tab == ArchiveTabInternal && !XTREME_SETTINGS()->show_internal_tab) continue; + break; } browser->is_root = true; @@ -503,14 +507,14 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { bool hide_dot_files = !is_browser ? true : tab == ArchiveTabInternal ? false : - !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); + !XTREME_SETTINGS()->show_hidden_files; archive_file_browser_set_path( browser, browser->path, archive_get_tab_ext(tab), skip_assets, hide_dot_files); tab_empty = false; // Empty check will be performed later } } - if((tab_empty) && (tab != ArchiveTabBrowser) && (tab != ArchiveTabInternal)) { + if(tab_empty && tab != ArchiveTabBrowser && tab != ArchiveTabInternal) { archive_switch_tab(browser, key); } else { with_view_model( diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c index 2df77cdf3e..ed19516262 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_filebrowser.c @@ -2,6 +2,8 @@ enum VarItemListIndex { VarItemListIndexSortDirsFirst, + VarItemListIndexShowHiddenFiles, + VarItemListIndexShowInternalTab, VarItemListIndexFavoriteTimeout, }; @@ -18,6 +20,22 @@ static void xtreme_app_scene_interface_filebrowser_sort_dirs_first_changed(Varia app->save_settings = true; } +static void xtreme_app_scene_interface_filebrowser_show_hidden_files_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->show_hidden_files = value; + app->save_settings = true; +} + +static void xtreme_app_scene_interface_filebrowser_show_internal_tab_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->show_internal_tab = value; + app->save_settings = true; +} + static void xtreme_app_scene_interface_filebrowser_favorite_timeout_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint32_t value = variable_item_get_current_value_index(item); @@ -43,6 +61,24 @@ void xtreme_app_scene_interface_filebrowser_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->sort_dirs_first); variable_item_set_current_value_text(item, xtreme_settings->sort_dirs_first ? "ON" : "OFF"); + item = variable_item_list_add( + var_item_list, + "Show Hidden Files", + 2, + xtreme_app_scene_interface_filebrowser_show_hidden_files_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->show_hidden_files); + variable_item_set_current_value_text(item, xtreme_settings->show_hidden_files ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, + "Show Internal Tab", + 2, + xtreme_app_scene_interface_filebrowser_show_internal_tab_changed, + app); + variable_item_set_current_value_index(item, xtreme_settings->show_internal_tab); + variable_item_set_current_value_text(item, xtreme_settings->show_internal_tab ? "ON" : "OFF"); + item = variable_item_list_add( var_item_list, "Favorite Timeout", diff --git a/lib/xtreme/settings.c b/lib/xtreme/settings.c index d698001546..023cbcbc98 100644 --- a/lib/xtreme/settings.c +++ b/lib/xtreme/settings.c @@ -6,7 +6,7 @@ #define TAG "XtremeSettings" XtremeSettings xtreme_settings = { - .asset_pack = "", + .asset_pack = "", // SFW .anim_speed = 100, // 100% .cycle_anims = 0, // Meta.txt .unlock_anims = false, // OFF @@ -24,6 +24,8 @@ XtremeSettings xtreme_settings = { .bar_borders = true, // ON .bar_background = false, // OFF .sort_dirs_first = true, // ON + .show_hidden_files = false, // OFF + .show_internal_tab = false, // OFF .favorite_timeout = 0, // OFF .bad_bt = false, // USB .bad_bt_remember = false, // OFF @@ -117,6 +119,14 @@ void XTREME_SETTINGS_LOAD() { x->sort_dirs_first = b; } flipper_format_rewind(file); + if(flipper_format_read_bool(file, "show_hidden_files", &b, 1)) { + x->show_hidden_files = b; + } + flipper_format_rewind(file); + if(flipper_format_read_bool(file, "show_internal_tab", &b, 1)) { + x->show_internal_tab = b; + } + flipper_format_rewind(file); if(flipper_format_read_uint32(file, "favorite_timeout", &u, 1)) { x->favorite_timeout = CLAMP(u, 60U, 0U); } @@ -174,6 +184,8 @@ void XTREME_SETTINGS_SAVE() { flipper_format_write_bool(file, "bar_borders", &x->bar_borders, 1); flipper_format_write_bool(file, "bar_background", &x->bar_background, 1); flipper_format_write_bool(file, "sort_dirs_first", &x->sort_dirs_first, 1); + flipper_format_write_bool(file, "show_hidden_files", &x->show_hidden_files, 1); + flipper_format_write_bool(file, "show_internal_tab", &x->show_internal_tab, 1); flipper_format_write_uint32(file, "favorite_timeout", &x->favorite_timeout, 1); flipper_format_write_bool(file, "bad_bt", &x->bad_bt, 1); flipper_format_write_bool(file, "bad_bt_remember", &x->bad_bt_remember, 1); diff --git a/lib/xtreme/xtreme.h b/lib/xtreme/xtreme.h index 4a52cd7cf7..89a1262013 100644 --- a/lib/xtreme/xtreme.h +++ b/lib/xtreme/xtreme.h @@ -33,6 +33,8 @@ typedef struct { bool bar_borders; bool bar_background; bool sort_dirs_first; + bool show_hidden_files; + bool show_internal_tab; uint32_t favorite_timeout; bool bad_bt; bool bad_bt_remember; From f01b68e1e0ff17b58a948a888211af770ce93674 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 5 Jun 2023 01:08:55 +0100 Subject: [PATCH 040/102] Allow desktop clock with no battery icon --- .../xtreme_app_scene_interface_statusbar.c | 2 + applications/services/gui/gui.c | 120 ++++++++---------- .../services/power/power_service/power.c | 7 + .../services/power/power_service/power.h | 7 + firmware/targets/f7/api_symbols.csv | 1 + 5 files changed, 73 insertions(+), 64 deletions(-) diff --git a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c index 1a3eb36b17..5d58472d89 100644 --- a/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c +++ b/applications/main/xtreme_app/scenes/xtreme_app_scene_interface_statusbar.c @@ -20,6 +20,8 @@ static void xtreme_app_scene_interface_statusbar_battery_icon_changed(VariableIt variable_item_set_current_value_text(item, battery_icon_names[index]); XTREME_SETTINGS()->battery_icon = index; app->save_settings = true; + power_set_battery_icon_enabled(furi_record_open(RECORD_POWER), index != BatteryIconOff); + furi_record_close(RECORD_POWER); } static void xtreme_app_scene_interface_statusbar_status_icons_changed(VariableItem* item) { diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 500d746f88..d6445d38e3 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -100,75 +100,67 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { } canvas_set_bitmap_mode(gui->canvas, 0); - uint8_t x; - // Right side - if(xtreme_settings->battery_icon != BatteryIconOff) { - x = GUI_DISPLAY_WIDTH - 1; - ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); - while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { - ViewPort* view_port = *ViewPortArray_ref(it); - if(view_port_is_enabled(view_port)) { - width = view_port_get_width(view_port); - if(!width) width = 8; - // Recalculate next position - right_used += (width + 2); - x -= (width + 2); - // Prepare work area background - canvas_frame_set( - gui->canvas, - x - 1, - GUI_STATUS_BAR_Y + 1, - width + 2, - GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - // Hide battery background - if(xtreme_settings->bar_borders) { - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box( - gui->canvas, - -1, - 0, - canvas_width(gui->canvas) + 1, - canvas_height(gui->canvas)); - } - canvas_set_color(gui->canvas, ColorBlack); - // ViewPort draw - canvas_frame_set( - gui->canvas, - x - xtreme_settings->bar_borders, - GUI_STATUS_BAR_Y + 2, - width, - GUI_STATUS_BAR_WORKAREA_HEIGHT); - view_port_draw(view_port, gui->canvas); - } - ViewPortArray_next(it); - } - // Draw frame around icons on the right - if(right_used) { + uint8_t x = GUI_DISPLAY_WIDTH - 1; + ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); + while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + width = view_port_get_width(view_port); + if(!width) width = 8; + // Recalculate next position + right_used += (width + 2); + x -= (width + 2); + // Prepare work area background canvas_frame_set( gui->canvas, - GUI_DISPLAY_WIDTH - 4 - right_used, - GUI_STATUS_BAR_Y, - right_used + 4, - GUI_STATUS_BAR_HEIGHT); - // Disable battery border + x - 1, + GUI_STATUS_BAR_Y + 1, + width + 2, + GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); + // Hide battery background if(xtreme_settings->bar_borders) { - canvas_set_color(gui->canvas, ColorBlack); - canvas_draw_rframe( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); - canvas_draw_line( - gui->canvas, - canvas_width(gui->canvas) - 2, - 1, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); - canvas_draw_line( - gui->canvas, - 1, - canvas_height(gui->canvas) - 2, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, -1, 0, canvas_width(gui->canvas) + 1, canvas_height(gui->canvas)); } + canvas_set_color(gui->canvas, ColorBlack); + // ViewPort draw + canvas_frame_set( + gui->canvas, + x - xtreme_settings->bar_borders, + GUI_STATUS_BAR_Y + 2, + width, + GUI_STATUS_BAR_WORKAREA_HEIGHT); + view_port_draw(view_port, gui->canvas); + } + ViewPortArray_next(it); + } + // Draw frame around icons on the right + if(right_used) { + canvas_frame_set( + gui->canvas, + GUI_DISPLAY_WIDTH - 4 - right_used, + GUI_STATUS_BAR_Y, + right_used + 4, + GUI_STATUS_BAR_HEIGHT); + // Disable battery border + if(xtreme_settings->bar_borders) { + canvas_set_color(gui->canvas, ColorBlack); + canvas_draw_rframe( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); + canvas_draw_line( + gui->canvas, + canvas_width(gui->canvas) - 2, + 1, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + canvas_draw_line( + gui->canvas, + 1, + canvas_height(gui->canvas) - 2, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); } } diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 0810c84d69..51fcb3895e 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -7,6 +7,12 @@ #define POWER_OFF_TIMEOUT 90 #define TAG "Power" +void power_set_battery_icon_enabled(Power* power, bool is_enabled) { + furi_assert(power); + + view_port_enabled_set(power->battery_view_port, is_enabled); +} + void power_draw_battery_callback(Canvas* canvas, void* context) { furi_assert(context); Power* power = context; @@ -356,6 +362,7 @@ Power* power_alloc() { // Battery view port power->battery_view_port = power_battery_view_port_alloc(power); + power_set_battery_icon_enabled(power, XTREME_SETTINGS()->battery_icon != BatteryIconOff); power->show_low_bat_level_message = true; //Auto shutdown timer diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 8a9d5947d1..a2dc34f90c 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -120,6 +120,13 @@ void power_enable_low_battery_level_notification(Power* power, bool enable); */ void power_trigger_ui_update(Power* power); +/** Enable or disable battery icon + * + * @param power Power instance + * @param is_enabled Show battery or not + */ +void power_set_battery_icon_enabled(Power* power, bool is_enabled); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a8a32069b6..5a8de3d235 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2276,6 +2276,7 @@ Function,+,power_get_settings_events_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* Function,+,power_off,void,Power* Function,+,power_reboot,void,PowerBootMode +Function,+,power_set_battery_icon_enabled,void,"Power*, _Bool" Function,-,power_trigger_ui_update,void,Power* Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" From 72ad22bb91e0277394ddf13e8ead026776224455 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Mon, 5 Jun 2023 14:25:43 +0400 Subject: [PATCH 041/102] [DEVOPS-18]: Add map file parser, mariadb inserter (#2732) --- .github/workflows/build.yml | 42 +++--- scripts/map_mariadb_insert.py | 139 +++++++++++++++++++ scripts/map_parser.py | 251 ++++++++++++++++++++++++++++++++++ 3 files changed, 414 insertions(+), 18 deletions(-) create mode 100755 scripts/map_mariadb_insert.py create mode 100755 scripts/map_parser.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ca603a64a7..e8b76a02cc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -100,25 +100,31 @@ jobs: cp build/f7-firmware-*/firmware.elf map_analyser_files/firmware.elf cp ${{ github.event_path }} map_analyser_files/event.json - - name: 'Upload map analyser files to storage' + - name: 'Analyse map file' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: prewk/s3-cp-action@v2 - with: - aws_s3_endpoint: "${{ secrets.MAP_REPORT_AWS_ENDPOINT }}" - aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" - aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" - source: "./map_analyser_files/" - dest: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" - flags: "--recursive --acl public-read" - - - name: 'Trigger map file reporter' - if: ${{ !github.event.pull_request.head.repo.fork }} - uses: peter-evans/repository-dispatch@v2 - with: - repository: flipperdevices/flipper-map-reporter - token: ${{ secrets.REPOSITORY_DISPATCH_TOKEN }} - event-type: map-file-analyse - client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' + run: | + source scripts/toolchain/fbtenv.sh + get_size() + { + SECTION="$1"; + arm-none-eabi-size \ + -A map_analyser_files/firmware.elf \ + | grep "^$SECTION" | awk '{print $2}' + } + export BSS_SIZE="$(get_size ".bss")" + export TEXT_SIZE="$(get_size ".text")" + export RODATA_SIZE="$(get_size ".rodata")" + export DATA_SIZE="$(get_size ".data")" + export FREE_FLASH_SIZE="$(get_size ".free_flash")" + python3 -m pip install mariadb==1.1.6 cxxfilt==0.3.0 + python3 scripts/map_parser.py map_analyser_files/firmware.elf.map map_analyser_files/firmware.elf.map.all + python3 scripts/map_mariadb_insert.py \ + ${{ secrets.AMAP_MARIADB_USER }} \ + ${{ secrets.AMAP_MARIADB_PASSWORD }} \ + ${{ secrets.AMAP_MARIADB_HOST }} \ + ${{ secrets.AMAP_MARIADB_PORT }} \ + ${{ secrets.AMAP_MARIADB_DATABASE }} \ + map_analyser_files/firmware.elf.map.all - name: 'Upload artifacts to update server' if: ${{ !github.event.pull_request.head.repo.fork }} diff --git a/scripts/map_mariadb_insert.py b/scripts/map_mariadb_insert.py new file mode 100755 index 0000000000..a4c9ed5c78 --- /dev/null +++ b/scripts/map_mariadb_insert.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python3 + +# Requiremets: +# mariadb==1.1.6 + +from datetime import datetime +import argparse +import mariadb +import sys +import os + + +def parseArgs(): + parser = argparse.ArgumentParser() + parser.add_argument("db_user", help="MariaDB user") + parser.add_argument("db_pass", help="MariaDB password") + parser.add_argument("db_host", help="MariaDB hostname") + parser.add_argument("db_port", type=int, help="MariaDB port") + parser.add_argument("db_name", help="MariaDB database") + parser.add_argument("report_file", help="Report file(.map.all)") + args = parser.parse_args() + return args + + +def mariadbConnect(args): + try: + conn = mariadb.connect( + user=args.db_user, + password=args.db_pass, + host=args.db_host, + port=args.db_port, + database=args.db_name, + ) + except mariadb.Error as e: + print(f"Error connecting to MariaDB: {e}") + sys.exit(1) + return conn + + +def parseEnv(): + outArr = [] + outArr.append(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + outArr.append(os.getenv("COMMIT_HASH", default=None)) + outArr.append(os.getenv("COMMIT_MSG", default=None)) + outArr.append(os.getenv("BRANCH_NAME", default=None)) + outArr.append(os.getenv("BSS_SIZE", default=None)) + outArr.append(os.getenv("TEXT_SIZE", default=None)) + outArr.append(os.getenv("RODATA_SIZE", default=None)) + outArr.append(os.getenv("DATA_SIZE", default=None)) + outArr.append(os.getenv("FREE_FLASH_SIZE", default=None)) + outArr.append(os.getenv("PULL_ID", default=None)) + outArr.append(os.getenv("PULL_NAME", default=None)) + return outArr + + +def createTables(cur, conn): + headerTable = "CREATE TABLE IF NOT EXISTS `header` ( \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `datetime` datetime NOT NULL, \ + `commit` varchar(40) NOT NULL, \ + `commit_msg` text NOT NULL, \ + `branch_name` text NOT NULL, \ + `bss_size` int(10) unsigned NOT NULL, \ + `text_size` int(10) unsigned NOT NULL, \ + `rodata_size` int(10) unsigned NOT NULL, \ + `data_size` int(10) unsigned NOT NULL, \ + `free_flash_size` int(10) unsigned NOT NULL, \ + `pullrequest_id` int(10) unsigned DEFAULT NULL, \ + `pullrequest_name` text DEFAULT NULL, \ + PRIMARY KEY (`id`), \ + KEY `header_id_index` (`id`) )" + dataTable = "CREATE TABLE IF NOT EXISTS `data` ( \ + `header_id` int(10) unsigned NOT NULL, \ + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, \ + `section` text NOT NULL, \ + `address` text NOT NULL, \ + `size` int(10) unsigned NOT NULL, \ + `name` text NOT NULL, \ + `lib` text NOT NULL, \ + `obj_name` text NOT NULL, \ + PRIMARY KEY (`id`), \ + KEY `data_id_index` (`id`), \ + KEY `data_header_id_index` (`header_id`), \ + CONSTRAINT `data_header_id_foreign` FOREIGN KEY (`header_id`) REFERENCES `header` (`id`) )" + cur.execute(headerTable) + cur.execute(dataTable) + conn.commit() + + +def insertHeader(data, cur, conn): + query = "INSERT INTO `header` ( \ + datetime, commit, commit_msg, branch_name, bss_size, text_size, \ + rodata_size, data_size, free_flash_size, pullrequest_id, pullrequest_name) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + cur.execute(query, data) + conn.commit() + return cur.lastrowid + + +def parseFile(fileObj, headerID): + arr = [] + fileLines = fileObj.readlines() + for line in fileLines: + lineArr = [] + tempLineArr = line.split("\t") + lineArr.append(headerID) + lineArr.append(tempLineArr[0]) # section + lineArr.append(int(tempLineArr[2], 16)) # address hex + lineArr.append(int(tempLineArr[3])) # size + lineArr.append(tempLineArr[4]) # name + lineArr.append(tempLineArr[5]) # lib + lineArr.append(tempLineArr[6]) # obj_name + arr.append(tuple(lineArr)) + return arr + + +def insertData(data, cur, conn): + query = "INSERT INTO `data` ( \ + header_id, section, address, size, \ + name, lib, obj_name) \ + VALUES (?, ?, ?, ?, ? ,?, ?)" + cur.executemany(query, data) + conn.commit() + + +def main(): + args = parseArgs() + dbConn = mariadbConnect(args) + reportFile = open(args.report_file) + dbCurs = dbConn.cursor() + createTables(dbCurs, dbConn) + headerID = insertHeader(parseEnv(), dbCurs, dbConn) + insertData(parseFile(reportFile, headerID), dbCurs, dbConn) + reportFile.close() + dbCurs.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/map_parser.py b/scripts/map_parser.py new file mode 100755 index 0000000000..c0c34e3d1d --- /dev/null +++ b/scripts/map_parser.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 + +# Requiremets: +# cxxfilt==0.3.0 + +import sys +import re +import os +from typing import TextIO +from cxxfilt import demangle + + +class Objectfile: + def __init__(self, section: str, offset: int, size: int, comment: str): + self.section = section.strip() + self.offset = offset + self.size = size + self.path = (None, None) + self.basepath = None + + if comment: + self.path = re.match(r"^(.+?)(?:\(([^\)]+)\))?$", comment).groups() + self.basepath = os.path.basename(self.path[0]) + + self.children = [] + + def __repr__(self) -> str: + return f"" + + +def update_children_size(children: list[list], subsection_size: int) -> list: + # set subsection size to an only child + if len(children) == 1: + children[0][1] = subsection_size + return children + + rest_size = subsection_size + + for index in range(1, len(children)): + if rest_size > 0: + # current size = current address - previous child address + child_size = children[index][0] - children[index - 1][0] + rest_size -= child_size + children[index - 1][1] = child_size + + # if there is rest size, set it to the last child element + if rest_size > 0: + children[-1][1] = rest_size + + return children + + +def parse_sections(file_name: str) -> list: + """ + Quick&Dirty parsing for GNU ld’s linker map output, needs LANG=C, because + some messages are localized. + """ + + sections = [] + with open(file_name, "r") as file: + # skip until memory map is found + found = False + + while True: + line = file.readline() + if not line: + break + if line.strip() == "Memory Configuration": + found = True + break + + if not found: + raise Exception(f"Memory configuration is not found in the{input_file}") + + # long section names result in a linebreak afterwards + sectionre = re.compile( + "(?P
.+?|.{14,}\n)[ ]+0x(?P[0-9a-f]+)[ ]+0x(?P[0-9a-f]+)(?:[ ]+(?P.+))?\n+", + re.I, + ) + subsectionre = re.compile( + "[ ]{16}0x(?P[0-9a-f]+)[ ]+(?P.+)\n+", re.I + ) + s = file.read() + pos = 0 + + while True: + m = sectionre.match(s, pos) + if not m: + # skip that line + try: + nextpos = s.index("\n", pos) + 1 + pos = nextpos + continue + except ValueError: + break + + pos = m.end() + section = m.group("section") + v = m.group("offset") + offset = int(v, 16) if v is not None else None + v = m.group("size") + size = int(v, 16) if v is not None else None + comment = m.group("comment") + + if section != "*default*" and size > 0: + of = Objectfile(section, offset, size, comment) + + if section.startswith(" "): + children = [] + sections[-1].children.append(of) + + while True: + m = subsectionre.match(s, pos) + if not m: + break + pos = m.end() + offset, function = m.groups() + offset = int(offset, 16) + if sections and sections[-1].children: + children.append([offset, 0, function]) + + if children: + children = update_children_size( + children=children, subsection_size=of.size + ) + + sections[-1].children[-1].children.extend(children) + + else: + sections.append(of) + + return sections + + +def get_subsection_name(section_name: str, subsection: Objectfile) -> str: + subsection_split_names = subsection.section.split(".") + if subsection.section.startswith("."): + subsection_split_names = subsection_split_names[1:] + + return ( + f".{subsection_split_names[1]}" + if len(subsection_split_names) > 2 + else section_name + ) + + +def write_subsection( + section_name: str, + subsection_name: str, + address: str, + size: int, + demangled_name: str, + module_name: str, + file_name: str, + mangled_name: str, + write_file_object: TextIO, +) -> None: + write_file_object.write( + f"{section_name}\t" + f"{subsection_name}\t" + f"{address}\t" + f"{size}\t" + f"{demangled_name}\t" + f"{module_name}\t" + f"{file_name}\t" + f"{mangled_name}\n" + ) + + +def save_subsection( + section_name: str, subsection: Objectfile, write_file_object: TextIO +) -> None: + subsection_name = get_subsection_name(section_name, subsection) + module_name = subsection.path[0] + file_name = subsection.path[1] + + if not file_name: + file_name, module_name = module_name, "" + + if not subsection.children: + address = f"{subsection.offset:x}" + size = subsection.size + mangled_name = ( + "" + if subsection.section == section_name + else subsection.section.split(".")[-1] + ) + demangled_name = demangle(mangled_name) if mangled_name else mangled_name + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + return + + for subsection_child in subsection.children: + address = f"{subsection_child[0]:x}" + size = subsection_child[1] + mangled_name = subsection_child[2] + demangled_name = demangle(mangled_name) + + write_subsection( + section_name=section_name, + subsection_name=subsection_name, + address=address, + size=size, + demangled_name=demangled_name, + module_name=module_name, + file_name=file_name, + mangled_name=mangled_name, + write_file_object=write_file_object, + ) + + +def save_section(section: Objectfile, write_file_object: TextIO) -> None: + section_name = section.section + for subsection in section.children: + save_subsection( + section_name=section_name, + subsection=subsection, + write_file_object=write_file_object, + ) + + +def save_parsed_data(parsed_data: list[Objectfile], output_file_name: str) -> None: + with open(output_file_name, "w") as write_file_object: + for section in parsed_data: + if section.children: + save_section(section=section, write_file_object=write_file_object) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + raise Exception(f"Usage: {sys.argv[0]} ") + + input_file = sys.argv[1] + output_file = sys.argv[2] + + parsed_sections = parse_sections(input_file) + + if parsed_sections is None: + raise Exception(f"Memory configuration is not {input_file}") + + save_parsed_data(parsed_sections, output_file) From ab86f58643be1a9d478c220c95447958cf72c194 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 2 Jun 2023 16:42:58 +0300 Subject: [PATCH 042/102] Fuzzers App: gui start --- .../external/pacs_fuzzer/application.fam | 19 ++++ applications/external/pacs_fuzzer/fuzzer.c | 78 +++++++++++++++ applications/external/pacs_fuzzer/fuzzer_i.h | 21 ++++ .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 8 ++ .../pacs_fuzzer/helpers/fuzzer_types.h | 8 ++ .../external/pacs_fuzzer/rfid_10px.png | Bin 0 -> 2389 bytes .../pacs_fuzzer/scenes/fuzzer_scene.c | 30 ++++++ .../pacs_fuzzer/scenes/fuzzer_scene.h | 29 ++++++ .../pacs_fuzzer/scenes/fuzzer_scene_config.h | 1 + .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 41 ++++++++ .../external/pacs_fuzzer/views/main_menu.c | 94 ++++++++++++++++++ .../external/pacs_fuzzer/views/main_menu.h | 26 +++++ 12 files changed, 355 insertions(+) create mode 100644 applications/external/pacs_fuzzer/application.fam create mode 100644 applications/external/pacs_fuzzer/fuzzer.c create mode 100644 applications/external/pacs_fuzzer/fuzzer_i.h create mode 100644 applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h create mode 100644 applications/external/pacs_fuzzer/helpers/fuzzer_types.h create mode 100644 applications/external/pacs_fuzzer/rfid_10px.png create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene.c create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene.h create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c create mode 100644 applications/external/pacs_fuzzer/views/main_menu.c create mode 100644 applications/external/pacs_fuzzer/views/main_menu.h diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam new file mode 100644 index 0000000000..5e0aafd994 --- /dev/null +++ b/applications/external/pacs_fuzzer/application.fam @@ -0,0 +1,19 @@ +App( + appid="pacs_fuzzer", + name="Fuzzer Gui", + apptype=FlipperAppType.EXTERNAL, + entry_point="fuzzer_start", + requires=[ + "gui", + "storage", + "dialogs", + "input", + "notification", + ], + stack_size=2 * 1024, + order=15, + fap_icon="rfid_10px.png", + fap_category="Debug", + # fap_icon_assets="images", + # fap_icon_assets_symbol="fuzzer", +) diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c new file mode 100644 index 0000000000..ac0400ae5c --- /dev/null +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -0,0 +1,78 @@ +#include "fuzzer_i.h" +#include "helpers/fuzzer_types.h" + +static bool fuzzer_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + PacsFuzzerApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool fuzzer_app_back_event_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void fuzzer_app_tick_event_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +PacsFuzzerApp* fuzzer_app_alloc() { + PacsFuzzerApp* app = malloc(sizeof(PacsFuzzerApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + + // Main view + app->main_view = fuzzer_view_main_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FuzzerViewIDMain, fuzzer_view_main_get_view(app->main_view)); + + app->scene_manager = scene_manager_alloc(&fuzzer_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, fuzzer_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, fuzzer_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, fuzzer_app_tick_event_callback, 100); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + scene_manager_next_scene(app->scene_manager, FuzzerSceneMain); + + return app; +} + +void fuzzer_app_free(PacsFuzzerApp* app) { + furi_assert(app); + + // Remote view + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDMain); + fuzzer_view_main_free(app->main_view); + + scene_manager_free(app->scene_manager); + view_dispatcher_free(app->view_dispatcher); + + // Close records + furi_record_close(RECORD_GUI); + + free(app); +} + +int32_t fuzzer_start(void* p) { + UNUSED(p); + PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); + + view_dispatcher_run(fuzzer_app->view_dispatcher); + + fuzzer_app_free(fuzzer_app); + return 0; +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h new file mode 100644 index 0000000000..f1ed6e738b --- /dev/null +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include + +#include "scenes/fuzzer_scene.h" +#include "views/main_menu.h" + +#include "helpers/fuzzer_types.h" + +#include + +typedef struct { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + FuzzerViewMain* main_view; + +} PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h new file mode 100644 index 0000000000..2ef8fce56e --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -0,0 +1,8 @@ +#pragma once + +typedef enum { + + // FuzzerCustomEvent + FuzzerCustomEventViewMainOk = 100, + FuzzerCustomEventViewMainBack, +} FuzzerCustomEvent; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h new file mode 100644 index 0000000000..7b6f5a6800 --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +typedef enum { + FuzzerViewIDMain, +} FuzzerViewID; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/rfid_10px.png b/applications/external/pacs_fuzzer/rfid_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..8097f477552333f695733baf98f72e52ae840206 GIT binary patch literal 2389 zcmcIleQXnD7{6^!#>U44<0gnZixCj6_tCqycdm@C?Y2%j%BZlR1g>}Qb?4f<$dYAbBp?=L>s9H44zi+G=z@ z$+GSR2r_^Bu5APL?}u;SJTN7D;oJYQXJbzy83Gd~}8wyVLJ?VRDO5w9Zzw8@g?;6Z|IeLEIrsbQiG#zUHZ2RGh zXD*aUd-8i4_PpADs0}HdxBi3jq2Z#Q^P&Dr^r?|WO%j;%cM#(P)0|Mc8; z{t8g`*A^{V)i?clE}f@2H&CjT}F< z?2jues!qd7PS)z04FoBfX?^mLy)Tp_$Rt&cG?`7IrJSH9?7UT9dorQHXauRON@~2& z3QRN#VzT0~4fhY&P+9cYRxu$Wr1?OLT-T+86?9@-1c8#I)9z+Pr-LUJp%g)pI7#A^ z8>2{$U^#~a&AepaJ-|V!`|Vrt9lHFc42XX!YK-a5tz}b zn0yjbjJa6^KQIJc)=XJdPz#Zds%@sn2DzpWkAYyf`V1Rfhy zjlu{PBk2f5aSnoGTnh;YM-b`I5OjjboBR#IOoSjf8osX&Rz+FroJeRW#03?@@6WtU}<<5`k-GmIOPTuus$(zJDPkQf=2B!Xdi7eP5vyx@MnDzsUZu=b~oE2;v- z$W@bzGC*KNw}3fBr-TVK?Z%=6L1S(pkqisLNim1EOqXHr@bS^87Ap}ViVmI>S)RcJ zh9WV*(*TPy(I`d}G$F90;3Qy6q1W>I)VQjLR1sDe;)?<&sd|Ek{*e=W4B(m)v)l~P z;VJ5514`GK>5mm)eP$Jx(Uj>pUa-9Gu?d#Q0Om>GmdB{x#CWFnceDTqI*$11FhiBh z4qgY|7_9WanhU=fd4q2spSnP~C5On1n8Y&u^S%9C@(-&evej?~Ro2+Su!z(GxDJJ~1-%>yfuE?__2!EG(Q-w?lmFd+!g0U&StM zdu5hu`a91J8fzAOad#fF`0WbM%BH;qRp;*CJ^0}3t1p(6l^kTQe)IdHQfq1l0}cMe I)$5-48`f?Vpa1{> literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene.c new file mode 100644 index 0000000000..0fe0f558dc --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene.c @@ -0,0 +1,30 @@ +#include "fuzzer_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const fuzzer_scene_on_enter_handlers[])(void*) = { +#include "fuzzer_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const fuzzer_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "fuzzer_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const fuzzer_scene_on_exit_handlers[])(void* context) = { +#include "fuzzer_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers fuzzer_scene_handlers = { + .on_enter_handlers = fuzzer_scene_on_enter_handlers, + .on_event_handlers = fuzzer_scene_on_event_handlers, + .on_exit_handlers = fuzzer_scene_on_exit_handlers, + .scene_num = FuzzerSceneNum, +}; diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene.h b/applications/external/pacs_fuzzer/scenes/fuzzer_scene.h new file mode 100644 index 0000000000..2806545105 --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FuzzerScene##id, +typedef enum { +#include "fuzzer_scene_config.h" + FuzzerSceneNum, +} FuzzerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers fuzzer_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "fuzzer_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "fuzzer_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "fuzzer_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h new file mode 100644 index 0000000000..7923cd83eb --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h @@ -0,0 +1 @@ +ADD_SCENE(fuzzer, main, Main) \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c new file mode 100644 index 0000000000..994867ed37 --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -0,0 +1,41 @@ +#include "../fuzzer_i.h" +#include "../helpers/fuzzer_custom_event.h" + +void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void fuzzer_scene_main_on_enter(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_view_main_set_callback(app->main_view, fuzzer_scene_main_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); +} + +bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + PacsFuzzerApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FuzzerCustomEventViewMainBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } + } + + return consumed; +} + +void fuzzer_scene_main_on_exit(void* context) { + // furi_assert(context); + // PacsFuzzerApp* app = context; + UNUSED(context); +} diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c new file mode 100644 index 0000000000..7db7d26f39 --- /dev/null +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -0,0 +1,94 @@ +#include "main_menu.h" +#include "../fuzzer_i.h" + +#include +#include + +struct FuzzerViewMain { + View* view; + FuzzerViewMainCallback callback; + void* context; +}; + +typedef struct { + uint8_t proto_index; + uint8_t menu_index; +} FuzzerViewMainModel; + +void fuzzer_view_main_set_callback( + FuzzerViewMain* fuzzer_view_main, + FuzzerViewMainCallback callback, + void* context) { + furi_assert(fuzzer_view_main); + + fuzzer_view_main->callback = callback; + fuzzer_view_main->context = context; +} + +void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { + UNUSED(canvas); + UNUSED(model); +} + +bool fuzzer_view_main_input(InputEvent* event, void* context) { + furi_assert(context); + FuzzerViewMain* fuzzer_view_main = context; + + if(event->key == InputKeyBack && + (event->type == InputTypeLong || event->type == InputTypeShort)) { + fuzzer_view_main->callback(FuzzerCustomEventViewMainBack, fuzzer_view_main->context); + return true; + } + + return true; +} + +void fuzzer_view_main_enter(void* context) { + furi_assert(context); +} + +void fuzzer_view_main_exit(void* context) { + furi_assert(context); +} + +FuzzerViewMain* fuzzer_view_main_alloc() { + FuzzerViewMain* fuzzer_view_main = malloc(sizeof(FuzzerViewMain)); + + // View allocation and configuration + fuzzer_view_main->view = view_alloc(); + view_allocate_model(fuzzer_view_main->view, ViewModelTypeLocking, sizeof(FuzzerViewMainModel)); + view_set_context(fuzzer_view_main->view, fuzzer_view_main); + view_set_draw_callback(fuzzer_view_main->view, (ViewDrawCallback)fuzzer_view_main_draw); + view_set_input_callback(fuzzer_view_main->view, fuzzer_view_main_input); + view_set_enter_callback(fuzzer_view_main->view, fuzzer_view_main_enter); + view_set_exit_callback(fuzzer_view_main->view, fuzzer_view_main_exit); + + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + model->proto_index = 0; + model->menu_index = 0; + }, + true); + return fuzzer_view_main; +} + +void fuzzer_view_main_free(FuzzerViewMain* fuzzer_view_main) { + furi_assert(fuzzer_view_main); + + // with_view_model( + // fuzzer_view_main->view, + // FuzzerViewMainModel * model, + // { + + // }, + // true); + view_free(fuzzer_view_main->view); + free(fuzzer_view_main); +} + +View* fuzzer_view_main_get_view(FuzzerViewMain* fuzzer_view_main) { + furi_assert(fuzzer_view_main); + return fuzzer_view_main->view; +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.h b/applications/external/pacs_fuzzer/views/main_menu.h new file mode 100644 index 0000000000..17631807f3 --- /dev/null +++ b/applications/external/pacs_fuzzer/views/main_menu.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "../helpers/fuzzer_custom_event.h" + +typedef enum { + FuzzerViewMainStateIdle, + FuzzerViewMainStateLoading, + FuzzerViewMainStateSending, + FuzzerViewMainStateOFF, +} FuzzerViewMainState; + +typedef struct FuzzerViewMain FuzzerViewMain; + +typedef void (*FuzzerViewMainCallback)(FuzzerCustomEvent event, void* context); + +void fuzzer_view_main_set_callback( + FuzzerViewMain* fuzzer_view_main, + FuzzerViewMainCallback callback, + void* context); + +FuzzerViewMain* fuzzer_view_main_alloc(); + +void fuzzer_view_main_free(FuzzerViewMain* fuzzer_view_main); + +View* fuzzer_view_main_get_view(FuzzerViewMain* fuzzer_view_main); \ No newline at end of file From 321f2d8d504e8362543f36e5c0337bd000b89eac Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 2 Jun 2023 17:48:21 +0300 Subject: [PATCH 043/102] Fuzzers app: main menu v0 --- .../pacs_fuzzer/helpers/fuzzer_types.h | 9 ++ .../external/pacs_fuzzer/helpers/protocol.c | 92 +++++++++++++++++++ .../external/pacs_fuzzer/helpers/protocol.h | 28 ++++++ .../external/pacs_fuzzer/views/main_menu.c | 83 ++++++++++++++++- 4 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 applications/external/pacs_fuzzer/helpers/protocol.c create mode 100644 applications/external/pacs_fuzzer/helpers/protocol.h diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index 7b6f5a6800..cfc9274d1f 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -3,6 +3,15 @@ #include #include +// TODO replace it +typedef enum { + FuzzerMainMenuIndexDefaultValues = 0, + FuzzerMainMenuIndexLoadFile, + FuzzerMainMenuIndexLoadFileCustomUids, + + FuzzerMainMenuIndexMax, +} FuzzerMainMenuIndex; + typedef enum { FuzzerViewIDMain, } FuzzerViewID; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.c b/applications/external/pacs_fuzzer/helpers/protocol.c new file mode 100644 index 0000000000..0727491799 --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/protocol.c @@ -0,0 +1,92 @@ +#include "protocol.h" + +#define DS1990_DATA_SIZE (8) +#define Metakom_DATA_SIZE (4) +#define Cyfral_DATA_SIZE (2) + +const uint8_t uid_list_ds1990[][DS1990_DATA_SIZE] = { + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал + {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах + {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% + {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) + {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF + {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 + {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni +}; + +const uint8_t uid_list_metakom[][Metakom_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d}, // ?? + {0x34, 0x00, 0x29, 0x3d}, // ?? + {0x04, 0xdf, 0x00, 0x00}, // ?? + {0xCA, 0xCA, 0xCA, 0xCA}, // ?? +}; + +const uint8_t uid_list_cyfral[][Cyfral_DATA_SIZE] = { + {0x00, 0x00}, // Null bytes + {0xFF, 0xFF}, // Only FF + {0x11, 0x11}, // Only 11 + {0x22, 0x22}, // Only 22 + {0x33, 0x33}, // Only 33 + {0x44, 0x44}, // Only 44 + {0x55, 0x55}, // Only 55 + {0x66, 0x66}, // Only 66 + {0x77, 0x77}, // Only 77 + {0x88, 0x88}, // Only 88 + {0x99, 0x99}, // Only 99 + {0x12, 0x34}, // Incremental UID + {0x56, 0x34}, // Decremental UID + {0xCA, 0xCA}, // ?? + {0x8E, 0xC9}, // Elevator code + {0x6A, 0x50}, // VERY fresh code from smartkey +}; + +const FuzzerProtocol fuzzer_proto_items[] = { + [DS1990] = + { + .name = "DS1990", + .data_size = DS1990_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_ds1990, + .len = sizeof(uid_list_ds1990) / DS1990_DATA_SIZE}, + }, + [Metakom] = + { + .name = "Metakom", + .data_size = Metakom_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_metakom, + .len = sizeof(uid_list_metakom) / Metakom_DATA_SIZE}, + }, + [Cyfral] = + { + .name = "Cyfral", + .data_size = Cyfral_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_cyfral, + .len = sizeof(uid_list_cyfral) / Cyfral_DATA_SIZE}, + }, +}; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.h b/applications/external/pacs_fuzzer/helpers/protocol.h new file mode 100644 index 0000000000..c43e7f128b --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/protocol.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +typedef enum { + DS1990, + Metakom, + Cyfral, + // Reserved + FuzzerProtoMax, +} FuzzerProtos; + +struct ProtoDict { + const uint8_t* val; + const uint8_t len; +}; + +typedef struct ProtoDict ProtoDict; + +struct FuzzerProtocol { + const char* name; + const uint8_t data_size; + const ProtoDict dict; +}; + +typedef struct FuzzerProtocol FuzzerProtocol; + +extern const FuzzerProtocol fuzzer_proto_items[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 7db7d26f39..2d55cc143b 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -4,12 +4,21 @@ #include #include +#include "../helpers/protocol.h" + +const char* main_menu_items[FuzzerMainMenuIndexMax] = { + [FuzzerMainMenuIndexDefaultValues] = "Default Values", + [FuzzerMainMenuIndexLoadFile] = "Load File", + [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", +}; + struct FuzzerViewMain { View* view; FuzzerViewMainCallback callback; void* context; }; +// TODO Furi string for procol name typedef struct { uint8_t proto_index; uint8_t menu_index; @@ -26,8 +35,30 @@ void fuzzer_view_main_set_callback( } void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { - UNUSED(canvas); - UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(model->menu_index > 0) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 24, AlignCenter, AlignTop, main_menu_items[model->menu_index - 1]); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 64, 36, AlignCenter, AlignTop, main_menu_items[model->menu_index]); + + if(model->menu_index < FuzzerMainMenuIndexMax) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 48, AlignCenter, AlignTop, main_menu_items[model->menu_index + 1]); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); + canvas_draw_str_aligned( + canvas, 64, 4, AlignCenter, AlignTop, fuzzer_proto_items[model->proto_index].name); + canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); } bool fuzzer_view_main_input(InputEvent* event, void* context) { @@ -38,6 +69,54 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { (event->type == InputTypeLong || event->type == InputTypeShort)) { fuzzer_view_main->callback(FuzzerCustomEventViewMainBack, fuzzer_view_main->context); return true; + } else if(event->key == InputKeyDown && event->type == InputTypeShort) { + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + if(model->menu_index < (FuzzerMainMenuIndexMax - 1)) { + model->menu_index++; + } + }, + true); + return true; + } else if(event->key == InputKeyUp && event->type == InputTypeShort) { + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + if(model->menu_index != 0) { + model->menu_index--; + } + }, + true); + return true; + } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + if(model->proto_index != 0) { + model->proto_index--; + } else { + model->proto_index = (FuzzerProtoMax - 1); + } + }, + true); + return true; + } else if(event->key == InputKeyRight && event->type == InputTypeShort) { + with_view_model( + fuzzer_view_main->view, + FuzzerViewMainModel * model, + { + if(model->proto_index == (FuzzerProtoMax - 1)) { + model->proto_index = 0; + } else { + model->proto_index++; + } + }, + true); + return true; } return true; From e31a0c4d6d7d144b67e64e621e5bd5c70e3210e9 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Fri, 2 Jun 2023 20:33:28 +0300 Subject: [PATCH 044/102] Fuzzers App: attack gui --- applications/external/pacs_fuzzer/fuzzer.c | 12 + applications/external/pacs_fuzzer/fuzzer_i.h | 4 + .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 7 +- .../pacs_fuzzer/helpers/fuzzer_types.h | 13 +- .../external/pacs_fuzzer/helpers/gui_const.c | 7 + .../external/pacs_fuzzer/helpers/gui_const.h | 12 + .../external/pacs_fuzzer/helpers/protocol.h | 4 + .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 53 +++++ .../pacs_fuzzer/scenes/fuzzer_scene_config.h | 3 +- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 6 + .../external/pacs_fuzzer/views/attack.c | 212 ++++++++++++++++++ .../external/pacs_fuzzer/views/attack.h | 27 +++ .../external/pacs_fuzzer/views/main_menu.c | 98 ++++---- .../external/pacs_fuzzer/views/main_menu.h | 15 +- 14 files changed, 415 insertions(+), 58 deletions(-) create mode 100644 applications/external/pacs_fuzzer/helpers/gui_const.c create mode 100644 applications/external/pacs_fuzzer/helpers/gui_const.h create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c create mode 100644 applications/external/pacs_fuzzer/views/attack.c create mode 100644 applications/external/pacs_fuzzer/views/attack.h diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index ac0400ae5c..42c7d1e472 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -22,6 +22,9 @@ static void fuzzer_app_tick_event_callback(void* context) { PacsFuzzerApp* fuzzer_app_alloc() { PacsFuzzerApp* app = malloc(sizeof(PacsFuzzerApp)); + app->fuzzer_state.menu_index = 0; + app->fuzzer_state.proto_index = 0; + // GUI app->gui = furi_record_open(RECORD_GUI); @@ -33,6 +36,11 @@ PacsFuzzerApp* fuzzer_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, FuzzerViewIDMain, fuzzer_view_main_get_view(app->main_view)); + // Attack view + app->attack_view = fuzzer_view_attack_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FuzzerViewIDAttack, fuzzer_view_attack_get_view(app->attack_view)); + app->scene_manager = scene_manager_alloc(&fuzzer_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); @@ -58,6 +66,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) { view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDMain); fuzzer_view_main_free(app->main_view); + // Attack view + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDAttack); + fuzzer_view_attack_free(app->attack_view); + scene_manager_free(app->scene_manager); view_dispatcher_free(app->view_dispatcher); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index f1ed6e738b..8f84a558df 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -7,6 +7,7 @@ #include "scenes/fuzzer_scene.h" #include "views/main_menu.h" +#include "views/attack.h" #include "helpers/fuzzer_types.h" @@ -16,6 +17,9 @@ typedef struct { Gui* gui; ViewDispatcher* view_dispatcher; SceneManager* scene_manager; + FuzzerViewMain* main_view; + FuzzerViewAttack* attack_view; + FuzzerState fuzzer_state; } PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h index 2ef8fce56e..189d72ef43 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -3,6 +3,9 @@ typedef enum { // FuzzerCustomEvent - FuzzerCustomEventViewMainOk = 100, - FuzzerCustomEventViewMainBack, + FuzzerCustomEventViewMainBack = 100, + FuzzerCustomEventViewMainOk, + + FuzzerCustomEventViewAttackBack, + FuzzerCustomEventViewAttackOk, } FuzzerCustomEvent; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index cfc9274d1f..55c64954ca 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -3,15 +3,12 @@ #include #include -// TODO replace it -typedef enum { - FuzzerMainMenuIndexDefaultValues = 0, - FuzzerMainMenuIndexLoadFile, - FuzzerMainMenuIndexLoadFileCustomUids, - - FuzzerMainMenuIndexMax, -} FuzzerMainMenuIndex; +typedef struct { + uint8_t menu_index; + uint8_t proto_index; +} FuzzerState; typedef enum { FuzzerViewIDMain, + FuzzerViewIDAttack, } FuzzerViewID; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/gui_const.c b/applications/external/pacs_fuzzer/helpers/gui_const.c new file mode 100644 index 0000000000..79e31ad1b1 --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/gui_const.c @@ -0,0 +1,7 @@ +#include "gui_const.h" + +const char* fuzzer_attack_names[FuzzerMainMenuIndexMax] = { + [FuzzerMainMenuIndexDefaultValues] = "Default Values", + [FuzzerMainMenuIndexLoadFile] = "Load File", + [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", +}; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/gui_const.h b/applications/external/pacs_fuzzer/helpers/gui_const.h new file mode 100644 index 0000000000..837322c2ae --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/gui_const.h @@ -0,0 +1,12 @@ +#pragma once + +// TODO replace it +typedef enum { + FuzzerMainMenuIndexDefaultValues = 0, + FuzzerMainMenuIndexLoadFile, + FuzzerMainMenuIndexLoadFileCustomUids, + + FuzzerMainMenuIndexMax, +} FuzzerMainMenuIndex; + +extern const char* fuzzer_attack_names[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.h b/applications/external/pacs_fuzzer/helpers/protocol.h index c43e7f128b..aedeedd8d2 100644 --- a/applications/external/pacs_fuzzer/helpers/protocol.h +++ b/applications/external/pacs_fuzzer/helpers/protocol.h @@ -2,6 +2,10 @@ #include +#define FUZZ_TIME_DELAY_MIN (4) +#define FUZZ_TIME_DELAY_DEFAULT (8) +#define FUZZ_TIME_DELAY_MAX (80) + typedef enum { DS1990, Metakom, diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c new file mode 100644 index 0000000000..71d38650c2 --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -0,0 +1,53 @@ +#include "../fuzzer_i.h" +#include "../helpers/fuzzer_custom_event.h" + +#include "../helpers/protocol.h" +#include "../helpers/gui_const.h" + +void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void fuzzer_scene_attack_on_enter(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_view_attack_set_callback(app->attack_view, fuzzer_scene_attack_callback, app); + + FuzzerProtocol proto = fuzzer_proto_items[app->fuzzer_state.proto_index]; + + fuzzer_view_attack_reset_data( + app->attack_view, + fuzzer_attack_names[app->fuzzer_state.menu_index], + proto.name, + proto.data_size); + fuzzer_view_attack_set_uid(app->attack_view, &proto.dict.val[0], false); + + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDAttack); +} + +bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + PacsFuzzerApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FuzzerCustomEventViewAttackBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } + } + + return consumed; +} + +void fuzzer_scene_attack_on_exit(void* context) { + // furi_assert(context); + // PacsFuzzerApp* app = context; + UNUSED(context); +} diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h index 7923cd83eb..bccdbd9a5d 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h @@ -1 +1,2 @@ -ADD_SCENE(fuzzer, main, Main) \ No newline at end of file +ADD_SCENE(fuzzer, main, Main) +ADD_SCENE(fuzzer, attack, Attack) \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 994867ed37..812e063ab6 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -13,6 +13,8 @@ void fuzzer_scene_main_on_enter(void* context) { fuzzer_view_main_set_callback(app->main_view, fuzzer_scene_main_callback, app); + fuzzer_view_main_update_data(app->main_view, app->fuzzer_state); + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); } @@ -28,6 +30,10 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(app->view_dispatcher); } consumed = true; + } else if(event.event == FuzzerCustomEventViewMainOk) { + fuzzer_view_main_get_state(app->main_view, &app->fuzzer_state); + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + consumed = true; } } diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c new file mode 100644 index 0000000000..a83740f3dd --- /dev/null +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -0,0 +1,212 @@ +#include "attack.h" +#include "../fuzzer_i.h" + +#include +#include + +#include "../helpers/protocol.h" + +#define ATTACK_SCENE_MAX_UID_LENGTH 25 + +struct FuzzerViewAttack { + View* view; + FuzzerViewAttackCallback callback; + void* context; +}; + +typedef struct { + uint8_t time_delay; + const char* attack_name; + const char* protocol_name; + bool attack_enabled; + char* uid; + uint8_t uid_size; +} FuzzerViewAttackModel; + +void fuzzer_view_attack_reset_data( + FuzzerViewAttack* view, + const char* attack_name, + const char* protocol_name, + uint8_t uid_size) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { + model->attack_name = attack_name; + model->protocol_name = protocol_name; + model->attack_enabled = false; + model->uid_size = uid_size; + }, + true); +} + +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool attack) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { + snprintf( + model->uid, + model->uid_size * 3, + "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", + uid[0], + uid[1], + uid[2], + uid[3], + uid[4], + uid[5], + uid[6], + uid[7]); + model->attack_enabled = attack; + }, + true); +} + +void fuzzer_view_attack_set_callback( + FuzzerViewAttack* view_attack, + FuzzerViewAttackCallback callback, + void* context) { + furi_assert(view_attack); + + view_attack->callback = callback; + view_attack->context = context; +} + +void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { + char time_delay[16]; + snprintf(time_delay, sizeof(time_delay), "Time delay: %d", model->time_delay); + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->attack_name); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, time_delay); + canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name); + + canvas_set_font(canvas, FontPrimary); + if(128 < canvas_string_width(canvas, model->uid)) { + canvas_set_font(canvas, FontSecondary); + } + canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, model->uid); + + if(model->attack_enabled) { + elements_button_center(canvas, "Stop"); + } else { + elements_button_center(canvas, "Start"); + elements_button_left(canvas, "TD -"); + elements_button_right(canvas, "+ TD"); + } +} + +bool fuzzer_view_attack_input(InputEvent* event, void* context) { + furi_assert(context); + FuzzerViewAttack* view_attack = context; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + view_attack->callback(FuzzerCustomEventViewAttackOk, view_attack->context); + return true; + } else if(event->key == InputKeyLeft) { + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { + if(!model->attack_enabled) { + if(event->type == InputTypeShort) { + if(model->time_delay > FUZZ_TIME_DELAY_MIN) { + model->time_delay--; + } + } else if(event->type == InputTypeLong) { + if((model->time_delay - 10) >= FUZZ_TIME_DELAY_MIN) { + model->time_delay -= 10; + } else { + model->time_delay = FUZZ_TIME_DELAY_MIN; + } + } + } + }, + true); + return true; + } else if(event->key == InputKeyRight) { + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { + if(!model->attack_enabled) { + if(event->type == InputTypeShort) { + if(model->time_delay < FUZZ_TIME_DELAY_MAX) { + model->time_delay++; + } + } else if(event->type == InputTypeLong) { + model->time_delay += 10; + if(model->time_delay > FUZZ_TIME_DELAY_MAX) { + model->time_delay = FUZZ_TIME_DELAY_MAX; + } + } + } + }, + true); + return true; + } + + return true; +} + +void fuzzer_view_attack_enter(void* context) { + furi_assert(context); +} + +void fuzzer_view_attack_exit(void* context) { + furi_assert(context); +} + +FuzzerViewAttack* fuzzer_view_attack_alloc() { + FuzzerViewAttack* view_attack = malloc(sizeof(FuzzerViewAttack)); + + // View allocation and configuration + view_attack->view = view_alloc(); + view_allocate_model(view_attack->view, ViewModelTypeLocking, sizeof(FuzzerViewAttackModel)); + view_set_context(view_attack->view, view_attack); + view_set_draw_callback(view_attack->view, (ViewDrawCallback)fuzzer_view_attack_draw); + view_set_input_callback(view_attack->view, fuzzer_view_attack_input); + view_set_enter_callback(view_attack->view, fuzzer_view_attack_enter); + view_set_exit_callback(view_attack->view, fuzzer_view_attack_exit); + + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { + model->time_delay = FUZZ_TIME_DELAY_MIN; + model->uid = malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); + model->attack_enabled = false; + + strcpy(model->uid, "Not_set"); + model->attack_name = "Not_set"; + model->protocol_name = "Not_set"; + }, + true); + return view_attack; +} + +void fuzzer_view_attack_free(FuzzerViewAttack* view_attack) { + furi_assert(view_attack); + + with_view_model( + view_attack->view, FuzzerViewAttackModel * model, { free(model->uid); }, true); + view_free(view_attack->view); + free(view_attack); +} + +View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack) { + furi_assert(view_attack); + return view_attack->view; +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h new file mode 100644 index 0000000000..082b7ba365 --- /dev/null +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include "../helpers/fuzzer_custom_event.h" + +typedef struct FuzzerViewAttack FuzzerViewAttack; + +typedef void (*FuzzerViewAttackCallback)(FuzzerCustomEvent event, void* context); + +void fuzzer_view_attack_set_callback( + FuzzerViewAttack* view_attack, + FuzzerViewAttackCallback callback, + void* context); + +FuzzerViewAttack* fuzzer_view_attack_alloc(); + +void fuzzer_view_attack_free(FuzzerViewAttack* view_attack); + +View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack); + +void fuzzer_view_attack_reset_data( + FuzzerViewAttack* view, + const char* attack_name, + const char* protocol_name, + uint8_t uid_size); + +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool attack); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 2d55cc143b..c037fdf44d 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -2,15 +2,10 @@ #include "../fuzzer_i.h" #include -#include +// #include #include "../helpers/protocol.h" - -const char* main_menu_items[FuzzerMainMenuIndexMax] = { - [FuzzerMainMenuIndexDefaultValues] = "Default Values", - [FuzzerMainMenuIndexLoadFile] = "Load File", - [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", -}; +#include "../helpers/gui_const.h" struct FuzzerViewMain { View* view; @@ -24,14 +19,38 @@ typedef struct { uint8_t menu_index; } FuzzerViewMainModel; +void fuzzer_view_main_update_data(FuzzerViewMain* view, FuzzerState state) { + furi_assert(view); + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + model->proto_index = state.proto_index; + model->menu_index = state.menu_index; + }, + true); +} + +void fuzzer_view_main_get_state(FuzzerViewMain* view, FuzzerState* state) { + furi_assert(view); + with_view_model( + view->view, + FuzzerViewMainModel * model, + { + state->proto_index = model->proto_index; + state->menu_index = model->menu_index; + }, + true); +} + void fuzzer_view_main_set_callback( - FuzzerViewMain* fuzzer_view_main, + FuzzerViewMain* view, FuzzerViewMainCallback callback, void* context) { - furi_assert(fuzzer_view_main); + furi_assert(view); - fuzzer_view_main->callback = callback; - fuzzer_view_main->context = context; + view->callback = callback; + view->context = context; } void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { @@ -41,17 +60,17 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { if(model->menu_index > 0) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, 64, 24, AlignCenter, AlignTop, main_menu_items[model->menu_index - 1]); + canvas, 64, 24, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index - 1]); } canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned( - canvas, 64, 36, AlignCenter, AlignTop, main_menu_items[model->menu_index]); + canvas, 64, 36, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index]); if(model->menu_index < FuzzerMainMenuIndexMax) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, 64, 48, AlignCenter, AlignTop, main_menu_items[model->menu_index + 1]); + canvas, 64, 48, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index + 1]); } canvas_set_font(canvas, FontPrimary); @@ -63,15 +82,18 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { bool fuzzer_view_main_input(InputEvent* event, void* context) { furi_assert(context); - FuzzerViewMain* fuzzer_view_main = context; + FuzzerViewMain* view = context; if(event->key == InputKeyBack && (event->type == InputTypeLong || event->type == InputTypeShort)) { - fuzzer_view_main->callback(FuzzerCustomEventViewMainBack, fuzzer_view_main->context); + view->callback(FuzzerCustomEventViewMainBack, view->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + view->callback(FuzzerCustomEventViewMainOk, view->context); return true; } else if(event->key == InputKeyDown && event->type == InputTypeShort) { with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { if(model->menu_index < (FuzzerMainMenuIndexMax - 1)) { @@ -82,7 +104,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { return true; } else if(event->key == InputKeyUp && event->type == InputTypeShort) { with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { if(model->menu_index != 0) { @@ -93,7 +115,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { return true; } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { if(model->proto_index != 0) { @@ -106,7 +128,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { return true; } else if(event->key == InputKeyRight && event->type == InputTypeShort) { with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { if(model->proto_index == (FuzzerProtoMax - 1)) { @@ -131,43 +153,43 @@ void fuzzer_view_main_exit(void* context) { } FuzzerViewMain* fuzzer_view_main_alloc() { - FuzzerViewMain* fuzzer_view_main = malloc(sizeof(FuzzerViewMain)); + FuzzerViewMain* view = malloc(sizeof(FuzzerViewMain)); // View allocation and configuration - fuzzer_view_main->view = view_alloc(); - view_allocate_model(fuzzer_view_main->view, ViewModelTypeLocking, sizeof(FuzzerViewMainModel)); - view_set_context(fuzzer_view_main->view, fuzzer_view_main); - view_set_draw_callback(fuzzer_view_main->view, (ViewDrawCallback)fuzzer_view_main_draw); - view_set_input_callback(fuzzer_view_main->view, fuzzer_view_main_input); - view_set_enter_callback(fuzzer_view_main->view, fuzzer_view_main_enter); - view_set_exit_callback(fuzzer_view_main->view, fuzzer_view_main_exit); + view->view = view_alloc(); + view_allocate_model(view->view, ViewModelTypeLocking, sizeof(FuzzerViewMainModel)); + view_set_context(view->view, view); + view_set_draw_callback(view->view, (ViewDrawCallback)fuzzer_view_main_draw); + view_set_input_callback(view->view, fuzzer_view_main_input); + view_set_enter_callback(view->view, fuzzer_view_main_enter); + view_set_exit_callback(view->view, fuzzer_view_main_exit); with_view_model( - fuzzer_view_main->view, + view->view, FuzzerViewMainModel * model, { model->proto_index = 0; model->menu_index = 0; }, true); - return fuzzer_view_main; + return view; } -void fuzzer_view_main_free(FuzzerViewMain* fuzzer_view_main) { - furi_assert(fuzzer_view_main); +void fuzzer_view_main_free(FuzzerViewMain* view) { + furi_assert(view); // with_view_model( - // fuzzer_view_main->view, + // view->view, // FuzzerViewMainModel * model, // { // }, // true); - view_free(fuzzer_view_main->view); - free(fuzzer_view_main); + view_free(view->view); + free(view); } -View* fuzzer_view_main_get_view(FuzzerViewMain* fuzzer_view_main) { - furi_assert(fuzzer_view_main); - return fuzzer_view_main->view; +View* fuzzer_view_main_get_view(FuzzerViewMain* view) { + furi_assert(view); + return view->view; } \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.h b/applications/external/pacs_fuzzer/views/main_menu.h index 17631807f3..262d54405b 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.h +++ b/applications/external/pacs_fuzzer/views/main_menu.h @@ -2,13 +2,7 @@ #include #include "../helpers/fuzzer_custom_event.h" - -typedef enum { - FuzzerViewMainStateIdle, - FuzzerViewMainStateLoading, - FuzzerViewMainStateSending, - FuzzerViewMainStateOFF, -} FuzzerViewMainState; +#include "../helpers/fuzzer_types.h" typedef struct FuzzerViewMain FuzzerViewMain; @@ -21,6 +15,9 @@ void fuzzer_view_main_set_callback( FuzzerViewMain* fuzzer_view_main_alloc(); -void fuzzer_view_main_free(FuzzerViewMain* fuzzer_view_main); +void fuzzer_view_main_free(FuzzerViewMain* view); + +View* fuzzer_view_main_get_view(FuzzerViewMain* view); -View* fuzzer_view_main_get_view(FuzzerViewMain* fuzzer_view_main); \ No newline at end of file +void fuzzer_view_main_update_data(FuzzerViewMain* view, FuzzerState state); +void fuzzer_view_main_get_state(FuzzerViewMain* view, FuzzerState* state); \ No newline at end of file From 70edcf3f6a1797cef169dfe524a3e744be309109 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 3 Jun 2023 00:32:32 +0300 Subject: [PATCH 045/102] Fuzzer App: worker --- applications/external/pacs_fuzzer/fuzzer.c | 4 + applications/external/pacs_fuzzer/fuzzer_i.h | 3 + .../pacs_fuzzer/helpers/fake_worker.c | 183 ++++++++++++++++++ .../pacs_fuzzer/helpers/fake_worker.h | 40 ++++ .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 2 + .../external/pacs_fuzzer/helpers/protocol.h | 2 + .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 73 ++++++- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 13 ++ .../external/pacs_fuzzer/views/attack.c | 21 +- .../external/pacs_fuzzer/views/attack.h | 6 +- 10 files changed, 336 insertions(+), 11 deletions(-) create mode 100644 applications/external/pacs_fuzzer/helpers/fake_worker.c create mode 100644 applications/external/pacs_fuzzer/helpers/fake_worker.h diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index 42c7d1e472..5a6a4c9b6e 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -25,6 +25,8 @@ PacsFuzzerApp* fuzzer_app_alloc() { app->fuzzer_state.menu_index = 0; app->fuzzer_state.proto_index = 0; + app->worker = fuzzer_worker_alloc(); + // GUI app->gui = furi_record_open(RECORD_GUI); @@ -76,6 +78,8 @@ void fuzzer_app_free(PacsFuzzerApp* app) { // Close records furi_record_close(RECORD_GUI); + fuzzer_worker_free(app->worker); + free(app); } diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 8f84a558df..bd4833c5bd 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -10,6 +10,7 @@ #include "views/attack.h" #include "helpers/fuzzer_types.h" +#include "helpers/fake_worker.h" #include @@ -22,4 +23,6 @@ typedef struct { FuzzerViewAttack* attack_view; FuzzerState fuzzer_state; + + FuzzerWorker* worker; } PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fake_worker.c b/applications/external/pacs_fuzzer/helpers/fake_worker.c new file mode 100644 index 0000000000..15e3e035ac --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/fake_worker.c @@ -0,0 +1,183 @@ +#include "fake_worker.h" + +#include +#include + +#include +#include + +#include + +struct FuzzerWorker { + iButtonWorker* proto_worker; + iButtonProtocolId protocol_id; + iButtonProtocols* protocols_items; + iButtonKey* key; + + const FuzzerProtocol* protocol; + FuzzerWorkerAttackType attack_type; + uint8_t timeer_delay; + + uint8_t payload[MAX_PAYLOAD_SIZE]; + Stream* uids_stream; + uint16_t index; + + bool treead_running; + FuriTimer* timer; + + FuzzerWorkerUidChagedCallback tick_callback; + void* tick_context; + + FuzzerWorkerEndCallback end_callback; + void* end_context; +}; + +static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { + furi_assert(worker); + furi_assert(worker->protocol); + bool res = false; + + const FuzzerProtocol* protocol = worker->protocol; + + if(next) { + worker->index++; + } + + switch(worker->attack_type) { + case FuzzerWorkerAttackTypeDefaultDict: + if(worker->index < protocol->dict.len) { + memcpy( + worker->payload, + &protocol->dict.val[worker->index * protocol->data_size], + protocol->data_size); + res = true; + } + break; + + default: + break; + } + + return res; +} + +static void fuzzer_worker_on_tick_callback(void* context) { + furi_assert(context); + + FuzzerWorker* worker = context; + + if(!fuzzer_worker_load_key(worker, true)) { + fuzzer_worker_stop(worker); + if(worker->end_callback) { + worker->end_callback(worker->end_context); + } + } else { + if(worker->tick_callback) { + worker->tick_callback(worker->tick_context); + } + } + + // TODO load ibutton key +} + +void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { + furi_assert(worker); + furi_assert(worker->protocol); + + memcpy(key, worker->payload, worker->protocol->data_size); +} + +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { + furi_assert(worker); + + worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; + worker->protocol = &fuzzer_proto_items[protocol_index]; + worker->index = 0; + + return fuzzer_worker_load_key(worker, false); +} + +FuzzerWorker* fuzzer_worker_alloc() { + FuzzerWorker* worker = malloc(sizeof(FuzzerWorker)); + + worker->protocols_items = ibutton_protocols_alloc(); + worker->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(worker->protocols_items)); + + worker->proto_worker = ibutton_worker_alloc(worker->protocols_items); + + worker->index = 0; + worker->treead_running = false; + + memset(worker->payload, 0x00, sizeof(worker->payload)); + + worker->timeer_delay = FUZZ_TIME_DELAY_DEFAULT; + + worker->timer = + furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypePeriodic, worker); + + return worker; +} + +void fuzzer_worker_free(FuzzerWorker* worker) { + furi_assert(worker); + + fuzzer_worker_stop(worker); + + furi_timer_free(worker->timer); + + ibutton_worker_free(worker->proto_worker); + + ibutton_key_free(worker->key); + ibutton_protocols_free(worker->protocols_items); + // TODO delete + UNUSED(fuzzer_worker_on_tick_callback); + free(worker); +} + +void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { + furi_assert(worker); + + worker->timeer_delay = timer_dellay; + + furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); + + // TODO start timer + // worker->treead_running = true; + // ibutton_worker_start_thread(worker->proto_worker); + + // TODO load ibutton key + + // ibutton_worker_emulate_start(worker->proto_worker, worker->key); +} + +void fuzzer_worker_stop(FuzzerWorker* worker) { + furi_assert(worker); + + furi_timer_stop(worker->timer); + + if(worker->treead_running) { + ibutton_worker_stop(worker->proto_worker); + ibutton_worker_stop_thread(worker->proto_worker); + worker->treead_running = false; + } + + // TODO stop timer, anything else +} + +void fuzzer_worker_set_uid_chaged_callback( + FuzzerWorker* worker, + FuzzerWorkerUidChagedCallback callback, + void* context) { + furi_assert(worker); + worker->tick_callback = callback; + worker->tick_context = context; +} + +void fuzzer_worker_set_end_callback( + FuzzerWorker* worker, + FuzzerWorkerEndCallback callback, + void* context) { + furi_assert(worker); + worker->end_callback = callback; + worker->end_context = context; +} diff --git a/applications/external/pacs_fuzzer/helpers/fake_worker.h b/applications/external/pacs_fuzzer/helpers/fake_worker.h new file mode 100644 index 0000000000..00e3cb23f8 --- /dev/null +++ b/applications/external/pacs_fuzzer/helpers/fake_worker.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "protocol.h" + +typedef enum { + FuzzerWorkerAttackTypeDefaultDict = 0, + FuzzerWorkerAttackTypeLoadFile, + FuzzerWorkerAttackTypeLoadFileCustomUids, + + FuzzerWorkerAttackTypeMax, +} FuzzerWorkerAttackType; + +typedef void (*FuzzerWorkerUidChagedCallback)(void* context); +typedef void (*FuzzerWorkerEndCallback)(void* context); + +typedef struct FuzzerWorker FuzzerWorker; + +FuzzerWorker* fuzzer_worker_alloc(); + +void fuzzer_worker_free(FuzzerWorker* worker); + +void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); + +void fuzzer_worker_stop(FuzzerWorker* worker); + +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index); + +void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key); + +void fuzzer_worker_set_uid_chaged_callback( + FuzzerWorker* worker, + FuzzerWorkerUidChagedCallback callback, + void* context); + +void fuzzer_worker_set_end_callback( + FuzzerWorker* worker, + FuzzerWorkerEndCallback callback, + void* context); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h index 189d72ef43..890d961dbb 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -8,4 +8,6 @@ typedef enum { FuzzerCustomEventViewAttackBack, FuzzerCustomEventViewAttackOk, + FuzzerCustomEventViewAttackTick, + FuzzerCustomEventViewAttackEnd, } FuzzerCustomEvent; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.h b/applications/external/pacs_fuzzer/helpers/protocol.h index aedeedd8d2..c0dd5dd157 100644 --- a/applications/external/pacs_fuzzer/helpers/protocol.h +++ b/applications/external/pacs_fuzzer/helpers/protocol.h @@ -2,6 +2,8 @@ #include +#define MAX_PAYLOAD_SIZE 8 + #define FUZZ_TIME_DELAY_MIN (4) #define FUZZ_TIME_DELAY_DEFAULT (8) #define FUZZ_TIME_DELAY_MAX (80) diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index 71d38650c2..f80cc9b299 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -1,9 +1,22 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -#include "../helpers/protocol.h" #include "../helpers/gui_const.h" +// TODO simlify callbacks and attack state + +void fuzzer_scene_attack_worker_tick_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackTick); +} + +void fuzzer_scene_attack_worker_end_callback(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackEnd); +} + void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -23,7 +36,20 @@ void fuzzer_scene_attack_on_enter(void* context) { fuzzer_attack_names[app->fuzzer_state.menu_index], proto.name, proto.data_size); - fuzzer_view_attack_set_uid(app->attack_view, &proto.dict.val[0], false); + + fuzzer_worker_set_uid_chaged_callback( + app->worker, fuzzer_scene_attack_worker_tick_callback, app); + + fuzzer_worker_set_end_callback(app->worker, fuzzer_scene_attack_worker_end_callback, app); + + uint8_t temp_uid[MAX_PAYLOAD_SIZE]; + + fuzzer_worker_get_current_key(app->worker, temp_uid); + + fuzzer_view_attack_set_attack(app->attack_view, false); + fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); + + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDAttack); } @@ -35,11 +61,40 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == FuzzerCustomEventViewAttackBack) { - if(!scene_manager_previous_scene(app->scene_manager)) { - scene_manager_stop(app->scene_manager); - view_dispatcher_stop(app->view_dispatcher); + if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack)) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + } else { + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); + fuzzer_view_attack_set_attack(app->attack_view, false); + fuzzer_worker_stop(app->worker); } consumed = true; + } else if(event.event == FuzzerCustomEventViewAttackOk) { + if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack)) { + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, true); + fuzzer_view_attack_set_attack(app->attack_view, true); + fuzzer_worker_start( + app->worker, fuzzer_view_attack_get_time_delay(app->attack_view)); + } else { + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); + fuzzer_view_attack_set_attack(app->attack_view, false); + fuzzer_worker_stop(app->worker); + } + consumed = true; + } else if(event.event == FuzzerCustomEventViewAttackTick) { + uint8_t temp_uid[MAX_PAYLOAD_SIZE]; + + fuzzer_worker_get_current_key(app->worker, temp_uid); + + fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); + consumed = true; + } else if(event.event == FuzzerCustomEventViewAttackEnd) { + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); + fuzzer_view_attack_set_attack(app->attack_view, false); + consumed = true; } } @@ -47,7 +102,9 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { } void fuzzer_scene_attack_on_exit(void* context) { - // furi_assert(context); - // PacsFuzzerApp* app = context; - UNUSED(context); + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_worker_set_uid_chaged_callback(app->worker, NULL, NULL); + fuzzer_worker_set_end_callback(app->worker, NULL, NULL); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 812e063ab6..00ecdc543b 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -1,6 +1,9 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" +#include "../helpers/protocol.h" +#include "../helpers/gui_const.h" + void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -32,6 +35,16 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(event.event == FuzzerCustomEventViewMainOk) { fuzzer_view_main_get_state(app->main_view, &app->fuzzer_state); + + switch(app->fuzzer_state.menu_index) { + case FuzzerMainMenuIndexDefaultValues: + fuzzer_worker_attack_dict(app->worker, app->fuzzer_state.proto_index); + break; + + default: + break; + } + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); consumed = true; } diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index a83740f3dd..57dd4dd4b2 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -42,7 +42,7 @@ void fuzzer_view_attack_reset_data( true); } -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool attack) { +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid) { furi_assert(view); with_view_model( @@ -61,11 +61,17 @@ void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool uid[5], uid[6], uid[7]); - model->attack_enabled = attack; }, true); } +void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack) { + furi_assert(view); + + with_view_model( + view->view, FuzzerViewAttackModel * model, { model->attack_enabled = attack; }, true); +} + void fuzzer_view_attack_set_callback( FuzzerViewAttack* view_attack, FuzzerViewAttackCallback callback, @@ -96,6 +102,7 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { } canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, model->uid); + canvas_set_font(canvas, FontSecondary); if(model->attack_enabled) { elements_button_center(canvas, "Stop"); } else { @@ -209,4 +216,14 @@ void fuzzer_view_attack_free(FuzzerViewAttack* view_attack) { View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack) { furi_assert(view_attack); return view_attack->view; +} + +uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view) { + furi_assert(view); + uint8_t time_delay; + + with_view_model( + view->view, FuzzerViewAttackModel * model, { time_delay = model->time_delay; }, false); + + return time_delay; } \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index 082b7ba365..c8204eb185 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -24,4 +24,8 @@ void fuzzer_view_attack_reset_data( const char* protocol_name, uint8_t uid_size); -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid, bool attack); \ No newline at end of file +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid); + +void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack); + +uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view); \ No newline at end of file From d3a260e4417d96a77c750f0f815f4255d39ace24 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 3 Jun 2023 14:24:27 +0300 Subject: [PATCH 046/102] Fuzzer App: worker add RFID --- .../external/pacs_fuzzer/application.fam | 38 +++- applications/external/pacs_fuzzer/fuzzer_i.h | 2 +- .../external/pacs_fuzzer/helpers/protocol.c | 92 -------- .../external/pacs_fuzzer/icons/125_10px.png | Bin 0 -> 308 bytes .../external/pacs_fuzzer/icons/ibutt_10px.png | Bin 0 -> 304 bytes .../pacs_fuzzer/{ => icons}/rfid_10px.png | Bin .../{helpers => lib/worker}/fake_worker.c | 108 +++++++-- .../{helpers => lib/worker}/fake_worker.h | 0 .../pacs_fuzzer/lib/worker/protocol.c | 214 ++++++++++++++++++ .../{helpers => lib/worker}/protocol.h | 23 ++ .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 2 +- .../external/pacs_fuzzer/views/attack.c | 2 +- .../external/pacs_fuzzer/views/main_menu.c | 2 +- 13 files changed, 366 insertions(+), 117 deletions(-) delete mode 100644 applications/external/pacs_fuzzer/helpers/protocol.c create mode 100644 applications/external/pacs_fuzzer/icons/125_10px.png create mode 100644 applications/external/pacs_fuzzer/icons/ibutt_10px.png rename applications/external/pacs_fuzzer/{ => icons}/rfid_10px.png (100%) rename applications/external/pacs_fuzzer/{helpers => lib/worker}/fake_worker.c (59%) rename applications/external/pacs_fuzzer/{helpers => lib/worker}/fake_worker.h (100%) create mode 100644 applications/external/pacs_fuzzer/lib/worker/protocol.c rename applications/external/pacs_fuzzer/{helpers => lib/worker}/protocol.h (65%) diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam index 5e0aafd994..6b6b2ab36b 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/pacs_fuzzer/application.fam @@ -12,8 +12,40 @@ App( ], stack_size=2 * 1024, order=15, - fap_icon="rfid_10px.png", + fap_icon="icons/rfid_10px.png", fap_category="Debug", - # fap_icon_assets="images", - # fap_icon_assets_symbol="fuzzer", + fap_private_libs=[ + Lib( + name="worker", + cdefines=["IBUTTON_PROTOCOL"], + ), + ], + fap_icon_assets="icons", + fap_icon_assets_symbol="fuzzer", +) + +App( + appid="pacs_rfid_fuzzer", + name="Fuzzer Gui rfid", + apptype=FlipperAppType.EXTERNAL, + entry_point="fuzzer_start", + requires=[ + "gui", + "storage", + "dialogs", + "input", + "notification", + ], + stack_size=2 * 1024, + order=15, + fap_icon="icons/125_10px.png", + fap_category="Debug", + fap_private_libs=[ + Lib( + name="worker", + cdefines=["RFID_125_PROTOCOL"], + ), + ], + fap_icon_assets="icons", + fap_icon_assets_symbol="fuzzer", ) diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index bd4833c5bd..bc31a137cf 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -10,7 +10,7 @@ #include "views/attack.h" #include "helpers/fuzzer_types.h" -#include "helpers/fake_worker.h" +#include "lib/worker/fake_worker.h" #include diff --git a/applications/external/pacs_fuzzer/helpers/protocol.c b/applications/external/pacs_fuzzer/helpers/protocol.c deleted file mode 100644 index 0727491799..0000000000 --- a/applications/external/pacs_fuzzer/helpers/protocol.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "protocol.h" - -#define DS1990_DATA_SIZE (8) -#define Metakom_DATA_SIZE (4) -#define Cyfral_DATA_SIZE (2) - -const uint8_t uid_list_ds1990[][DS1990_DATA_SIZE] = { - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал - {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах - {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% - {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% - {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) - {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF - {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 - {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni -}; - -const uint8_t uid_list_metakom[][Metakom_DATA_SIZE] = { - {0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d}, // ?? - {0x34, 0x00, 0x29, 0x3d}, // ?? - {0x04, 0xdf, 0x00, 0x00}, // ?? - {0xCA, 0xCA, 0xCA, 0xCA}, // ?? -}; - -const uint8_t uid_list_cyfral[][Cyfral_DATA_SIZE] = { - {0x00, 0x00}, // Null bytes - {0xFF, 0xFF}, // Only FF - {0x11, 0x11}, // Only 11 - {0x22, 0x22}, // Only 22 - {0x33, 0x33}, // Only 33 - {0x44, 0x44}, // Only 44 - {0x55, 0x55}, // Only 55 - {0x66, 0x66}, // Only 66 - {0x77, 0x77}, // Only 77 - {0x88, 0x88}, // Only 88 - {0x99, 0x99}, // Only 99 - {0x12, 0x34}, // Incremental UID - {0x56, 0x34}, // Decremental UID - {0xCA, 0xCA}, // ?? - {0x8E, 0xC9}, // Elevator code - {0x6A, 0x50}, // VERY fresh code from smartkey -}; - -const FuzzerProtocol fuzzer_proto_items[] = { - [DS1990] = - { - .name = "DS1990", - .data_size = DS1990_DATA_SIZE, - .dict = - {.val = (const uint8_t*)&uid_list_ds1990, - .len = sizeof(uid_list_ds1990) / DS1990_DATA_SIZE}, - }, - [Metakom] = - { - .name = "Metakom", - .data_size = Metakom_DATA_SIZE, - .dict = - {.val = (const uint8_t*)&uid_list_metakom, - .len = sizeof(uid_list_metakom) / Metakom_DATA_SIZE}, - }, - [Cyfral] = - { - .name = "Cyfral", - .data_size = Cyfral_DATA_SIZE, - .dict = - {.val = (const uint8_t*)&uid_list_cyfral, - .len = sizeof(uid_list_cyfral) / Cyfral_DATA_SIZE}, - }, -}; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/icons/125_10px.png b/applications/external/pacs_fuzzer/icons/125_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..ce01284a2c1f3eb413f581b84f1fb3f3a2a7223b GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)bWZjP>yH&963)5S4_<9hOs!iI<>&pI=m5)bW_|craZ$KesPZ!4!j_b)kjvx52z42i= z^Wk##wtdVzTiGS{99;2>!TC2M!yZeXz}?LkD}l;YOI#yLQW8s2t&)pUffR$0fsvuE zfvK*cNr<75m9c@9v4ysQft7*5`ikN&C>nC}Q!>*kp&E>VdO{3LtqcsU49p-Jly36? Qy~)7f>FVdQ&MBb@0C$~I0{{R3 literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/rfid_10px.png b/applications/external/pacs_fuzzer/icons/rfid_10px.png similarity index 100% rename from applications/external/pacs_fuzzer/rfid_10px.png rename to applications/external/pacs_fuzzer/icons/rfid_10px.png diff --git a/applications/external/pacs_fuzzer/helpers/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c similarity index 59% rename from applications/external/pacs_fuzzer/helpers/fake_worker.c rename to applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 15e3e035ac..6da2becbc4 100644 --- a/applications/external/pacs_fuzzer/helpers/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -3,16 +3,36 @@ #include #include +#if defined(RFID_125_PROTOCOL) + +#else + +#endif + +#if defined(RFID_125_PROTOCOL) + +#include +#include + +#else + #include #include +#endif #include struct FuzzerWorker { +#if defined(RFID_125_PROTOCOL) + LFRFIDWorker* proto_worker; + ProtocolId protocol_id; + ProtocolDict* protocols_items; +#else iButtonWorker* proto_worker; - iButtonProtocolId protocol_id; + iButtonProtocolId protocol_id; // TODO iButtonProtocols* protocols_items; iButtonKey* key; +#endif const FuzzerProtocol* protocol; FuzzerWorkerAttackType attack_type; @@ -57,7 +77,18 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { default: break; } - +#if defined(RFID_125_PROTOCOL) + protocol_dict_set_data( + worker->protocols_items, worker->protocol_id, worker->payload, MAX_PAYLOAD_SIZE); +#else + ibutton_key_set_protocol_id(worker->key, worker->protocol_id); + iButtonEditableData data; + ibutton_protocols_get_editable_data(worker->protocols_items, worker->key, &data); + + // TODO check data.size logic + data.size = MAX_PAYLOAD_SIZE; + memcpy(data.ptr, worker->payload, MAX_PAYLOAD_SIZE); // data.size); +#endif return res; } @@ -66,18 +97,31 @@ static void fuzzer_worker_on_tick_callback(void* context) { FuzzerWorker* worker = context; + if(worker->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_stop(worker->proto_worker); +#else + ibutton_worker_stop(worker->proto_worker); +#endif + } + if(!fuzzer_worker_load_key(worker, true)) { fuzzer_worker_stop(worker); if(worker->end_callback) { worker->end_callback(worker->end_context); } } else { + if(worker->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); +#else + ibutton_worker_emulate_start(worker->proto_worker, worker->key); +#endif + } if(worker->tick_callback) { worker->tick_callback(worker->tick_context); } } - - // TODO load ibutton key } void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { @@ -90,8 +134,17 @@ void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { furi_assert(worker); - worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; worker->protocol = &fuzzer_proto_items[protocol_index]; + // TODO iButtonProtocolIdInvalid check + +#if defined(RFID_125_PROTOCOL) + worker->protocol_id = + protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); +#else + worker->protocol_id = + ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); +#endif + worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; worker->index = 0; return fuzzer_worker_load_key(worker, false); @@ -100,11 +153,17 @@ bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index FuzzerWorker* fuzzer_worker_alloc() { FuzzerWorker* worker = malloc(sizeof(FuzzerWorker)); +#if defined(RFID_125_PROTOCOL) + worker->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + + worker->proto_worker = lfrfid_worker_alloc(worker->protocols_items); +#else worker->protocols_items = ibutton_protocols_alloc(); worker->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(worker->protocols_items)); worker->proto_worker = ibutton_worker_alloc(worker->protocols_items); - +#endif + worker->attack_type = FuzzerWorkerAttackTypeMax; worker->index = 0; worker->treead_running = false; @@ -125,29 +184,37 @@ void fuzzer_worker_free(FuzzerWorker* worker) { furi_timer_free(worker->timer); +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_free(worker->proto_worker); + + protocol_dict_free(worker->protocols_items); +#else ibutton_worker_free(worker->proto_worker); ibutton_key_free(worker->key); ibutton_protocols_free(worker->protocols_items); - // TODO delete - UNUSED(fuzzer_worker_on_tick_callback); +#endif + free(worker); } void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { furi_assert(worker); - worker->timeer_delay = timer_dellay; - - furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); + if(worker->attack_type < FuzzerWorkerAttackTypeMax) { + worker->timeer_delay = timer_dellay; - // TODO start timer - // worker->treead_running = true; - // ibutton_worker_start_thread(worker->proto_worker); + furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); - // TODO load ibutton key - - // ibutton_worker_emulate_start(worker->proto_worker, worker->key); + worker->treead_running = true; +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_start_thread(worker->proto_worker); + lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); +#else + ibutton_worker_start_thread(worker->proto_worker); + ibutton_worker_emulate_start(worker->proto_worker, worker->key); +#endif + } } void fuzzer_worker_stop(FuzzerWorker* worker) { @@ -156,12 +223,17 @@ void fuzzer_worker_stop(FuzzerWorker* worker) { furi_timer_stop(worker->timer); if(worker->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_stop(worker->proto_worker); + lfrfid_worker_stop_thread(worker->proto_worker); +#else ibutton_worker_stop(worker->proto_worker); ibutton_worker_stop_thread(worker->proto_worker); +#endif worker->treead_running = false; } - // TODO stop timer, anything else + // TODO anything else } void fuzzer_worker_set_uid_chaged_callback( diff --git a/applications/external/pacs_fuzzer/helpers/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h similarity index 100% rename from applications/external/pacs_fuzzer/helpers/fake_worker.h rename to applications/external/pacs_fuzzer/lib/worker/fake_worker.h diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c new file mode 100644 index 0000000000..b27524d069 --- /dev/null +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -0,0 +1,214 @@ +#include "protocol.h" + +// ####################### +// ## Ibutton Protocols ## +// ####################### +#define DS1990_DATA_SIZE (8) +#define Metakom_DATA_SIZE (4) +#define Cyfral_DATA_SIZE (2) + +const uint8_t uid_list_ds1990[][DS1990_DATA_SIZE] = { + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit + {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает + {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). + {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал + {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах + {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom + {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% + {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) + {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF + {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 + {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni +}; + +const uint8_t uid_list_metakom[][Metakom_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d}, // ?? + {0x34, 0x00, 0x29, 0x3d}, // ?? + {0x04, 0xdf, 0x00, 0x00}, // ?? + {0xCA, 0xCA, 0xCA, 0xCA}, // ?? +}; + +const uint8_t uid_list_cyfral[][Cyfral_DATA_SIZE] = { + {0x00, 0x00}, // Null bytes + {0xFF, 0xFF}, // Only FF + {0x11, 0x11}, // Only 11 + {0x22, 0x22}, // Only 22 + {0x33, 0x33}, // Only 33 + {0x44, 0x44}, // Only 44 + {0x55, 0x55}, // Only 55 + {0x66, 0x66}, // Only 66 + {0x77, 0x77}, // Only 77 + {0x88, 0x88}, // Only 88 + {0x99, 0x99}, // Only 99 + {0x12, 0x34}, // Incremental UID + {0x56, 0x34}, // Decremental UID + {0xCA, 0xCA}, // ?? + {0x8E, 0xC9}, // Elevator code + {0x6A, 0x50}, // VERY fresh code from smartkey +}; + +// ########################### +// ## Rfid_125khz Protocols ## +// ########################### +#define EM4100_DATA_SIZE (5) +#define HIDProx_DATA_SIZE (6) +#define PAC_DATA_SIZE (4) +#define H10301_DATA_SIZE (3) + +const uint8_t uid_list_em4100[][EM4100_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha + {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha + {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha + {0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +const uint8_t uid_list_hid[][HIDProx_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, // Incremental UID + {0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID + {0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +const uint8_t uid_list_pac[][PAC_DATA_SIZE] = { + {0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78}, // Incremental UID + {0x9A, 0x78, 0x56, 0x34}, // Decremental UID + {0x04, 0xd0, 0x9b, 0x0d}, // From arha + {0x34, 0x00, 0x29, 0x3d}, // From arha + {0x04, 0xdf, 0x00, 0x00}, // From arha + {0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +const uint8_t uid_list_h10301[][H10301_DATA_SIZE] = { + {0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56}, // Incremental UID + {0x56, 0x34, 0x12}, // Decremental UID + {0xCA, 0xCA, 0xCA}, // From arha +}; + +#if defined(RFID_125_PROTOCOL) +const FuzzerProtocol fuzzer_proto_items[] = { + [EM4100] = + { + .name = "EM4100", + .data_size = EM4100_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_em4100, + .len = sizeof(uid_list_em4100) / EM4100_DATA_SIZE}, + }, + [HIDProx] = + { + .name = "HIDProx", + .data_size = HIDProx_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_hid, + .len = sizeof(uid_list_hid) / HIDProx_DATA_SIZE}, + }, + [PAC] = + { + .name = "PAC/Stanley", + .data_size = PAC_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_pac, + .len = sizeof(uid_list_pac) / PAC_DATA_SIZE}, + }, + [H10301] = + { + .name = "H10301", + .data_size = H10301_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_h10301, + .len = sizeof(uid_list_h10301) / H10301_DATA_SIZE}, + }, +}; +#else +const FuzzerProtocol fuzzer_proto_items[] = { + [DS1990] = + { + .name = "DS1990", + .data_size = DS1990_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_ds1990, + .len = sizeof(uid_list_ds1990) / DS1990_DATA_SIZE}, + }, + [Metakom] = + { + .name = "Metakom", + .data_size = Metakom_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_metakom, + .len = sizeof(uid_list_metakom) / Metakom_DATA_SIZE}, + }, + [Cyfral] = + { + .name = "Cyfral", + .data_size = Cyfral_DATA_SIZE, + .dict = + {.val = (const uint8_t*)&uid_list_cyfral, + .len = sizeof(uid_list_cyfral) / Cyfral_DATA_SIZE}, + }, +}; +#endif \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h similarity index 65% rename from applications/external/pacs_fuzzer/helpers/protocol.h rename to applications/external/pacs_fuzzer/lib/worker/protocol.h index c0dd5dd157..c6d7c88ba2 100644 --- a/applications/external/pacs_fuzzer/helpers/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -2,16 +2,39 @@ #include +// #define RFID_125_PROTOCOL + +#if defined(RFID_125_PROTOCOL) + +#define MAX_PAYLOAD_SIZE 6 + +#define FUZZ_TIME_DELAY_MIN (5) +#define FUZZ_TIME_DELAY_DEFAULT (10) +#define FUZZ_TIME_DELAY_MAX (70) + +#else + #define MAX_PAYLOAD_SIZE 8 #define FUZZ_TIME_DELAY_MIN (4) #define FUZZ_TIME_DELAY_DEFAULT (8) #define FUZZ_TIME_DELAY_MAX (80) +#endif + typedef enum { + +#if defined(RFID_125_PROTOCOL) + EM4100, + HIDProx, + PAC, + H10301, +#else DS1990, Metakom, Cyfral, +#endif + // Reserved FuzzerProtoMax, } FuzzerProtos; diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 00ecdc543b..1caf0b0edf 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -1,7 +1,7 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -#include "../helpers/protocol.h" +#include "../lib/worker/protocol.h" #include "../helpers/gui_const.h" void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 57dd4dd4b2..9e589985d3 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -4,7 +4,7 @@ #include #include -#include "../helpers/protocol.h" +#include "../lib/worker/protocol.h" #define ATTACK_SCENE_MAX_UID_LENGTH 25 diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index c037fdf44d..13ed005f1c 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -4,7 +4,7 @@ #include // #include -#include "../helpers/protocol.h" +#include "../lib/worker/protocol.h" #include "../helpers/gui_const.h" struct FuzzerViewMain { From 2b677c83e3832e3bfb7d9c9df2bf0fa178f82406 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 3 Jun 2023 18:47:53 +0300 Subject: [PATCH 047/102] Fuzzers App: load custom dict --- .../external/pacs_fuzzer/application.fam | 4 +- applications/external/pacs_fuzzer/fuzzer.c | 38 +++++- applications/external/pacs_fuzzer/fuzzer_i.h | 19 +++ .../pacs_fuzzer/lib/worker/fake_worker.c | 114 ++++++++++++++++-- .../pacs_fuzzer/lib/worker/fake_worker.h | 9 +- .../pacs_fuzzer/lib/worker/protocol.h | 18 --- .../pacs_fuzzer/lib/worker/protocol_i.h | 31 +++++ .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 21 ++-- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 43 ++++++- 9 files changed, 253 insertions(+), 44 deletions(-) create mode 100644 applications/external/pacs_fuzzer/lib/worker/protocol_i.h diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam index 6b6b2ab36b..8e67af6d40 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/pacs_fuzzer/application.fam @@ -2,7 +2,7 @@ App( appid="pacs_fuzzer", name="Fuzzer Gui", apptype=FlipperAppType.EXTERNAL, - entry_point="fuzzer_start", + entry_point="fuzzer_start_ibtn", requires=[ "gui", "storage", @@ -28,7 +28,7 @@ App( appid="pacs_rfid_fuzzer", name="Fuzzer Gui rfid", apptype=FlipperAppType.EXTERNAL, - entry_point="fuzzer_start", + entry_point="fuzzer_start_rfid", requires=[ "gui", "storage", diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index 5a6a4c9b6e..b6b66fb185 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -27,9 +27,14 @@ PacsFuzzerApp* fuzzer_app_alloc() { app->worker = fuzzer_worker_alloc(); + app->file_path = furi_string_alloc(); + // GUI app->gui = furi_record_open(RECORD_GUI); + // Dialog + app->dialogs = furi_record_open(RECORD_DIALOGS); + // View Dispatcher app->view_dispatcher = view_dispatcher_alloc(); @@ -75,18 +80,49 @@ void fuzzer_app_free(PacsFuzzerApp* app) { scene_manager_free(app->scene_manager); view_dispatcher_free(app->view_dispatcher); + // Dialog + furi_record_close(RECORD_DIALOGS); + // Close records furi_record_close(RECORD_GUI); + furi_string_free(app->file_path); + fuzzer_worker_free(app->worker); free(app); } -int32_t fuzzer_start(void* p) { +int32_t fuzzer_start_ibtn(void* p) { UNUSED(p); PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); + FuzzerConsts app_const = { + .custom_dict_folder = "/ext/ibtnfuzzer", + .custom_dict_extension = ".txt", + .key_extension = ".ibtn", + .path_key_folder = "/ext/ibutton", + }; + fuzzer_app->fuzzer_const = &app_const; + + view_dispatcher_run(fuzzer_app->view_dispatcher); + + fuzzer_app_free(fuzzer_app); + return 0; +} + +int32_t fuzzer_start_rfid(void* p) { + UNUSED(p); + PacsFuzzerApp* fuzzer_app = fuzzer_app_alloc(); + + FuzzerConsts app_const = { + .custom_dict_folder = "/ext/rfidfuzzer", + .custom_dict_extension = ".txt", + .key_extension = ".rfid", + .path_key_folder = "/ext/lfrfid", + }; + fuzzer_app->fuzzer_const = &app_const; + view_dispatcher_run(fuzzer_app->view_dispatcher); fuzzer_app_free(fuzzer_app); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index bc31a137cf..b56becd8fc 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -1,9 +1,13 @@ #pragma once +#include +#include + #include #include #include #include +#include #include "scenes/fuzzer_scene.h" #include "views/main_menu.h" @@ -13,16 +17,31 @@ #include "lib/worker/fake_worker.h" #include +#include "fuzzer_icons.h" + +#define FUZZ_TIME_DELAY_MIN (5) +#define FUZZ_TIME_DELAY_MAX (80) + +typedef struct { + const char* custom_dict_extension; + const char* custom_dict_folder; + const char* key_extension; + const char* path_key_folder; +} FuzzerConsts; typedef struct { Gui* gui; ViewDispatcher* view_dispatcher; SceneManager* scene_manager; + DialogsApp* dialogs; FuzzerViewMain* main_view; FuzzerViewAttack* attack_view; + FuriString* file_path; + FuzzerState fuzzer_state; + FuzzerConsts* fuzzer_const; FuzzerWorker* worker; } PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 6da2becbc4..ff74f29e10 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -1,25 +1,28 @@ #include "fake_worker.h" -#include #include -#if defined(RFID_125_PROTOCOL) - -#else +#include +#include +#include -#endif +#define TAG "Fuzzer worker" +#define FUZZ_TIME_DELAY_DEFAULT (10) #if defined(RFID_125_PROTOCOL) +#define MAX_PAYLOAD_SIZE 6 #include #include #else +#define MAX_PAYLOAD_SIZE 8 #include #include #endif + #include struct FuzzerWorker { @@ -74,6 +77,42 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { } break; + case FuzzerWorkerAttackTypeLoadFileCustomUids: { + uint8_t str_len = protocol->data_size * 2 + 1; + FuriString* data_str = furi_string_alloc(); + while(true) { + furi_string_reset(data_str); + if(!stream_read_line(worker->uids_stream, data_str)) { + stream_rewind(worker->uids_stream); + // TODO Check empty file & close stream and storage + break; + } else if(furi_string_get_char(data_str, 0) == '#') { + // Skip comment string + continue; + } else if(furi_string_size(data_str) != str_len) { + // Ignore strin with bad length + FURI_LOG_W(TAG, "Bad string length"); + continue; + } else { + FURI_LOG_D(TAG, "Uid candidate: \"%s\"", furi_string_get_cstr(data_str)); + bool parse_ok = true; + for(uint8_t i = 0; i < protocol->data_size; i++) { + if(!hex_char_to_uint8( + furi_string_get_cstr(data_str)[i * 2], + furi_string_get_cstr(data_str)[i * 2 + 1], + &worker->payload[i])) { + parse_ok = false; + break; + } + } + res = parse_ok; + } + break; + } + } + + break; + default: break; } @@ -134,20 +173,71 @@ void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { furi_assert(worker); + bool res = false; + worker->protocol = &fuzzer_proto_items[protocol_index]; - // TODO iButtonProtocolIdInvalid check #if defined(RFID_125_PROTOCOL) worker->protocol_id = protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); #else + // TODO iButtonProtocolIdInvalid check worker->protocol_id = ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); #endif worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; worker->index = 0; - return fuzzer_worker_load_key(worker, false); + if(!fuzzer_worker_load_key(worker, false)) { + worker->attack_type = FuzzerWorkerAttackTypeMax; + } else { + res = true; + } + + return res; +} + +bool fuzzer_worker_attack_file_dict( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + FuriString* file_path) { + furi_assert(worker); + furi_assert(file_path); + + bool res = false; + + worker->protocol = &fuzzer_proto_items[protocol_index]; + +#if defined(RFID_125_PROTOCOL) + worker->protocol_id = + protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); +#else + // TODO iButtonProtocolIdInvalid check + worker->protocol_id = + ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); +#endif + + Storage* storage = furi_record_open(RECORD_STORAGE); + worker->uids_stream = buffered_file_stream_alloc(storage); + + if(!buffered_file_stream_open( + worker->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(worker->uids_stream); + return res; + } + + worker->attack_type = FuzzerWorkerAttackTypeLoadFileCustomUids; + worker->index = 0; + + if(!fuzzer_worker_load_key(worker, false)) { + worker->attack_type = FuzzerWorkerAttackTypeMax; + buffered_file_stream_close(worker->uids_stream); + furi_record_close(RECORD_STORAGE); + } else { + res = true; + } + + return res; } FuzzerWorker* fuzzer_worker_alloc() { @@ -198,7 +288,7 @@ void fuzzer_worker_free(FuzzerWorker* worker) { free(worker); } -void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { +bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { furi_assert(worker); if(worker->attack_type < FuzzerWorkerAttackTypeMax) { @@ -214,7 +304,9 @@ void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { ibutton_worker_start_thread(worker->proto_worker); ibutton_worker_emulate_start(worker->proto_worker, worker->key); #endif + return true; } + return false; } void fuzzer_worker_stop(FuzzerWorker* worker) { @@ -233,6 +325,12 @@ void fuzzer_worker_stop(FuzzerWorker* worker) { worker->treead_running = false; } + if(worker->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) { + buffered_file_stream_close(worker->uids_stream); + furi_record_close(RECORD_STORAGE); + worker->attack_type = FuzzerWorkerAttackTypeMax; + } + // TODO anything else } diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 00e3cb23f8..2628ac6496 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "protocol.h" @@ -21,12 +21,17 @@ FuzzerWorker* fuzzer_worker_alloc(); void fuzzer_worker_free(FuzzerWorker* worker); -void fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); +bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); void fuzzer_worker_stop(FuzzerWorker* worker); bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index); +bool fuzzer_worker_attack_file_dict( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + FuriString* file_path); + void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key); void fuzzer_worker_set_uid_chaged_callback( diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index c6d7c88ba2..a69d3db04b 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -4,24 +4,6 @@ // #define RFID_125_PROTOCOL -#if defined(RFID_125_PROTOCOL) - -#define MAX_PAYLOAD_SIZE 6 - -#define FUZZ_TIME_DELAY_MIN (5) -#define FUZZ_TIME_DELAY_DEFAULT (10) -#define FUZZ_TIME_DELAY_MAX (70) - -#else - -#define MAX_PAYLOAD_SIZE 8 - -#define FUZZ_TIME_DELAY_MIN (4) -#define FUZZ_TIME_DELAY_DEFAULT (8) -#define FUZZ_TIME_DELAY_MAX (80) - -#endif - typedef enum { #if defined(RFID_125_PROTOCOL) diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h new file mode 100644 index 0000000000..e0ab76cb35 --- /dev/null +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -0,0 +1,31 @@ +#pragma once + +#include "protocol.h" + +#if defined(RFID_125_PROTOCOL) + +#define MAX_PAYLOAD_SIZE 6 + +#define FUZZ_TIME_DELAY_MIN (5) +#define FUZZ_TIME_DELAY_DEFAULT (10) +#define FUZZ_TIME_DELAY_MAX (70) + +#define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" +#define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/rfidfuzzer" +#define FUZZER_APP_KEY_EXTENSION ".rfid" +#define FUZZER_APP_PATH_KEY_FOLDER "/ext/lfrfid" + +#else + +#define MAX_PAYLOAD_SIZE 8 + +#define FUZZ_TIME_DELAY_MIN (4) +#define FUZZ_TIME_DELAY_DEFAULT (8) +#define FUZZ_TIME_DELAY_MAX (80) + +#define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" +#define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/ibtnfuzzer" +#define FUZZER_APP_KEY_EXTENSION ".ibtn" +#define FUZZER_APP_PATH_KEY_FOLDER "/ext/ibutton" + +#endif diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index f80cc9b299..c18e2cec94 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -31,21 +31,20 @@ void fuzzer_scene_attack_on_enter(void* context) { FuzzerProtocol proto = fuzzer_proto_items[app->fuzzer_state.proto_index]; - fuzzer_view_attack_reset_data( - app->attack_view, - fuzzer_attack_names[app->fuzzer_state.menu_index], - proto.name, - proto.data_size); - fuzzer_worker_set_uid_chaged_callback( app->worker, fuzzer_scene_attack_worker_tick_callback, app); fuzzer_worker_set_end_callback(app->worker, fuzzer_scene_attack_worker_end_callback, app); - uint8_t temp_uid[MAX_PAYLOAD_SIZE]; + uint8_t temp_uid[proto.data_size]; fuzzer_worker_get_current_key(app->worker, temp_uid); + fuzzer_view_attack_reset_data( + app->attack_view, + fuzzer_attack_names[app->fuzzer_state.menu_index], + proto.name, + proto.data_size); fuzzer_view_attack_set_attack(app->attack_view, false); fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); @@ -73,11 +72,11 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackOk) { - if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack)) { + if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) && + fuzzer_worker_start( + app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, true); fuzzer_view_attack_set_attack(app->attack_view, true); - fuzzer_worker_start( - app->worker, fuzzer_view_attack_get_time_delay(app->attack_view)); } else { scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); fuzzer_view_attack_set_attack(app->attack_view, false); @@ -85,7 +84,7 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackTick) { - uint8_t temp_uid[MAX_PAYLOAD_SIZE]; + uint8_t temp_uid[fuzzer_proto_items[app->fuzzer_state.proto_index].data_size]; fuzzer_worker_get_current_key(app->worker, temp_uid); diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 1caf0b0edf..a42701adf0 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -10,6 +10,26 @@ void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, event); } +static bool fuzzer_scene_main_load_custom_dict(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + FuzzerConsts* consts = app->fuzzer_const; + + furi_string_set_str(app->file_path, consts->custom_dict_folder); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, consts->custom_dict_extension, &I_rfid_10px); + browser_options.base_path = consts->custom_dict_folder; + browser_options.hide_ext = false; + + bool res = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + + return res; +} + void fuzzer_scene_main_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -36,16 +56,35 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { } else if(event.event == FuzzerCustomEventViewMainOk) { fuzzer_view_main_get_state(app->main_view, &app->fuzzer_state); + // TODO error logic + bool loading_ok = false; + switch(app->fuzzer_state.menu_index) { case FuzzerMainMenuIndexDefaultValues: - fuzzer_worker_attack_dict(app->worker, app->fuzzer_state.proto_index); + + loading_ok = fuzzer_worker_attack_dict(app->worker, app->fuzzer_state.proto_index); + + if(!loading_ok) { + // error + } + break; + + case FuzzerMainMenuIndexLoadFileCustomUids: + if(!fuzzer_scene_main_load_custom_dict(app)) { + break; + } else { + loading_ok = fuzzer_worker_attack_file_dict( + app->worker, app->fuzzer_state.proto_index, app->file_path); + } break; default: break; } - scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + if(loading_ok) { + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + } consumed = true; } } From 5b4bb66848e28a325b97872339beb986d80c4d39 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sat, 3 Jun 2023 22:03:20 +0300 Subject: [PATCH 048/102] Fuzzer App: Field editor view --- applications/external/pacs_fuzzer/fuzzer.c | 11 + applications/external/pacs_fuzzer/fuzzer_i.h | 2 + .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 3 + .../pacs_fuzzer/helpers/fuzzer_types.h | 1 + .../pacs_fuzzer/scenes/fuzzer_scene_config.h | 3 +- .../scenes/fuzzer_scene_field_editor.c | 42 +++ .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 6 + .../external/pacs_fuzzer/views/attack.c | 2 - .../external/pacs_fuzzer/views/field_editor.c | 251 ++++++++++++++++++ .../external/pacs_fuzzer/views/field_editor.h | 19 ++ .../external/pacs_fuzzer/views/main_menu.c | 2 - 11 files changed, 337 insertions(+), 5 deletions(-) create mode 100644 applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c create mode 100644 applications/external/pacs_fuzzer/views/field_editor.c create mode 100644 applications/external/pacs_fuzzer/views/field_editor.h diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index b6b66fb185..6b609040cf 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -48,6 +48,13 @@ PacsFuzzerApp* fuzzer_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, FuzzerViewIDAttack, fuzzer_view_attack_get_view(app->attack_view)); + // FieldEditor view + app->field_editor_view = fuzzer_view_field_editor_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FuzzerViewIDFieldEditor, + fuzzer_view_field_editor_get_view(app->field_editor_view)); + app->scene_manager = scene_manager_alloc(&fuzzer_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); @@ -77,6 +84,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) { view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDAttack); fuzzer_view_attack_free(app->attack_view); + // FieldEditor view + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDFieldEditor); + fuzzer_view_field_editor_free(app->field_editor_view); + scene_manager_free(app->scene_manager); view_dispatcher_free(app->view_dispatcher); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index b56becd8fc..59ec2df338 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -12,6 +12,7 @@ #include "scenes/fuzzer_scene.h" #include "views/main_menu.h" #include "views/attack.h" +#include "views/field_editor.h" #include "helpers/fuzzer_types.h" #include "lib/worker/fake_worker.h" @@ -37,6 +38,7 @@ typedef struct { DialogsApp* dialogs; FuzzerViewMain* main_view; FuzzerViewAttack* attack_view; + FuzzerViewFieldEditor* field_editor_view; FuriString* file_path; diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h index 890d961dbb..930029d3c4 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -10,4 +10,7 @@ typedef enum { FuzzerCustomEventViewAttackOk, FuzzerCustomEventViewAttackTick, FuzzerCustomEventViewAttackEnd, + + FuzzerCustomEventViewFieldEditorBack, + FuzzerCustomEventViewFieldEditorOk, } FuzzerCustomEvent; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index 55c64954ca..7e390f8754 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -11,4 +11,5 @@ typedef struct { typedef enum { FuzzerViewIDMain, FuzzerViewIDAttack, + FuzzerViewIDFieldEditor, } FuzzerViewID; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h index bccdbd9a5d..711ebe1c42 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h @@ -1,2 +1,3 @@ ADD_SCENE(fuzzer, main, Main) -ADD_SCENE(fuzzer, attack, Attack) \ No newline at end of file +ADD_SCENE(fuzzer, attack, Attack) +ADD_SCENE(fuzzer, field_editor, FieldEditor) \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c new file mode 100644 index 0000000000..197bd82c4c --- /dev/null +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -0,0 +1,42 @@ +#include "../fuzzer_i.h" +#include "../helpers/fuzzer_custom_event.h" + +void fuzzer_scene_field_editor_callback(FuzzerCustomEvent event, void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void fuzzer_scene_field_editor_on_enter(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + fuzzer_view_field_editor_set_callback( + app->field_editor_view, fuzzer_scene_field_editor_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); +} + +bool fuzzer_scene_field_editor_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + PacsFuzzerApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == FuzzerCustomEventViewFieldEditorBack) { + if(!scene_manager_previous_scene(app->scene_manager)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + } + } + + return consumed; +} + +void fuzzer_scene_field_editor_on_exit(void* context) { + // furi_assert(context); + // PacsFuzzerApp* app = context; + UNUSED(context); +} diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index a42701adf0..cb5e97c529 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -69,6 +69,12 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { } break; + case FuzzerMainMenuIndexLoadFile: + // TODO Delete + scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); + + break; + case FuzzerMainMenuIndexLoadFileCustomUids: if(!fuzzer_scene_main_load_custom_dict(app)) { break; diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 9e589985d3..910d69c0cf 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -4,8 +4,6 @@ #include #include -#include "../lib/worker/protocol.h" - #define ATTACK_SCENE_MAX_UID_LENGTH 25 struct FuzzerViewAttack { diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c new file mode 100644 index 0000000000..daf8e9d24f --- /dev/null +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -0,0 +1,251 @@ +#include "field_editor.h" +#include "../fuzzer_i.h" + +#include +#include +#include + +#define UID_STR_LENGTH 25 +#define EDITOR_STRING_Y 50 + +struct FuzzerViewFieldEditor { + View* view; + FuzzerViewFieldEditorCallback callback; + void* context; +}; + +// TODO model +typedef struct { + uint8_t* uid; + uint8_t uid_size; + uint8_t index; + FuriString* uid_str; + bool lo; +} FuzzerViewFieldEditorModel; + +void fuzzer_view_field_editor_set_callback( + FuzzerViewFieldEditor* view_edit, + FuzzerViewFieldEditorCallback callback, + void* context) { + furi_assert(view_edit); + + view_edit->callback = callback; + view_edit->context = context; +} + +void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Left and right: select byte"); + canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignTop, "Up and down: adjust byte"); + + char msg_index[18]; + canvas_set_font(canvas, FontPrimary); + snprintf(msg_index, sizeof(msg_index), "Field index : %d", model->index); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); + + // ####### Editor ####### + FuriString* temp_s = model->uid_str; + canvas_set_font(canvas, FontSecondary); + + furi_string_reset(temp_s); + for(int i = -3; i != 0; i++) { + if(0 <= (model->index + i)) { + furi_string_cat_printf(temp_s, "%2X ", model->uid[model->index + i]); + } + } + canvas_draw_str_aligned( + canvas, 52, EDITOR_STRING_Y, AlignRight, AlignBottom, furi_string_get_cstr(temp_s)); + + furi_string_reset(temp_s); + for(int i = 1; i != 4; i++) { + if((model->index + i) < model->uid_size) { + furi_string_cat_printf(temp_s, " %2X", model->uid[model->index + i]); + } + } + canvas_draw_str_aligned( + canvas, 77, EDITOR_STRING_Y, AlignLeft, AlignBottom, furi_string_get_cstr(temp_s)); + + canvas_set_font(canvas, FontPrimary); + + furi_string_reset(temp_s); + furi_string_cat_printf(temp_s, "<%02X>", model->uid[model->index]); + canvas_draw_str_aligned( + canvas, 64, EDITOR_STRING_Y, AlignCenter, AlignBottom, furi_string_get_cstr(temp_s)); + + uint16_t w = canvas_string_width(canvas, furi_string_get_cstr(temp_s)); + w -= 11; // '<' & '>' + w /= 2; + + if(model->lo) { + canvas_draw_line(canvas, 64 + 1, EDITOR_STRING_Y + 2, 64 + w, EDITOR_STRING_Y + 2); + } else { + canvas_draw_line(canvas, 64 - w, EDITOR_STRING_Y + 2, 64 - 1, EDITOR_STRING_Y + 2); + } + // ####### Editor ####### +} + +bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { + furi_assert(context); + FuzzerViewFieldEditor* view_edit = context; + + if(event->key == InputKeyBack && event->type == InputTypeShort) { + view_edit->callback(FuzzerCustomEventViewFieldEditorBack, view_edit->context); + return true; + } else if(event->key == InputKeyOk && event->type == InputTypeShort) { + view_edit->callback(FuzzerCustomEventViewFieldEditorOk, view_edit->context); + return true; + } else if(event->key == InputKeyLeft) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(model->index > 0 || model->lo) { + if(!model->lo) { + model->index--; + } + model->lo = !model->lo; + } + } else if(event->type == InputTypeLong) { + model->index = 0; + model->lo = false; + } + }, + true); + return true; + } else if(event->key == InputKeyRight) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(model->index < (model->uid_size - 1) || !model->lo) { + if(model->lo) { + model->index++; + } + model->lo = !model->lo; + } + } else if(event->type == InputTypeLong) { + model->index = model->uid_size - 1; + model->lo = true; + } + }, + true); + return true; + } else if(event->key == InputKeyUp) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(model->lo) { + model->uid[model->index] = (model->uid[model->index] & 0xF0) | + ((model->uid[model->index] + 1) & 0x0F); + } else { + model->uid[model->index] = ((model->uid[model->index] + 0x10) & 0xF0) | + (model->uid[model->index] & 0x0F); + } + } + }, + true); + return true; + } else if(event->key == InputKeyDown) { + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + if(event->type == InputTypeShort) { + if(model->lo) { + model->uid[model->index] = (model->uid[model->index] & 0xF0) | + ((model->uid[model->index] - 1) & 0x0F); + } else { + model->uid[model->index] = ((model->uid[model->index] - 0x10) & 0xF0) | + (model->uid[model->index] & 0x0F); + } + } + }, + true); + return true; + } + + return true; +} + +void fuzzer_view_field_editor_enter(void* context) { + furi_assert(context); + // TODO delete only for debug + // FuzzerViewFieldEditor* view_edit = context; + // uint8_t temp[8] = { + // 0x12, + // 0x34, + // 0x56, + // 0x78, + // 0x90, + // 0xAB, + // 0xCD, + // 0xEF, + // }; + // with_view_model( + // view_edit->view, + // FuzzerViewFieldEditorModel * model, + // { + // memcpy(model->uid, &temp, 8); + + // // memset(model->uid, 0xCC, 8); + // model->index = 0; + // model->uid_size = 8; + // }, + // true); +} + +void fuzzer_view_field_editor_exit(void* context) { + furi_assert(context); +} + +FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc() { + FuzzerViewFieldEditor* view_edit = malloc(sizeof(FuzzerViewFieldEditor)); + + // View allocation and configuration + view_edit->view = view_alloc(); + view_allocate_model(view_edit->view, ViewModelTypeLocking, sizeof(FuzzerViewFieldEditorModel)); + view_set_context(view_edit->view, view_edit); + view_set_draw_callback(view_edit->view, (ViewDrawCallback)fuzzer_view_field_editor_draw); + view_set_input_callback(view_edit->view, fuzzer_view_field_editor_input); + view_set_enter_callback(view_edit->view, fuzzer_view_field_editor_enter); + view_set_exit_callback(view_edit->view, fuzzer_view_field_editor_exit); + + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + model->uid_str = furi_string_alloc(); + + model->uid = malloc(8); + }, + true); + + return view_edit; +} + +void fuzzer_view_field_editor_free(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + furi_string_free(model->uid_str); + free(model->uid); + }, + true); + view_free(view_edit->view); + free(view_edit); +} + +View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + return view_edit->view; +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h new file mode 100644 index 0000000000..df3aba7247 --- /dev/null +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/fuzzer_custom_event.h" + +typedef struct FuzzerViewFieldEditor FuzzerViewFieldEditor; + +typedef void (*FuzzerViewFieldEditorCallback)(FuzzerCustomEvent event, void* context); + +void fuzzer_view_field_editor_set_callback( + FuzzerViewFieldEditor* view_attack, + FuzzerViewFieldEditorCallback callback, + void* context); + +FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc(); + +void fuzzer_view_field_editor_free(FuzzerViewFieldEditor* view_attack); + +View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 13ed005f1c..d12c3e3807 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -2,9 +2,7 @@ #include "../fuzzer_i.h" #include -// #include -#include "../lib/worker/protocol.h" #include "../helpers/gui_const.h" struct FuzzerViewMain { From 3bd08ab31cb56ce30aaf6d6953dc3c6a12419072 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Sun, 4 Jun 2023 20:21:51 +0300 Subject: [PATCH 049/102] Fuzzer App: Load key file --- .../pacs_fuzzer/lib/worker/fake_worker.c | 119 +++++++++++++++--- .../pacs_fuzzer/lib/worker/fake_worker.h | 11 ++ .../scenes/fuzzer_scene_field_editor.c | 22 ++++ .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 36 +++++- applications/external/pacs_fuzzer/todo.md | 32 +++++ .../external/pacs_fuzzer/views/field_editor.c | 42 ++++++- .../external/pacs_fuzzer/views/field_editor.h | 11 +- 7 files changed, 247 insertions(+), 26 deletions(-) create mode 100644 applications/external/pacs_fuzzer/todo.md diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index ff74f29e10..b44790ebcb 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -12,6 +12,7 @@ #if defined(RFID_125_PROTOCOL) #define MAX_PAYLOAD_SIZE 6 +#include #include #include @@ -44,6 +45,7 @@ struct FuzzerWorker { uint8_t payload[MAX_PAYLOAD_SIZE]; Stream* uids_stream; uint16_t index; + uint8_t chusen_byte; bool treead_running; FuriTimer* timer; @@ -62,12 +64,11 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { const FuzzerProtocol* protocol = worker->protocol; - if(next) { - worker->index++; - } - switch(worker->attack_type) { case FuzzerWorkerAttackTypeDefaultDict: + if(next) { + worker->index++; + } if(worker->index < protocol->dict.len) { memcpy( worker->payload, @@ -78,6 +79,9 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { break; case FuzzerWorkerAttackTypeLoadFileCustomUids: { + if(next) { + worker->index++; + } uint8_t str_len = protocol->data_size * 2 + 1; FuriString* data_str = furi_string_alloc(); while(true) { @@ -113,6 +117,14 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { break; + case FuzzerWorkerAttackTypeLoadFile: + if(worker->payload[worker->index] != 0xFF) { + worker->payload[worker->index]++; + res = true; + } + + break; + default: break; } @@ -170,11 +182,7 @@ void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { memcpy(key, worker->payload, worker->protocol->data_size); } -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { - furi_assert(worker); - - bool res = false; - +static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtos protocol_index) { worker->protocol = &fuzzer_proto_items[protocol_index]; #if defined(RFID_125_PROTOCOL) @@ -185,6 +193,14 @@ bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index worker->protocol_id = ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); #endif +} + +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { + furi_assert(worker); + + bool res = false; + fuzzer_worker_set_protocol(worker, protocol_index); + worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; worker->index = 0; @@ -205,17 +221,7 @@ bool fuzzer_worker_attack_file_dict( furi_assert(file_path); bool res = false; - - worker->protocol = &fuzzer_proto_items[protocol_index]; - -#if defined(RFID_125_PROTOCOL) - worker->protocol_id = - protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); -#else - // TODO iButtonProtocolIdInvalid check - worker->protocol_id = - ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); -#endif + fuzzer_worker_set_protocol(worker, protocol_index); Storage* storage = furi_record_open(RECORD_STORAGE); worker->uids_stream = buffered_file_stream_alloc(storage); @@ -240,6 +246,79 @@ bool fuzzer_worker_attack_file_dict( return res; } +bool fuzzer_worker_attack_bf_byte( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + const uint8_t* uid, + uint8_t chusen) { + furi_assert(worker); + + bool res = false; + fuzzer_worker_set_protocol(worker, protocol_index); + + worker->attack_type = FuzzerWorkerAttackTypeLoadFile; + worker->index = chusen; + + memcpy(worker->payload, uid, worker->protocol->data_size); + + res = true; + + return res; +} + +// TODO make it protocol independent +bool fuzzer_worker_load_key_from_file( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + const char* filename) { + furi_assert(worker); + + bool res = false; + fuzzer_worker_set_protocol(worker, protocol_index); + +#if defined(RFID_125_PROTOCOL) + ProtocolId loaded_proto_id = lfrfid_dict_file_load(worker->protocols_items, filename); + if(loaded_proto_id == PROTOCOL_NO) { + // Err Cant load file + FURI_LOG_W(TAG, "Cant load file"); + } else if(worker->protocol_id != loaded_proto_id) { // Err wrong protocol + FURI_LOG_W(TAG, "Wrong protocol"); + FURI_LOG_W( + TAG, + "Selected: %s Loaded: %s", + worker->protocol->name, + protocol_dict_get_name(worker->protocols_items, loaded_proto_id)); + } else { + protocol_dict_get_data( + worker->protocols_items, worker->protocol_id, worker->payload, MAX_PAYLOAD_SIZE); + res = true; + } +#else + if(!ibutton_protocols_load(worker->protocols_items, worker->key, filename)) { + // Err Cant load file + FURI_LOG_W(TAG, "Cant load file"); + } else { + if(worker->protocol_id != ibutton_key_get_protocol_id(worker->key)) { + // Err wrong protocol + FURI_LOG_W(TAG, "Wrong protocol"); + FURI_LOG_W( + TAG, + "Selected: %s Loaded: %s", + worker->protocol->name, + ibutton_protocols_get_name( + worker->protocols_items, ibutton_key_get_protocol_id(worker->key))); + } else { + iButtonEditableData data; + ibutton_protocols_get_editable_data(worker->protocols_items, worker->key, &data); + memcpy(worker->payload, data.ptr, data.size); + res = true; + } + } +#endif + + return res; +} + FuzzerWorker* fuzzer_worker_alloc() { FuzzerWorker* worker = malloc(sizeof(FuzzerWorker)); diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 2628ac6496..b1eea19fb3 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -27,6 +27,12 @@ void fuzzer_worker_stop(FuzzerWorker* worker); bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index); +bool fuzzer_worker_attack_bf_byte( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + const uint8_t* uid, + uint8_t chusen); + bool fuzzer_worker_attack_file_dict( FuzzerWorker* worker, FuzzerProtos protocol_index, @@ -34,6 +40,11 @@ bool fuzzer_worker_attack_file_dict( void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key); +bool fuzzer_worker_load_key_from_file( + FuzzerWorker* worker, + FuzzerProtos protocol_index, + const char* filename); + void fuzzer_worker_set_uid_chaged_callback( FuzzerWorker* worker, FuzzerWorkerUidChagedCallback callback, diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index 197bd82c4c..52e19e9e0d 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -1,6 +1,8 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" +#define UID_MAX_SIZE 8 // TODO + void fuzzer_scene_field_editor_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -14,6 +16,17 @@ void fuzzer_scene_field_editor_on_enter(void* context) { fuzzer_view_field_editor_set_callback( app->field_editor_view, fuzzer_scene_field_editor_callback, app); + uint8_t uid[UID_MAX_SIZE]; + + uint8_t* uid_p = &uid[0]; + + fuzzer_worker_get_current_key(app->worker, uid_p); + + fuzzer_view_field_editor_reset_data( + app->field_editor_view, + uid_p, + fuzzer_proto_items[app->fuzzer_state.proto_index].data_size); // TODO + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); } @@ -29,6 +42,15 @@ bool fuzzer_scene_field_editor_on_event(void* context, SceneManagerEvent event) view_dispatcher_stop(app->view_dispatcher); } consumed = true; + } else if(event.event == FuzzerCustomEventViewFieldEditorOk) { + // TODO + if(fuzzer_worker_attack_bf_byte( + app->worker, + app->fuzzer_state.proto_index, + fuzzer_view_field_editor_get_uid(app->field_editor_view), + fuzzer_view_field_editor_get_index(app->field_editor_view))) { + scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); + } } } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index cb5e97c529..3f03835fac 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -30,6 +30,26 @@ static bool fuzzer_scene_main_load_custom_dict(void* context) { return res; } +static bool fuzzer_scene_main_load_key(void* context) { + furi_assert(context); + PacsFuzzerApp* app = context; + + FuzzerConsts* consts = app->fuzzer_const; + + furi_string_set_str(app->file_path, consts->path_key_folder); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, consts->key_extension, &I_rfid_10px); // TODO icon + browser_options.base_path = consts->path_key_folder; + browser_options.hide_ext = true; + + bool res = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + + return res; +} + void fuzzer_scene_main_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -70,9 +90,19 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { break; case FuzzerMainMenuIndexLoadFile: - // TODO Delete - scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); - + if(!fuzzer_scene_main_load_key(app)) { + break; + } else { + if(fuzzer_worker_load_key_from_file( + app->worker, + app->fuzzer_state.proto_index, + furi_string_get_cstr(app->file_path))) { + scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); + FURI_LOG_I("Scene", "Load ok"); + } else { + FURI_LOG_W("Scene", "Load err"); + } + } break; case FuzzerMainMenuIndexLoadFileCustomUids: diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md new file mode 100644 index 0000000000..c96e897e97 --- /dev/null +++ b/applications/external/pacs_fuzzer/todo.md @@ -0,0 +1,32 @@ +## Working Improvement + +#### Quality of life + +- [ ] Make the "Load File" independent of the current protocol +- [ ] Add pause + - [ ] Switching UIDs if possible +- [ ] Led and sound Notification +- [ ] Error Notification + - [ ] Custom UIDs dict loading + - [ ] Key file loading + - [ ] Anything else + +#### App functionality + +- [ ] Add `BFCustomerID` attack +- [ ] Save key logic + +## Code Improvement + +- [ ] GUI + - [ ] Rewrite `gui_const` logic + - [ ] Separate protocol name from `fuzzer_proto_items` + - [ ] Icon in dialog + - [ ] Description and buttons in `field_editor` view + - [ ] Protocol carousel in `main_menu` +- [ ] UID + - [ ] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` + - [ ] `UID_MAX_SIZE` +- [ ] Add pause + - [ ] Fix `Custom dict` attack when ended +- [ ] this can be simplified `fuzzer_proto_items` diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index daf8e9d24f..f073baf996 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -8,6 +8,8 @@ #define UID_STR_LENGTH 25 #define EDITOR_STRING_Y 50 +#define UID_MAX_SIZE 8 // TODO + struct FuzzerViewFieldEditor { View* view; FuzzerViewFieldEditorCallback callback; @@ -18,8 +20,10 @@ struct FuzzerViewFieldEditor { typedef struct { uint8_t* uid; uint8_t uid_size; - uint8_t index; + FuriString* uid_str; + + uint8_t index; bool lo; } FuzzerViewFieldEditorModel; @@ -33,6 +37,40 @@ void fuzzer_view_field_editor_set_callback( view_edit->context = context; } +void fuzzer_view_field_editor_reset_data( + FuzzerViewFieldEditor* view_edit, + uint8_t* uid, + uint8_t uid_size) { + furi_assert(view_edit); + + with_view_model( + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + memcpy(model->uid, uid, uid_size); + model->index = 0; + model->lo = false; + model->uid_size = uid_size; + }, + true); +} + +const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + uint8_t* uid; + with_view_model( + view_edit->view, FuzzerViewFieldEditorModel * model, { uid = model->uid; }, true); + return uid; +} + +uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit) { + furi_assert(view_edit); + uint8_t index; + with_view_model( + view_edit->view, FuzzerViewFieldEditorModel * model, { index = model->index; }, true); + return index; +} + void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); @@ -223,7 +261,7 @@ FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc() { { model->uid_str = furi_string_alloc(); - model->uid = malloc(8); + model->uid = malloc(UID_MAX_SIZE); }, true); diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h index df3aba7247..26a8a8ac95 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.h +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -16,4 +16,13 @@ FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc(); void fuzzer_view_field_editor_free(FuzzerViewFieldEditor* view_attack); -View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); \ No newline at end of file +View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); + +void fuzzer_view_field_editor_reset_data( + FuzzerViewFieldEditor* view_edit, + uint8_t* uid, + uint8_t uid_size); + +const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit); + +uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit); \ No newline at end of file From b95620cdd0fe8332e9f499551fdd9c6eceef4975 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:10:51 +0300 Subject: [PATCH 050/102] Fuzzer App: Some Improvement --- applications/external/pacs_fuzzer/fuzzer.c | 2 + applications/external/pacs_fuzzer/fuzzer_i.h | 1 + .../external/pacs_fuzzer/helpers/gui_const.c | 7 -- .../external/pacs_fuzzer/helpers/gui_const.h | 12 --- .../pacs_fuzzer/lib/worker/fake_worker.c | 20 ++--- .../pacs_fuzzer/lib/worker/fake_worker.h | 10 +-- .../pacs_fuzzer/lib/worker/protocol.c | 73 +++++++++++++++---- .../pacs_fuzzer/lib/worker/protocol.h | 34 +++++---- .../pacs_fuzzer/lib/worker/protocol_i.h | 52 ++++++++----- .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 35 ++++----- .../scenes/fuzzer_scene_field_editor.c | 14 +--- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 3 +- applications/external/pacs_fuzzer/todo.md | 12 +-- .../external/pacs_fuzzer/views/attack.c | 37 ++++++---- .../external/pacs_fuzzer/views/attack.h | 6 +- .../external/pacs_fuzzer/views/field_editor.c | 12 +-- .../external/pacs_fuzzer/views/field_editor.h | 5 +- .../external/pacs_fuzzer/views/main_menu.c | 32 +++++--- 18 files changed, 213 insertions(+), 154 deletions(-) delete mode 100644 applications/external/pacs_fuzzer/helpers/gui_const.c delete mode 100644 applications/external/pacs_fuzzer/helpers/gui_const.h diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index 6b609040cf..0a9aa3f7d7 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -113,6 +113,7 @@ int32_t fuzzer_start_ibtn(void* p) { .custom_dict_extension = ".txt", .key_extension = ".ibtn", .path_key_folder = "/ext/ibutton", + .key_icon = &I_ibutt_10px, }; fuzzer_app->fuzzer_const = &app_const; @@ -131,6 +132,7 @@ int32_t fuzzer_start_rfid(void* p) { .custom_dict_extension = ".txt", .key_extension = ".rfid", .path_key_folder = "/ext/lfrfid", + .key_icon = &I_125_10px, }; fuzzer_app->fuzzer_const = &app_const; diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 59ec2df338..2f24ec4311 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -28,6 +28,7 @@ typedef struct { const char* custom_dict_folder; const char* key_extension; const char* path_key_folder; + const Icon* key_icon; } FuzzerConsts; typedef struct { diff --git a/applications/external/pacs_fuzzer/helpers/gui_const.c b/applications/external/pacs_fuzzer/helpers/gui_const.c deleted file mode 100644 index 79e31ad1b1..0000000000 --- a/applications/external/pacs_fuzzer/helpers/gui_const.c +++ /dev/null @@ -1,7 +0,0 @@ -#include "gui_const.h" - -const char* fuzzer_attack_names[FuzzerMainMenuIndexMax] = { - [FuzzerMainMenuIndexDefaultValues] = "Default Values", - [FuzzerMainMenuIndexLoadFile] = "Load File", - [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", -}; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/helpers/gui_const.h b/applications/external/pacs_fuzzer/helpers/gui_const.h deleted file mode 100644 index 837322c2ae..0000000000 --- a/applications/external/pacs_fuzzer/helpers/gui_const.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -// TODO replace it -typedef enum { - FuzzerMainMenuIndexDefaultValues = 0, - FuzzerMainMenuIndexLoadFile, - FuzzerMainMenuIndexLoadFileCustomUids, - - FuzzerMainMenuIndexMax, -} FuzzerMainMenuIndex; - -extern const char* fuzzer_attack_names[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index b44790ebcb..f4a442bc0c 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -1,4 +1,5 @@ #include "fake_worker.h" +#include "protocol_i.h" #include @@ -11,14 +12,12 @@ #if defined(RFID_125_PROTOCOL) -#define MAX_PAYLOAD_SIZE 6 #include #include #include #else -#define MAX_PAYLOAD_SIZE 8 #include #include @@ -175,14 +174,17 @@ static void fuzzer_worker_on_tick_callback(void* context) { } } -void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key) { +void fuzzer_worker_get_current_key(FuzzerWorker* worker, FuzzerPayload* output_key) { furi_assert(worker); + furi_assert(output_key); furi_assert(worker->protocol); - memcpy(key, worker->payload, worker->protocol->data_size); + output_key->data_size = worker->protocol->data_size; + output_key->data = malloc(sizeof(output_key->data_size)); + memcpy(output_key->data, worker->payload, worker->protocol->data_size); } -static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtos protocol_index) { +static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtocolsID protocol_index) { worker->protocol = &fuzzer_proto_items[protocol_index]; #if defined(RFID_125_PROTOCOL) @@ -195,7 +197,7 @@ static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtos protoc #endif } -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index) { +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index) { furi_assert(worker); bool res = false; @@ -215,7 +217,7 @@ bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index bool fuzzer_worker_attack_file_dict( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, FuriString* file_path) { furi_assert(worker); furi_assert(file_path); @@ -248,7 +250,7 @@ bool fuzzer_worker_attack_file_dict( bool fuzzer_worker_attack_bf_byte( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, const uint8_t* uid, uint8_t chusen) { furi_assert(worker); @@ -269,7 +271,7 @@ bool fuzzer_worker_attack_bf_byte( // TODO make it protocol independent bool fuzzer_worker_load_key_from_file( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, const char* filename) { furi_assert(worker); diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index b1eea19fb3..fe680f36b4 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -25,24 +25,24 @@ bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); void fuzzer_worker_stop(FuzzerWorker* worker); -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtos protocol_index); +bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index); bool fuzzer_worker_attack_bf_byte( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, const uint8_t* uid, uint8_t chusen); bool fuzzer_worker_attack_file_dict( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, FuriString* file_path); -void fuzzer_worker_get_current_key(FuzzerWorker* worker, uint8_t* key); +void fuzzer_worker_get_current_key(FuzzerWorker* worker, FuzzerPayload* output_key); bool fuzzer_worker_load_key_from_file( FuzzerWorker* worker, - FuzzerProtos protocol_index, + FuzzerProtocolsID protocol_index, const char* filename); void fuzzer_worker_set_uid_chaged_callback( diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index b27524d069..b9f9e6bf62 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -1,4 +1,5 @@ -#include "protocol.h" +#include "protocol_i.h" +#include "furi.h" // ####################### // ## Ibutton Protocols ## @@ -156,32 +157,40 @@ const FuzzerProtocol fuzzer_proto_items[] = { .name = "EM4100", .data_size = EM4100_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_em4100, - .len = sizeof(uid_list_em4100) / EM4100_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_em4100, + .len = COUNT_OF(uid_list_em4100), + }, }, [HIDProx] = { .name = "HIDProx", .data_size = HIDProx_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_hid, - .len = sizeof(uid_list_hid) / HIDProx_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_hid, + .len = COUNT_OF(uid_list_hid), + }, }, [PAC] = { .name = "PAC/Stanley", .data_size = PAC_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_pac, - .len = sizeof(uid_list_pac) / PAC_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_pac, + .len = COUNT_OF(uid_list_pac), + }, }, [H10301] = { .name = "H10301", .data_size = H10301_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_h10301, - .len = sizeof(uid_list_h10301) / H10301_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_h10301, + .len = COUNT_OF(uid_list_h10301), + }, }, }; #else @@ -191,24 +200,56 @@ const FuzzerProtocol fuzzer_proto_items[] = { .name = "DS1990", .data_size = DS1990_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_ds1990, - .len = sizeof(uid_list_ds1990) / DS1990_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_ds1990, + .len = COUNT_OF(uid_list_ds1990), + }, }, [Metakom] = { .name = "Metakom", .data_size = Metakom_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_metakom, - .len = sizeof(uid_list_metakom) / Metakom_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_metakom, + .len = COUNT_OF(uid_list_metakom), + }, }, [Cyfral] = { .name = "Cyfral", .data_size = Cyfral_DATA_SIZE, .dict = - {.val = (const uint8_t*)&uid_list_cyfral, - .len = sizeof(uid_list_cyfral) / Cyfral_DATA_SIZE}, + { + .val = (const uint8_t*)&uid_list_cyfral, + .len = COUNT_OF(uid_list_cyfral), + }, }, }; -#endif \ No newline at end of file +#endif + +const char* fuzzer_attack_names[] = { + [FuzzerMainMenuIndexDefaultValues] = "Default Values", + [FuzzerMainMenuIndexLoadFile] = "Load File", + [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", +}; + +const char* fuzzer_proto_get_name(FuzzerProtocolsID index) { + return fuzzer_proto_items[index].name; +} + +uint8_t fuzzer_proto_get_count_of_protocols() { + return COUNT_OF(fuzzer_proto_items); +} + +uint8_t fuzzer_proto_get_max_data_size() { + return MAX_PAYLOAD_SIZE; +} + +const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index) { + return fuzzer_attack_names[index]; +} + +uint8_t fuzzer_proto_get_count_of_menu_items() { + return COUNT_OF(fuzzer_attack_names); +} \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index a69d3db04b..0de1223ffc 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -4,8 +4,9 @@ // #define RFID_125_PROTOCOL -typedef enum { +typedef struct FuzzerPayload FuzzerPayload; +typedef enum { #if defined(RFID_125_PROTOCOL) EM4100, HIDProx, @@ -16,24 +17,25 @@ typedef enum { Metakom, Cyfral, #endif +} FuzzerProtocolsID; - // Reserved - FuzzerProtoMax, -} FuzzerProtos; - -struct ProtoDict { - const uint8_t* val; - const uint8_t len; +typedef enum { + FuzzerMainMenuIndexDefaultValues = 0, + FuzzerMainMenuIndexLoadFile, + FuzzerMainMenuIndexLoadFileCustomUids, +} FuzzerMainMenuIndex; + +struct FuzzerPayload { + uint8_t* data; + uint8_t data_size; }; -typedef struct ProtoDict ProtoDict; +uint8_t fuzzer_proto_get_max_data_size(); -struct FuzzerProtocol { - const char* name; - const uint8_t data_size; - const ProtoDict dict; -}; +const char* fuzzer_proto_get_name(FuzzerProtocolsID index); + +uint8_t fuzzer_proto_get_count_of_protocols(); -typedef struct FuzzerProtocol FuzzerProtocol; +const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index); -extern const FuzzerProtocol fuzzer_proto_items[]; \ No newline at end of file +uint8_t fuzzer_proto_get_count_of_menu_items(); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h index e0ab76cb35..841784f167 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -3,29 +3,45 @@ #include "protocol.h" #if defined(RFID_125_PROTOCOL) +#define MAX_PAYLOAD_SIZE (6) +#else +#define MAX_PAYLOAD_SIZE (8) +#endif -#define MAX_PAYLOAD_SIZE 6 +typedef struct ProtoDict ProtoDict; +typedef struct FuzzerProtocol FuzzerProtocol; -#define FUZZ_TIME_DELAY_MIN (5) -#define FUZZ_TIME_DELAY_DEFAULT (10) -#define FUZZ_TIME_DELAY_MAX (70) +struct ProtoDict { + const uint8_t* val; + const uint8_t len; // TODO +}; -#define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" -#define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/rfidfuzzer" -#define FUZZER_APP_KEY_EXTENSION ".rfid" -#define FUZZER_APP_PATH_KEY_FOLDER "/ext/lfrfid" +struct FuzzerProtocol { + const char* name; + const uint8_t data_size; + const ProtoDict dict; +}; -#else +// #define MAX_PAYLOAD_SIZE 6 -#define MAX_PAYLOAD_SIZE 8 +// #define FUZZ_TIME_DELAY_MIN (5) +// #define FUZZ_TIME_DELAY_DEFAULT (10) +// #define FUZZ_TIME_DELAY_MAX (70) -#define FUZZ_TIME_DELAY_MIN (4) -#define FUZZ_TIME_DELAY_DEFAULT (8) -#define FUZZ_TIME_DELAY_MAX (80) +// #define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" +// #define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/rfidfuzzer" +// #define FUZZER_APP_KEY_EXTENSION ".rfid" +// #define FUZZER_APP_PATH_KEY_FOLDER "/ext/lfrfid" -#define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" -#define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/ibtnfuzzer" -#define FUZZER_APP_KEY_EXTENSION ".ibtn" -#define FUZZER_APP_PATH_KEY_FOLDER "/ext/ibutton" +// #define MAX_PAYLOAD_SIZE 8 -#endif +// #define FUZZ_TIME_DELAY_MIN (4) +// #define FUZZ_TIME_DELAY_DEFAULT (8) +// #define FUZZ_TIME_DELAY_MAX (80) + +// #define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" +// #define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/ibtnfuzzer" +// #define FUZZER_APP_KEY_EXTENSION ".ibtn" +// #define FUZZER_APP_PATH_KEY_FOLDER "/ext/ibutton" + +extern const FuzzerProtocol fuzzer_proto_items[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index c18e2cec94..ac3962f329 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -1,8 +1,6 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -#include "../helpers/gui_const.h" - // TODO simlify callbacks and attack state void fuzzer_scene_attack_worker_tick_callback(void* context) { @@ -23,30 +21,37 @@ void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, event); } +static void fuzzer_scene_attack_update_uid(PacsFuzzerApp* app) { + furi_assert(app); + furi_assert(app->worker); + furi_assert(app->attack_view); + + FuzzerPayload uid; + fuzzer_worker_get_current_key(app->worker, &uid); + + fuzzer_view_attack_set_uid(app->attack_view, uid); + + free(uid.data); +} + void fuzzer_scene_attack_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; fuzzer_view_attack_set_callback(app->attack_view, fuzzer_scene_attack_callback, app); - FuzzerProtocol proto = fuzzer_proto_items[app->fuzzer_state.proto_index]; - fuzzer_worker_set_uid_chaged_callback( app->worker, fuzzer_scene_attack_worker_tick_callback, app); fuzzer_worker_set_end_callback(app->worker, fuzzer_scene_attack_worker_end_callback, app); - uint8_t temp_uid[proto.data_size]; - - fuzzer_worker_get_current_key(app->worker, temp_uid); - fuzzer_view_attack_reset_data( app->attack_view, - fuzzer_attack_names[app->fuzzer_state.menu_index], - proto.name, - proto.data_size); + fuzzer_proto_get_menu_label(app->fuzzer_state.menu_index), + fuzzer_proto_get_name(app->fuzzer_state.proto_index)); fuzzer_view_attack_set_attack(app->attack_view, false); - fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); + + fuzzer_scene_attack_update_uid(app); scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); @@ -84,11 +89,7 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackTick) { - uint8_t temp_uid[fuzzer_proto_items[app->fuzzer_state.proto_index].data_size]; - - fuzzer_worker_get_current_key(app->worker, temp_uid); - - fuzzer_view_attack_set_uid(app->attack_view, (uint8_t*)&temp_uid); + fuzzer_scene_attack_update_uid(app); consumed = true; } else if(event.event == FuzzerCustomEventViewAttackEnd) { scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index 52e19e9e0d..f8adae0dfd 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -1,8 +1,6 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -#define UID_MAX_SIZE 8 // TODO - void fuzzer_scene_field_editor_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -16,16 +14,12 @@ void fuzzer_scene_field_editor_on_enter(void* context) { fuzzer_view_field_editor_set_callback( app->field_editor_view, fuzzer_scene_field_editor_callback, app); - uint8_t uid[UID_MAX_SIZE]; - - uint8_t* uid_p = &uid[0]; + FuzzerPayload uid; + fuzzer_worker_get_current_key(app->worker, &uid); - fuzzer_worker_get_current_key(app->worker, uid_p); + fuzzer_view_field_editor_reset_data(app->field_editor_view, uid); - fuzzer_view_field_editor_reset_data( - app->field_editor_view, - uid_p, - fuzzer_proto_items[app->fuzzer_state.proto_index].data_size); // TODO + free(uid.data); view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 3f03835fac..e27bc07766 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -2,7 +2,6 @@ #include "../helpers/fuzzer_custom_event.h" #include "../lib/worker/protocol.h" -#include "../helpers/gui_const.h" void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { furi_assert(context); @@ -40,7 +39,7 @@ static bool fuzzer_scene_main_load_key(void* context) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( - &browser_options, consts->key_extension, &I_rfid_10px); // TODO icon + &browser_options, consts->key_extension, consts->key_icon); browser_options.base_path = consts->path_key_folder; browser_options.hide_ext = true; diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index c96e897e97..75e708c06f 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -19,14 +19,14 @@ ## Code Improvement - [ ] GUI - - [ ] Rewrite `gui_const` logic + - [x] Rewrite `gui_const` logic - [ ] Separate protocol name from `fuzzer_proto_items` - - [ ] Icon in dialog + - [x] Icon in dialog - [ ] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` -- [ ] UID - - [ ] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - - [ ] `UID_MAX_SIZE` +- [x] UID + - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` + - [x] `UID_MAX_SIZE` - [ ] Add pause - [ ] Fix `Custom dict` attack when ended -- [ ] this can be simplified `fuzzer_proto_items` +- [x] this can be simplified `fuzzer_proto_items` diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 910d69c0cf..6ef306f079 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -5,6 +5,7 @@ #include #define ATTACK_SCENE_MAX_UID_LENGTH 25 +#define UID_MAX_DISPLAYED_LEN (8U) struct FuzzerViewAttack { View* view; @@ -18,14 +19,12 @@ typedef struct { const char* protocol_name; bool attack_enabled; char* uid; - uint8_t uid_size; } FuzzerViewAttackModel; void fuzzer_view_attack_reset_data( FuzzerViewAttack* view, const char* attack_name, - const char* protocol_name, - uint8_t uid_size) { + const char* protocol_name) { furi_assert(view); with_view_model( @@ -35,32 +34,38 @@ void fuzzer_view_attack_reset_data( model->attack_name = attack_name; model->protocol_name = protocol_name; model->attack_enabled = false; - model->uid_size = uid_size; + strcpy(model->uid, "Not_set"); }, true); } -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid) { +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid) { furi_assert(view); + // TODO fix it + uint8_t* data = malloc(uid.data_size); + memcpy(data, uid.data, uid.data_size); + with_view_model( view->view, FuzzerViewAttackModel * model, { snprintf( model->uid, - model->uid_size * 3, + uid.data_size * 3, "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", - uid[0], - uid[1], - uid[2], - uid[3], - uid[4], - uid[5], - uid[6], - uid[7]); + data[0], + data[1], + data[2], + data[3], + data[4], + data[5], + data[6], + data[7]); }, true); + + free(data); } void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack) { @@ -175,6 +180,10 @@ void fuzzer_view_attack_exit(void* context) { } FuzzerViewAttack* fuzzer_view_attack_alloc() { + if(fuzzer_proto_get_max_data_size() > UID_MAX_DISPLAYED_LEN) { + furi_crash("Maximum of displayed bytes exceeded"); + } + FuzzerViewAttack* view_attack = malloc(sizeof(FuzzerViewAttack)); // View allocation and configuration diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index c8204eb185..e1aa4edae0 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -2,6 +2,7 @@ #include #include "../helpers/fuzzer_custom_event.h" +#include "../lib/worker/protocol.h" typedef struct FuzzerViewAttack FuzzerViewAttack; @@ -21,10 +22,9 @@ View* fuzzer_view_attack_get_view(FuzzerViewAttack* view_attack); void fuzzer_view_attack_reset_data( FuzzerViewAttack* view, const char* attack_name, - const char* protocol_name, - uint8_t uid_size); + const char* protocol_name); -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const uint8_t* uid); +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid); void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack); diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index f073baf996..53e15e152f 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -8,8 +8,6 @@ #define UID_STR_LENGTH 25 #define EDITOR_STRING_Y 50 -#define UID_MAX_SIZE 8 // TODO - struct FuzzerViewFieldEditor { View* view; FuzzerViewFieldEditorCallback callback; @@ -39,18 +37,17 @@ void fuzzer_view_field_editor_set_callback( void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - uint8_t* uid, - uint8_t uid_size) { + const FuzzerPayload new_uid) { furi_assert(view_edit); with_view_model( view_edit->view, FuzzerViewFieldEditorModel * model, { - memcpy(model->uid, uid, uid_size); + memcpy(model->uid, new_uid.data, new_uid.data_size); model->index = 0; model->lo = false; - model->uid_size = uid_size; + model->uid_size = new_uid.data_size; }, true); } @@ -260,8 +257,7 @@ FuzzerViewFieldEditor* fuzzer_view_field_editor_alloc() { FuzzerViewFieldEditorModel * model, { model->uid_str = furi_string_alloc(); - - model->uid = malloc(UID_MAX_SIZE); + model->uid = malloc(fuzzer_proto_get_max_data_size()); }, true); diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h index 26a8a8ac95..f76b5d3369 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.h +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -2,6 +2,7 @@ #include #include "../helpers/fuzzer_custom_event.h" +#include "../lib/worker/protocol.h" typedef struct FuzzerViewFieldEditor FuzzerViewFieldEditor; @@ -20,9 +21,9 @@ View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - uint8_t* uid, - uint8_t uid_size); + const FuzzerPayload new_uid); +// TODO const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit); uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index d12c3e3807..8194275ec6 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -3,7 +3,7 @@ #include -#include "../helpers/gui_const.h" +#include "../lib/worker/protocol.h" struct FuzzerViewMain { View* view; @@ -15,6 +15,8 @@ struct FuzzerViewMain { typedef struct { uint8_t proto_index; uint8_t menu_index; + uint8_t proto_max; + uint8_t menu_max; } FuzzerViewMainModel; void fuzzer_view_main_update_data(FuzzerViewMain* view, FuzzerState state) { @@ -58,23 +60,33 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { if(model->menu_index > 0) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, 64, 24, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index - 1]); + canvas, + 64, + 24, + AlignCenter, + AlignTop, + fuzzer_proto_get_menu_label(model->menu_index - 1)); } canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned( - canvas, 64, 36, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index]); + canvas, 64, 36, AlignCenter, AlignTop, fuzzer_proto_get_menu_label(model->menu_index)); - if(model->menu_index < FuzzerMainMenuIndexMax) { + if(model->menu_index < model->menu_max) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( - canvas, 64, 48, AlignCenter, AlignTop, fuzzer_attack_names[model->menu_index + 1]); + canvas, + 64, + 48, + AlignCenter, + AlignTop, + fuzzer_proto_get_menu_label(model->menu_index + 1)); } canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); canvas_draw_str_aligned( - canvas, 64, 4, AlignCenter, AlignTop, fuzzer_proto_items[model->proto_index].name); + canvas, 64, 4, AlignCenter, AlignTop, fuzzer_proto_get_name(model->proto_index)); canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); } @@ -94,7 +106,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { view->view, FuzzerViewMainModel * model, { - if(model->menu_index < (FuzzerMainMenuIndexMax - 1)) { + if(model->menu_index < (model->menu_max - 1)) { model->menu_index++; } }, @@ -119,7 +131,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { if(model->proto_index != 0) { model->proto_index--; } else { - model->proto_index = (FuzzerProtoMax - 1); + model->proto_index = (model->proto_max - 1); } }, true); @@ -129,7 +141,7 @@ bool fuzzer_view_main_input(InputEvent* event, void* context) { view->view, FuzzerViewMainModel * model, { - if(model->proto_index == (FuzzerProtoMax - 1)) { + if(model->proto_index == (model->proto_max - 1)) { model->proto_index = 0; } else { model->proto_index++; @@ -167,7 +179,9 @@ FuzzerViewMain* fuzzer_view_main_alloc() { FuzzerViewMainModel * model, { model->proto_index = 0; + model->proto_max = fuzzer_proto_get_count_of_protocols(); model->menu_index = 0; + model->menu_max = fuzzer_proto_get_count_of_menu_items(); }, true); return view; From 6eed74c71628f4a1e98fda43f58b3d4bb1077293 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 14:35:15 +0300 Subject: [PATCH 051/102] Fuzzer App: prtocol carusel prototype --- .../pacs_fuzzer/helpers/fuzzer_types.h | 1 - applications/external/pacs_fuzzer/todo.md | 1 + .../external/pacs_fuzzer/views/main_menu.c | 35 +++++++++++++++++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index 7e390f8754..a50b89c613 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -1,7 +1,6 @@ #pragma once #include -#include typedef struct { uint8_t menu_index; diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 75e708c06f..1b56ec189a 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -24,6 +24,7 @@ - [x] Icon in dialog - [ ] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` + - [x] prototype - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] `UID_MAX_SIZE` diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 8194275ec6..8c4c236037 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -5,6 +5,9 @@ #include "../lib/worker/protocol.h" +#define PROTOCOL_NAME_Y 12 +// #define PROTOCOL_CAROUSEL + struct FuzzerViewMain { View* view; FuzzerViewMainCallback callback; @@ -84,10 +87,36 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { } canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); + canvas_draw_str_aligned(canvas, 27, PROTOCOL_NAME_Y, AlignCenter, AlignBottom, "<"); + canvas_draw_str_aligned( + canvas, + 64, + PROTOCOL_NAME_Y, + AlignCenter, + AlignBottom, + fuzzer_proto_get_name(model->proto_index)); + canvas_draw_str_aligned(canvas, 101, PROTOCOL_NAME_Y, AlignCenter, AlignBottom, ">"); + +#ifdef PROTOCOL_CAROUSEL + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 20, + PROTOCOL_NAME_Y, + AlignRight, + AlignBottom, + (model->proto_index > 0) ? fuzzer_proto_get_name(model->proto_index - 1) : + fuzzer_proto_get_name((model->proto_max - 1))); canvas_draw_str_aligned( - canvas, 64, 4, AlignCenter, AlignTop, fuzzer_proto_get_name(model->proto_index)); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); + canvas, + 108, + PROTOCOL_NAME_Y, + AlignLeft, + AlignBottom, + (model->proto_index < (model->proto_max - 1)) ? + fuzzer_proto_get_name(model->proto_index + 1) : + fuzzer_proto_get_name(0)); +#endif } bool fuzzer_view_main_input(InputEvent* event, void* context) { From d3eb43ce3537643f78840b248e4f186d0cd160bb Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:49:30 +0300 Subject: [PATCH 052/102] Fuzzer App: Attack state --- .../pacs_fuzzer/helpers/fuzzer_types.h | 8 +++ .../pacs_fuzzer/lib/worker/fake_worker.c | 35 +++++++++-- .../pacs_fuzzer/lib/worker/fake_worker.h | 2 + .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 53 ++++++++++------ applications/external/pacs_fuzzer/todo.md | 6 +- .../external/pacs_fuzzer/views/attack.c | 63 ++++++++++++++++--- .../external/pacs_fuzzer/views/attack.h | 11 +++- 7 files changed, 143 insertions(+), 35 deletions(-) diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index a50b89c613..259fc2b52d 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -7,6 +7,14 @@ typedef struct { uint8_t proto_index; } FuzzerState; +typedef enum { + FuzzerAttackStateOff = 0, + FuzzerAttackStateIdle, + FuzzerAttackStateRunning, + FuzzerAttackStateEnd, + +} FuzzerAttackState; + typedef enum { FuzzerViewIDMain, FuzzerViewIDAttack, diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index f4a442bc0c..97f6320852 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -156,7 +156,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { } if(!fuzzer_worker_load_key(worker, true)) { - fuzzer_worker_stop(worker); + fuzzer_worker_pause(worker); // XXX if(worker->end_callback) { worker->end_callback(worker->end_context); } @@ -377,12 +377,23 @@ bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); - worker->treead_running = true; + if(!worker->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_start_thread(worker->proto_worker); + lfrfid_worker_start_thread(worker->proto_worker); +#else + ibutton_worker_start_thread(worker->proto_worker); +#endif + FURI_LOG_D(TAG, "Worker Starting"); + worker->treead_running = true; + } else { + FURI_LOG_D(TAG, "Worker UnPaused"); + } + +#if defined(RFID_125_PROTOCOL) + // lfrfid_worker_start_thread(worker->proto_worker); lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); #else - ibutton_worker_start_thread(worker->proto_worker); + // ibutton_worker_start_thread(worker->proto_worker); ibutton_worker_emulate_start(worker->proto_worker, worker->key); #endif return true; @@ -390,6 +401,21 @@ bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { return false; } +void fuzzer_worker_pause(FuzzerWorker* worker) { + furi_assert(worker); + + furi_timer_stop(worker->timer); + + if(worker->treead_running) { +#if defined(RFID_125_PROTOCOL) + lfrfid_worker_stop(worker->proto_worker); +#else + ibutton_worker_stop(worker->proto_worker); +#endif + FURI_LOG_D(TAG, "Worker Paused"); + } +} + void fuzzer_worker_stop(FuzzerWorker* worker) { furi_assert(worker); @@ -403,6 +429,7 @@ void fuzzer_worker_stop(FuzzerWorker* worker) { ibutton_worker_stop(worker->proto_worker); ibutton_worker_stop_thread(worker->proto_worker); #endif + FURI_LOG_D(TAG, "Worker Stopping"); worker->treead_running = false; } diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index fe680f36b4..2f8733393e 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -25,6 +25,8 @@ bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); void fuzzer_worker_stop(FuzzerWorker* worker); +void fuzzer_worker_pause(FuzzerWorker* worker); + bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index); bool fuzzer_worker_attack_bf_byte( diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index ac3962f329..61fa842618 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -49,11 +49,10 @@ void fuzzer_scene_attack_on_enter(void* context) { app->attack_view, fuzzer_proto_get_menu_label(app->fuzzer_state.menu_index), fuzzer_proto_get_name(app->fuzzer_state.proto_index)); - fuzzer_view_attack_set_attack(app->attack_view, false); fuzzer_scene_attack_update_uid(app); - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDAttack); } @@ -65,35 +64,53 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == FuzzerCustomEventViewAttackBack) { - if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack)) { + if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == + FuzzerAttackStateRunning) { + // Pause if attack running + fuzzer_worker_pause(app->worker); + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); + fuzzer_view_attack_pause(app->attack_view); + } else { + // Exit + fuzzer_worker_stop(app->worker); + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateOff); + fuzzer_view_attack_stop(app->attack_view); if(!scene_manager_previous_scene(app->scene_manager)) { scene_manager_stop(app->scene_manager); view_dispatcher_stop(app->view_dispatcher); } - } else { - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); - fuzzer_view_attack_set_attack(app->attack_view, false); - fuzzer_worker_stop(app->worker); } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackOk) { - if(!scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) && - fuzzer_worker_start( - app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, true); - fuzzer_view_attack_set_attack(app->attack_view, true); - } else { - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); - fuzzer_view_attack_set_attack(app->attack_view, false); - fuzzer_worker_stop(app->worker); + if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == + FuzzerAttackStateIdle) { + // Start or Continue Attack + if(fuzzer_worker_start( + app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateRunning); + fuzzer_view_attack_start(app->attack_view); + } else { + // Error? + } + } else if( + scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == + FuzzerAttackStateRunning) { + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); + fuzzer_view_attack_pause(app->attack_view); + fuzzer_worker_pause(app->worker); // XXX } consumed = true; } else if(event.event == FuzzerCustomEventViewAttackTick) { fuzzer_scene_attack_update_uid(app); consumed = true; } else if(event.event == FuzzerCustomEventViewAttackEnd) { - scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, false); - fuzzer_view_attack_set_attack(app->attack_view, false); + scene_manager_set_scene_state( + app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateEnd); + fuzzer_view_attack_end(app->attack_view); consumed = true; } } diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 1b56ec189a..dd6f4fc697 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -3,7 +3,7 @@ #### Quality of life - [ ] Make the "Load File" independent of the current protocol -- [ ] Add pause +- [x] Add pause - [ ] Switching UIDs if possible - [ ] Led and sound Notification - [ ] Error Notification @@ -28,6 +28,6 @@ - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] `UID_MAX_SIZE` -- [ ] Add pause - - [ ] Fix `Custom dict` attack when ended +- [x] Add pause + - [x] Fix `Custom dict` attack when ended - [x] this can be simplified `fuzzer_proto_items` diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 6ef306f079..ad99f0132f 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -17,7 +17,7 @@ typedef struct { uint8_t time_delay; const char* attack_name; const char* protocol_name; - bool attack_enabled; + FuzzerAttackState attack_state; char* uid; } FuzzerViewAttackModel; @@ -33,7 +33,7 @@ void fuzzer_view_attack_reset_data( { model->attack_name = attack_name; model->protocol_name = protocol_name; - model->attack_enabled = false; + model->attack_state = FuzzerAttackStateIdle; strcpy(model->uid, "Not_set"); }, true); @@ -68,11 +68,44 @@ void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid) free(data); } -void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack) { +void fuzzer_view_attack_start(FuzzerViewAttack* view) { furi_assert(view); with_view_model( - view->view, FuzzerViewAttackModel * model, { model->attack_enabled = attack; }, true); + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateRunning; }, + true); +} + +void fuzzer_view_attack_stop(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateOff; }, + true); +} + +void fuzzer_view_attack_pause(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateIdle; }, + true); +} + +void fuzzer_view_attack_end(FuzzerViewAttack* view) { + furi_assert(view); + + with_view_model( + view->view, + FuzzerViewAttackModel * model, + { model->attack_state = FuzzerAttackStateEnd; }, + true); } void fuzzer_view_attack_set_callback( @@ -106,12 +139,15 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, model->uid); canvas_set_font(canvas, FontSecondary); - if(model->attack_enabled) { + if(model->attack_state == FuzzerAttackStateRunning) { elements_button_center(canvas, "Stop"); - } else { + } else if(model->attack_state == FuzzerAttackStateIdle) { elements_button_center(canvas, "Start"); elements_button_left(canvas, "TD -"); elements_button_right(canvas, "+ TD"); + } else if(model->attack_state == FuzzerAttackStateEnd) { + // elements_button_center(canvas, "Restart"); // Reset + elements_button_left(canvas, "Exit"); } } @@ -130,7 +166,8 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { view_attack->view, FuzzerViewAttackModel * model, { - if(!model->attack_enabled) { + if(model->attack_state == FuzzerAttackStateIdle) { + // TimeDelay if(event->type == InputTypeShort) { if(model->time_delay > FUZZ_TIME_DELAY_MIN) { model->time_delay--; @@ -142,6 +179,11 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { model->time_delay = FUZZ_TIME_DELAY_MIN; } } + } else if( + (model->attack_state == FuzzerAttackStateEnd) && + (event->type == InputTypeShort)) { + // Exit if Ended + view_attack->callback(FuzzerCustomEventViewAttackBack, view_attack->context); } }, true); @@ -151,7 +193,8 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { view_attack->view, FuzzerViewAttackModel * model, { - if(!model->attack_enabled) { + if(model->attack_state == FuzzerAttackStateIdle) { + // TimeDelay if(event->type == InputTypeShort) { if(model->time_delay < FUZZ_TIME_DELAY_MAX) { model->time_delay++; @@ -162,6 +205,8 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { model->time_delay = FUZZ_TIME_DELAY_MAX; } } + } else { + // Nothing } }, true); @@ -201,7 +246,7 @@ FuzzerViewAttack* fuzzer_view_attack_alloc() { { model->time_delay = FUZZ_TIME_DELAY_MIN; model->uid = malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); - model->attack_enabled = false; + model->attack_state = FuzzerAttackStateOff; strcpy(model->uid, "Not_set"); model->attack_name = "Not_set"; diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index e1aa4edae0..41fd857bfa 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -1,7 +1,10 @@ #pragma once #include + #include "../helpers/fuzzer_custom_event.h" +#include "../helpers/fuzzer_types.h" + #include "../lib/worker/protocol.h" typedef struct FuzzerViewAttack FuzzerViewAttack; @@ -26,6 +29,12 @@ void fuzzer_view_attack_reset_data( void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid); -void fuzzer_view_attack_set_attack(FuzzerViewAttack* view, bool attack); +void fuzzer_view_attack_start(FuzzerViewAttack* view); + +void fuzzer_view_attack_stop(FuzzerViewAttack* view); + +void fuzzer_view_attack_pause(FuzzerViewAttack* view); + +void fuzzer_view_attack_end(FuzzerViewAttack* view); uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view); \ No newline at end of file From 6a9f396663092a90241399148a102bdaef57739f Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 20:24:38 +0300 Subject: [PATCH 053/102] Fuzzer App: Some description --- .../pacs_fuzzer/lib/worker/fake_worker.c | 293 +++++++++--------- .../pacs_fuzzer/lib/worker/fake_worker.h | 121 ++++++-- .../pacs_fuzzer/lib/worker/protocol.h | 22 ++ .../scenes/fuzzer_scene_field_editor.c | 2 +- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 5 +- 5 files changed, 273 insertions(+), 170 deletions(-) diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 97f6320852..9d3d89cdfc 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -56,22 +56,22 @@ struct FuzzerWorker { void* end_context; }; -static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { - furi_assert(worker); - furi_assert(worker->protocol); +static bool fuzzer_worker_load_key(FuzzerWorker* instance, bool next) { + furi_assert(instance); + furi_assert(instance->protocol); bool res = false; - const FuzzerProtocol* protocol = worker->protocol; + const FuzzerProtocol* protocol = instance->protocol; - switch(worker->attack_type) { + switch(instance->attack_type) { case FuzzerWorkerAttackTypeDefaultDict: if(next) { - worker->index++; + instance->index++; } - if(worker->index < protocol->dict.len) { + if(instance->index < protocol->dict.len) { memcpy( - worker->payload, - &protocol->dict.val[worker->index * protocol->data_size], + instance->payload, + &protocol->dict.val[instance->index * protocol->data_size], protocol->data_size); res = true; } @@ -79,14 +79,14 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { case FuzzerWorkerAttackTypeLoadFileCustomUids: { if(next) { - worker->index++; + instance->index++; } uint8_t str_len = protocol->data_size * 2 + 1; FuriString* data_str = furi_string_alloc(); while(true) { furi_string_reset(data_str); - if(!stream_read_line(worker->uids_stream, data_str)) { - stream_rewind(worker->uids_stream); + if(!stream_read_line(instance->uids_stream, data_str)) { + stream_rewind(instance->uids_stream); // TODO Check empty file & close stream and storage break; } else if(furi_string_get_char(data_str, 0) == '#') { @@ -103,7 +103,7 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { if(!hex_char_to_uint8( furi_string_get_cstr(data_str)[i * 2], furi_string_get_cstr(data_str)[i * 2 + 1], - &worker->payload[i])) { + &instance->payload[i])) { parse_ok = false; break; } @@ -117,8 +117,8 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { break; case FuzzerWorkerAttackTypeLoadFile: - if(worker->payload[worker->index] != 0xFF) { - worker->payload[worker->index]++; + if(instance->payload[instance->index] != 0xFF) { + instance->payload[instance->index]++; res = true; } @@ -129,15 +129,15 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { } #if defined(RFID_125_PROTOCOL) protocol_dict_set_data( - worker->protocols_items, worker->protocol_id, worker->payload, MAX_PAYLOAD_SIZE); + instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE); #else - ibutton_key_set_protocol_id(worker->key, worker->protocol_id); + ibutton_key_set_protocol_id(instance->key, instance->protocol_id); iButtonEditableData data; - ibutton_protocols_get_editable_data(worker->protocols_items, worker->key, &data); + ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data); // TODO check data.size logic data.size = MAX_PAYLOAD_SIZE; - memcpy(data.ptr, worker->payload, MAX_PAYLOAD_SIZE); // data.size); + memcpy(data.ptr, instance->payload, MAX_PAYLOAD_SIZE); // data.size); #endif return res; } @@ -145,69 +145,69 @@ static bool fuzzer_worker_load_key(FuzzerWorker* worker, bool next) { static void fuzzer_worker_on_tick_callback(void* context) { furi_assert(context); - FuzzerWorker* worker = context; + FuzzerWorker* instance = context; - if(worker->treead_running) { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_stop(worker->proto_worker); + lfrfid_worker_stop(instance->proto_worker); #else - ibutton_worker_stop(worker->proto_worker); + ibutton_worker_stop(instance->proto_worker); #endif } - if(!fuzzer_worker_load_key(worker, true)) { - fuzzer_worker_pause(worker); // XXX - if(worker->end_callback) { - worker->end_callback(worker->end_context); + if(!fuzzer_worker_load_key(instance, true)) { + fuzzer_worker_pause(instance); // XXX + if(instance->end_callback) { + instance->end_callback(instance->end_context); } } else { - if(worker->treead_running) { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); + lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); #else - ibutton_worker_emulate_start(worker->proto_worker, worker->key); + ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif } - if(worker->tick_callback) { - worker->tick_callback(worker->tick_context); + if(instance->tick_callback) { + instance->tick_callback(instance->tick_context); } } } -void fuzzer_worker_get_current_key(FuzzerWorker* worker, FuzzerPayload* output_key) { - furi_assert(worker); +void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key) { + furi_assert(instance); furi_assert(output_key); - furi_assert(worker->protocol); + furi_assert(instance->protocol); - output_key->data_size = worker->protocol->data_size; + output_key->data_size = instance->protocol->data_size; output_key->data = malloc(sizeof(output_key->data_size)); - memcpy(output_key->data, worker->payload, worker->protocol->data_size); + memcpy(output_key->data, instance->payload, instance->protocol->data_size); } -static void fuzzer_worker_set_protocol(FuzzerWorker* worker, FuzzerProtocolsID protocol_index) { - worker->protocol = &fuzzer_proto_items[protocol_index]; +static void fuzzer_worker_set_protocol(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) { + instance->protocol = &fuzzer_proto_items[protocol_index]; #if defined(RFID_125_PROTOCOL) - worker->protocol_id = - protocol_dict_get_protocol_by_name(worker->protocols_items, worker->protocol->name); + instance->protocol_id = + protocol_dict_get_protocol_by_name(instance->protocols_items, instance->protocol->name); #else // TODO iButtonProtocolIdInvalid check - worker->protocol_id = - ibutton_protocols_get_id_by_name(worker->protocols_items, worker->protocol->name); + instance->protocol_id = + ibutton_protocols_get_id_by_name(instance->protocols_items, instance->protocol->name); #endif } -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index) { - furi_assert(worker); +bool fuzzer_worker_init_attack_dict(FuzzerWorker* instance, FuzzerProtocolsID protocol_index) { + furi_assert(instance); bool res = false; - fuzzer_worker_set_protocol(worker, protocol_index); + fuzzer_worker_set_protocol(instance, protocol_index); - worker->attack_type = FuzzerWorkerAttackTypeDefaultDict; - worker->index = 0; + instance->attack_type = FuzzerWorkerAttackTypeDefaultDict; + instance->index = 0; - if(!fuzzer_worker_load_key(worker, false)) { - worker->attack_type = FuzzerWorkerAttackTypeMax; + if(!fuzzer_worker_load_key(instance, false)) { + instance->attack_type = FuzzerWorkerAttackTypeMax; } else { res = true; } @@ -215,31 +215,31 @@ bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_ return res; } -bool fuzzer_worker_attack_file_dict( - FuzzerWorker* worker, +bool fuzzer_worker_init_attack_file_dict( + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, FuriString* file_path) { - furi_assert(worker); + furi_assert(instance); furi_assert(file_path); bool res = false; - fuzzer_worker_set_protocol(worker, protocol_index); + fuzzer_worker_set_protocol(instance, protocol_index); Storage* storage = furi_record_open(RECORD_STORAGE); - worker->uids_stream = buffered_file_stream_alloc(storage); + instance->uids_stream = buffered_file_stream_alloc(storage); if(!buffered_file_stream_open( - worker->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { - buffered_file_stream_close(worker->uids_stream); + instance->uids_stream, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + buffered_file_stream_close(instance->uids_stream); return res; } - worker->attack_type = FuzzerWorkerAttackTypeLoadFileCustomUids; - worker->index = 0; + instance->attack_type = FuzzerWorkerAttackTypeLoadFileCustomUids; + instance->index = 0; - if(!fuzzer_worker_load_key(worker, false)) { - worker->attack_type = FuzzerWorkerAttackTypeMax; - buffered_file_stream_close(worker->uids_stream); + if(!fuzzer_worker_load_key(instance, false)) { + instance->attack_type = FuzzerWorkerAttackTypeMax; + buffered_file_stream_close(instance->uids_stream); furi_record_close(RECORD_STORAGE); } else { res = true; @@ -248,20 +248,20 @@ bool fuzzer_worker_attack_file_dict( return res; } -bool fuzzer_worker_attack_bf_byte( - FuzzerWorker* worker, +bool fuzzer_worker_init_attack_bf_byte( + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, const uint8_t* uid, uint8_t chusen) { - furi_assert(worker); + furi_assert(instance); bool res = false; - fuzzer_worker_set_protocol(worker, protocol_index); + fuzzer_worker_set_protocol(instance, protocol_index); - worker->attack_type = FuzzerWorkerAttackTypeLoadFile; - worker->index = chusen; + instance->attack_type = FuzzerWorkerAttackTypeLoadFile; + instance->index = chusen; - memcpy(worker->payload, uid, worker->protocol->data_size); + memcpy(instance->payload, uid, instance->protocol->data_size); res = true; @@ -270,49 +270,49 @@ bool fuzzer_worker_attack_bf_byte( // TODO make it protocol independent bool fuzzer_worker_load_key_from_file( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, const char* filename) { - furi_assert(worker); + furi_assert(instance); bool res = false; - fuzzer_worker_set_protocol(worker, protocol_index); + fuzzer_worker_set_protocol(instance, protocol_index); #if defined(RFID_125_PROTOCOL) - ProtocolId loaded_proto_id = lfrfid_dict_file_load(worker->protocols_items, filename); + ProtocolId loaded_proto_id = lfrfid_dict_file_load(instance->protocols_items, filename); if(loaded_proto_id == PROTOCOL_NO) { // Err Cant load file FURI_LOG_W(TAG, "Cant load file"); - } else if(worker->protocol_id != loaded_proto_id) { // Err wrong protocol + } else if(instance->protocol_id != loaded_proto_id) { // Err wrong protocol FURI_LOG_W(TAG, "Wrong protocol"); FURI_LOG_W( TAG, "Selected: %s Loaded: %s", - worker->protocol->name, - protocol_dict_get_name(worker->protocols_items, loaded_proto_id)); + instance->protocol->name, + protocol_dict_get_name(instance->protocols_items, loaded_proto_id)); } else { protocol_dict_get_data( - worker->protocols_items, worker->protocol_id, worker->payload, MAX_PAYLOAD_SIZE); + instance->protocols_items, instance->protocol_id, instance->payload, MAX_PAYLOAD_SIZE); res = true; } #else - if(!ibutton_protocols_load(worker->protocols_items, worker->key, filename)) { + if(!ibutton_protocols_load(instance->protocols_items, instance->key, filename)) { // Err Cant load file FURI_LOG_W(TAG, "Cant load file"); } else { - if(worker->protocol_id != ibutton_key_get_protocol_id(worker->key)) { + if(instance->protocol_id != ibutton_key_get_protocol_id(instance->key)) { // Err wrong protocol FURI_LOG_W(TAG, "Wrong protocol"); FURI_LOG_W( TAG, "Selected: %s Loaded: %s", - worker->protocol->name, + instance->protocol->name, ibutton_protocols_get_name( - worker->protocols_items, ibutton_key_get_protocol_id(worker->key))); + instance->protocols_items, ibutton_key_get_protocol_id(instance->key))); } else { iButtonEditableData data; - ibutton_protocols_get_editable_data(worker->protocols_items, worker->key, &data); - memcpy(worker->payload, data.ptr, data.size); + ibutton_protocols_get_editable_data(instance->protocols_items, instance->key, &data); + memcpy(instance->payload, data.ptr, data.size); res = true; } } @@ -322,140 +322,141 @@ bool fuzzer_worker_load_key_from_file( } FuzzerWorker* fuzzer_worker_alloc() { - FuzzerWorker* worker = malloc(sizeof(FuzzerWorker)); + FuzzerWorker* instance = malloc(sizeof(FuzzerWorker)); #if defined(RFID_125_PROTOCOL) - worker->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + instance->protocols_items = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); - worker->proto_worker = lfrfid_worker_alloc(worker->protocols_items); + instance->proto_worker = lfrfid_worker_alloc(instance->protocols_items); #else - worker->protocols_items = ibutton_protocols_alloc(); - worker->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(worker->protocols_items)); + instance->protocols_items = ibutton_protocols_alloc(); + instance->key = + ibutton_key_alloc(ibutton_protocols_get_max_data_size(instance->protocols_items)); - worker->proto_worker = ibutton_worker_alloc(worker->protocols_items); + instance->proto_worker = ibutton_worker_alloc(instance->protocols_items); #endif - worker->attack_type = FuzzerWorkerAttackTypeMax; - worker->index = 0; - worker->treead_running = false; + instance->attack_type = FuzzerWorkerAttackTypeMax; + instance->index = 0; + instance->treead_running = false; - memset(worker->payload, 0x00, sizeof(worker->payload)); + memset(instance->payload, 0x00, sizeof(instance->payload)); - worker->timeer_delay = FUZZ_TIME_DELAY_DEFAULT; + instance->timeer_delay = FUZZ_TIME_DELAY_DEFAULT; - worker->timer = - furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypePeriodic, worker); + instance->timer = + furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypePeriodic, instance); - return worker; + return instance; } -void fuzzer_worker_free(FuzzerWorker* worker) { - furi_assert(worker); +void fuzzer_worker_free(FuzzerWorker* instance) { + furi_assert(instance); - fuzzer_worker_stop(worker); + fuzzer_worker_stop(instance); - furi_timer_free(worker->timer); + furi_timer_free(instance->timer); #if defined(RFID_125_PROTOCOL) - lfrfid_worker_free(worker->proto_worker); + lfrfid_worker_free(instance->proto_worker); - protocol_dict_free(worker->protocols_items); + protocol_dict_free(instance->protocols_items); #else - ibutton_worker_free(worker->proto_worker); + ibutton_worker_free(instance->proto_worker); - ibutton_key_free(worker->key); - ibutton_protocols_free(worker->protocols_items); + ibutton_key_free(instance->key); + ibutton_protocols_free(instance->protocols_items); #endif - free(worker); + free(instance); } -bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay) { - furi_assert(worker); +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { + furi_assert(instance); - if(worker->attack_type < FuzzerWorkerAttackTypeMax) { - worker->timeer_delay = timer_dellay; + if(instance->attack_type < FuzzerWorkerAttackTypeMax) { + instance->timeer_delay = timer_dellay; - furi_timer_start(worker->timer, furi_ms_to_ticks(timer_dellay * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(timer_dellay * 100)); - if(!worker->treead_running) { + if(!instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_start_thread(worker->proto_worker); + lfrfid_worker_start_thread(instance->proto_worker); #else - ibutton_worker_start_thread(worker->proto_worker); + ibutton_worker_start_thread(instance->proto_worker); #endif FURI_LOG_D(TAG, "Worker Starting"); - worker->treead_running = true; + instance->treead_running = true; } else { FURI_LOG_D(TAG, "Worker UnPaused"); } #if defined(RFID_125_PROTOCOL) - // lfrfid_worker_start_thread(worker->proto_worker); - lfrfid_worker_emulate_start(worker->proto_worker, worker->protocol_id); + // lfrfid_worker_start_thread(instance->proto_worker); + lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); #else - // ibutton_worker_start_thread(worker->proto_worker); - ibutton_worker_emulate_start(worker->proto_worker, worker->key); + // ibutton_worker_start_thread(instance->proto_worker); + ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif return true; } return false; } -void fuzzer_worker_pause(FuzzerWorker* worker) { - furi_assert(worker); +void fuzzer_worker_pause(FuzzerWorker* instance) { + furi_assert(instance); - furi_timer_stop(worker->timer); + furi_timer_stop(instance->timer); - if(worker->treead_running) { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_stop(worker->proto_worker); + lfrfid_worker_stop(instance->proto_worker); #else - ibutton_worker_stop(worker->proto_worker); + ibutton_worker_stop(instance->proto_worker); #endif FURI_LOG_D(TAG, "Worker Paused"); } } -void fuzzer_worker_stop(FuzzerWorker* worker) { - furi_assert(worker); +void fuzzer_worker_stop(FuzzerWorker* instance) { + furi_assert(instance); - furi_timer_stop(worker->timer); + furi_timer_stop(instance->timer); - if(worker->treead_running) { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_stop(worker->proto_worker); - lfrfid_worker_stop_thread(worker->proto_worker); + lfrfid_worker_stop(instance->proto_worker); + lfrfid_worker_stop_thread(instance->proto_worker); #else - ibutton_worker_stop(worker->proto_worker); - ibutton_worker_stop_thread(worker->proto_worker); + ibutton_worker_stop(instance->proto_worker); + ibutton_worker_stop_thread(instance->proto_worker); #endif FURI_LOG_D(TAG, "Worker Stopping"); - worker->treead_running = false; + instance->treead_running = false; } - if(worker->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) { - buffered_file_stream_close(worker->uids_stream); + if(instance->attack_type == FuzzerWorkerAttackTypeLoadFileCustomUids) { + buffered_file_stream_close(instance->uids_stream); furi_record_close(RECORD_STORAGE); - worker->attack_type = FuzzerWorkerAttackTypeMax; + instance->attack_type = FuzzerWorkerAttackTypeMax; } // TODO anything else } void fuzzer_worker_set_uid_chaged_callback( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerWorkerUidChagedCallback callback, void* context) { - furi_assert(worker); - worker->tick_callback = callback; - worker->tick_context = context; + furi_assert(instance); + instance->tick_callback = callback; + instance->tick_context = context; } void fuzzer_worker_set_end_callback( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerWorkerEndCallback callback, void* context) { - furi_assert(worker); - worker->end_callback = callback; - worker->end_context = context; + furi_assert(instance); + instance->end_callback = callback; + instance->end_context = context; } diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 2f8733393e..04635169b3 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -17,42 +17,121 @@ typedef void (*FuzzerWorkerEndCallback)(void* context); typedef struct FuzzerWorker FuzzerWorker; +/** + * Allocate FuzzerWorker + * + * @return FuzzerWorker* pointer to FuzzerWorker + */ FuzzerWorker* fuzzer_worker_alloc(); -void fuzzer_worker_free(FuzzerWorker* worker); - -bool fuzzer_worker_start(FuzzerWorker* worker, uint8_t timer_dellay); - -void fuzzer_worker_stop(FuzzerWorker* worker); - -void fuzzer_worker_pause(FuzzerWorker* worker); - -bool fuzzer_worker_attack_dict(FuzzerWorker* worker, FuzzerProtocolsID protocol_index); +/** + * Free FuzzerWorker + * + * @param instance Pointer to a FuzzerWorker + */ +void fuzzer_worker_free(FuzzerWorker* instance); + +/** + * Start or continue emulation + * + * @param instance Pointer to a FuzzerWorker + * @param timer_dellay Emulation time of one UID in tenths of a second + * @return bool True if emulation has started + */ +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay); + +/** + * Stop emulation and deinit worker + * + * @param instance Pointer to a FuzzerWorker + */ +void fuzzer_worker_stop(FuzzerWorker* instance); + +/** + * Suspend emulation + * + * @param instance Pointer to a FuzzerWorker + */ +void fuzzer_worker_pause(FuzzerWorker* instance); + +/** + * Init attack by default dictionary + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @return bool True if initialization is successful + */ +bool fuzzer_worker_init_attack_dict(FuzzerWorker* instance, FuzzerProtocolsID protocol_index); + +/** + * Init attack by custom dictionary + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @param file_path file path to the dictionary + * @return bool True if initialization is successful + */ +bool fuzzer_worker_init_attack_file_dict( + FuzzerWorker* instance, + FuzzerProtocolsID protocol_index, + FuriString* file_path); -bool fuzzer_worker_attack_bf_byte( - FuzzerWorker* worker, +/** + * Init attack brute force one of byte + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @param uid UID for brute force + * @param chosen index of chusen byte + * @return bool True if initialization is successful + */ +bool fuzzer_worker_init_attack_bf_byte( + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, const uint8_t* uid, uint8_t chusen); -bool fuzzer_worker_attack_file_dict( - FuzzerWorker* worker, - FuzzerProtocolsID protocol_index, - FuriString* file_path); - -void fuzzer_worker_get_current_key(FuzzerWorker* worker, FuzzerPayload* output_key); - +/** + * Get current UID + * + * @param instance Pointer to a FuzzerWorker + * @param output_key Pointer to a FuzzerWorker, memory for data will be allocated + */ +void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key); + +/** + * Load UID from Flipper Format Key file + * + * @param instance Pointer to a FuzzerWorker + * @param protocol_index index of the selected protocol + * @param filename file path to the key file + * @return bool True if loading is successful + */ bool fuzzer_worker_load_key_from_file( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerProtocolsID protocol_index, const char* filename); +/** + * Set callback for uid changed + * + * @param instance Pointer to a FuzzerWorker + * @param callback Callback for uid changed + * @param context Context for callback + */ void fuzzer_worker_set_uid_chaged_callback( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerWorkerUidChagedCallback callback, void* context); +/** + * Set callback for end of emulation + * + * @param instance Pointer to a FuzzerWorker + * @param callback Callback for end of emulation + * @param context Context for callback + */ void fuzzer_worker_set_end_callback( - FuzzerWorker* worker, + FuzzerWorker* instance, FuzzerWorkerEndCallback callback, void* context); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index 0de1223ffc..a2893123e6 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -30,12 +30,34 @@ struct FuzzerPayload { uint8_t data_size; }; +/** + * Get maximum length of UID among all supported protocols + * @return Maximum length of UID + */ uint8_t fuzzer_proto_get_max_data_size(); +/** + * Get protocol name based on its index + * @param index protocol index + * @return pointer to a string containing the name + */ const char* fuzzer_proto_get_name(FuzzerProtocolsID index); +/** + * Get number of protocols + * @return number of protocols + */ uint8_t fuzzer_proto_get_count_of_protocols(); +/** + * Get menu label based on its index + * @param index menu index + * @return pointer to a string containing the menu label + */ const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index); +/** + * Get number of menu items + * @return number of menu items + */ uint8_t fuzzer_proto_get_count_of_menu_items(); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index f8adae0dfd..637eff2d77 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -38,7 +38,7 @@ bool fuzzer_scene_field_editor_on_event(void* context, SceneManagerEvent event) consumed = true; } else if(event.event == FuzzerCustomEventViewFieldEditorOk) { // TODO - if(fuzzer_worker_attack_bf_byte( + if(fuzzer_worker_init_attack_bf_byte( app->worker, app->fuzzer_state.proto_index, fuzzer_view_field_editor_get_uid(app->field_editor_view), diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index e27bc07766..5ad37654ad 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -81,7 +81,8 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { switch(app->fuzzer_state.menu_index) { case FuzzerMainMenuIndexDefaultValues: - loading_ok = fuzzer_worker_attack_dict(app->worker, app->fuzzer_state.proto_index); + loading_ok = + fuzzer_worker_init_attack_dict(app->worker, app->fuzzer_state.proto_index); if(!loading_ok) { // error @@ -108,7 +109,7 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { if(!fuzzer_scene_main_load_custom_dict(app)) { break; } else { - loading_ok = fuzzer_worker_attack_file_dict( + loading_ok = fuzzer_worker_init_attack_file_dict( app->worker, app->fuzzer_state.proto_index, app->file_path); } break; From b346487e760e8d55505a91e117e801344237fd39 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Mon, 5 Jun 2023 21:19:32 +0300 Subject: [PATCH 054/102] Fuzzer App: BFCustomerID attack, some fix --- .../pacs_fuzzer/lib/worker/protocol.c | 28 +++++++++++++------ .../pacs_fuzzer/lib/worker/protocol.h | 18 ++++++++---- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 22 ++++++++++++--- applications/external/pacs_fuzzer/todo.md | 2 +- .../external/pacs_fuzzer/views/main_menu.c | 2 +- 5 files changed, 53 insertions(+), 19 deletions(-) diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index b9f9e6bf62..c295289ae8 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -228,10 +228,18 @@ const FuzzerProtocol fuzzer_proto_items[] = { }; #endif -const char* fuzzer_attack_names[] = { - [FuzzerMainMenuIndexDefaultValues] = "Default Values", - [FuzzerMainMenuIndexLoadFile] = "Load File", - [FuzzerMainMenuIndexLoadFileCustomUids] = "Load UIDs from file", +typedef struct { + const char* menu_label; + FuzzerAttackId attack_id; +} FuzzerMenuItems; + +const FuzzerMenuItems fuzzer_menu_items[] = { + {"Default Values", FuzzerAttackIdDefaultValues}, +#ifdef RFID_125_PROTOCOL + {"BF Customer ID", FuzzerAttackIdBFCustomerID}, +#endif + {"Load File", FuzzerAttackIdLoadFile}, + {"Load UIDs from file", FuzzerAttackIdLoadFileCustomUids}, }; const char* fuzzer_proto_get_name(FuzzerProtocolsID index) { @@ -246,10 +254,14 @@ uint8_t fuzzer_proto_get_max_data_size() { return MAX_PAYLOAD_SIZE; } -const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index) { - return fuzzer_attack_names[index]; +const char* fuzzer_proto_get_menu_label(uint8_t index) { + return fuzzer_menu_items[index].menu_label; +} + +FuzzerAttackId fuzzer_proto_get_attack_id_by_index(uint8_t index) { + return fuzzer_menu_items[index].attack_id; } uint8_t fuzzer_proto_get_count_of_menu_items() { - return COUNT_OF(fuzzer_attack_names); -} \ No newline at end of file + return COUNT_OF(fuzzer_menu_items); +} diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index a2893123e6..4c2c70e0c6 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -20,10 +20,11 @@ typedef enum { } FuzzerProtocolsID; typedef enum { - FuzzerMainMenuIndexDefaultValues = 0, - FuzzerMainMenuIndexLoadFile, - FuzzerMainMenuIndexLoadFileCustomUids, -} FuzzerMainMenuIndex; + FuzzerAttackIdDefaultValues = 0, + FuzzerAttackIdLoadFile, + FuzzerAttackIdLoadFileCustomUids, + FuzzerAttackIdBFCustomerID, +} FuzzerAttackId; struct FuzzerPayload { uint8_t* data; @@ -54,7 +55,14 @@ uint8_t fuzzer_proto_get_count_of_protocols(); * @param index menu index * @return pointer to a string containing the menu label */ -const char* fuzzer_proto_get_menu_label(FuzzerMainMenuIndex index); +const char* fuzzer_proto_get_menu_label(uint8_t index); + +/** + * Get FuzzerAttackId based on its index + * @param index menu index + * @return FuzzerAttackId + */ +FuzzerAttackId fuzzer_proto_get_attack_id_by_index(uint8_t index); /** * Get number of menu items diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 5ad37654ad..6990374303 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -77,9 +77,11 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { // TODO error logic bool loading_ok = false; + uint8_t d_size = fuzzer_proto_get_max_data_size(); + uint8_t* uid; - switch(app->fuzzer_state.menu_index) { - case FuzzerMainMenuIndexDefaultValues: + switch(fuzzer_proto_get_attack_id_by_index(app->fuzzer_state.menu_index)) { + case FuzzerAttackIdDefaultValues: loading_ok = fuzzer_worker_init_attack_dict(app->worker, app->fuzzer_state.proto_index); @@ -88,8 +90,20 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { // error } break; + case FuzzerAttackIdBFCustomerID: + uid = malloc(d_size); + memset(uid, 0x00, d_size); - case FuzzerMainMenuIndexLoadFile: + loading_ok = fuzzer_worker_init_attack_bf_byte( + app->worker, app->fuzzer_state.proto_index, uid, 0); + + free(uid); + if(!loading_ok) { + // error + } + break; + + case FuzzerAttackIdLoadFile: if(!fuzzer_scene_main_load_key(app)) { break; } else { @@ -105,7 +119,7 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { } break; - case FuzzerMainMenuIndexLoadFileCustomUids: + case FuzzerAttackIdLoadFileCustomUids: if(!fuzzer_scene_main_load_custom_dict(app)) { break; } else { diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index dd6f4fc697..7afd3fad83 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -13,7 +13,7 @@ #### App functionality -- [ ] Add `BFCustomerID` attack +- [x] Add `BFCustomerID` attack - [ ] Save key logic ## Code Improvement diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 8c4c236037..49b0a0d9ca 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -75,7 +75,7 @@ void fuzzer_view_main_draw(Canvas* canvas, FuzzerViewMainModel* model) { canvas_draw_str_aligned( canvas, 64, 36, AlignCenter, AlignTop, fuzzer_proto_get_menu_label(model->menu_index)); - if(model->menu_index < model->menu_max) { + if(model->menu_index < (model->menu_max - 1)) { canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned( canvas, From 237d2ba1a01ff1ea371d92bdaf0df7527c1ac845 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 11:14:23 +0300 Subject: [PATCH 055/102] Fuzzer App: field_editor view V2 --- .../pacs_fuzzer/icons/ButtonLeft_4x7.png | Bin 0 -> 1415 bytes .../pacs_fuzzer/icons/ButtonRight_4x7.png | Bin 0 -> 1839 bytes .../external/pacs_fuzzer/icons/Ok_btn_9x9.png | Bin 0 -> 3605 bytes .../pacs_fuzzer/icons/Pin_arrow_up_7x9.png | Bin 0 -> 3603 bytes .../pacs_fuzzer/icons/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes applications/external/pacs_fuzzer/todo.md | 2 +- .../external/pacs_fuzzer/views/attack.c | 42 +++---- .../external/pacs_fuzzer/views/field_editor.c | 108 ++++++++++++------ .../external/pacs_fuzzer/views/main_menu.c | 1 - 9 files changed, 94 insertions(+), 59 deletions(-) create mode 100644 applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png create mode 100644 applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png create mode 100644 applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png create mode 100644 applications/external/pacs_fuzzer/icons/Pin_arrow_up_7x9.png create mode 100644 applications/external/pacs_fuzzer/icons/Pin_back_arrow_10x8.png diff --git a/applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png b/applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..0b4655d43247083aa705620e9836ac415b42ca46 GIT binary patch literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png b/applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1c74c1c0038ea55172f19ac875003fc80c2d06 GIT binary patch literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png b/applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png new file mode 100644 index 0000000000000000000000000000000000000000..9a1539da2049f12f7b25f96b11a9c40cd8227302 GIT binary patch literal 3605 zcmaJ@c{r5q+kR|?vSeS9G2*Q(Gqz$f_GQ#q8r!JE7=ytqjlqnNNGaK}Wlbolp-q`& zs|bxHiiEP0&{#s&zVZIv-rx7f*Y_O9^W67+-RF5;*L_{ra~$^-2RmyaK{-JH0EBE1 z7AVdru>JD$aK0bym%#uaXpT2Gcd#)x2azcxAABGV0BC)Aj-lw(6)B^^6`Y8RS?}DV z%)ko(See1!Eb3M$dL6)A6csaRjExg?k&xVzi*Rm;?iNJk#f=mkVEUR~jXN3dd|Lmz z;y}sMh%ol-?E1&`>dD;6jdps6NYoxN)s%@sf4~40YY6LAOtMEbwA4g#OCpANL823^ zSH66W05Hcxr$tg98gFntAOYL}xm$C;Skv&Ym?{TVR{)d(41vWacX1`7fM!jnW(lBK z26*WB#9I(Z1Ast!xEUC@Cj`v=urcBTdP`FWq=DYTy`}s>0vC{VzHdNRvxNFy}ir1|g=xDsrFP&l1P<-Sv zXLqYVYz{b^ZIV@1Ulg->7DEgvM*Min&Y8{8QW! z$_pA434?^wCTq$4%^>Zo8&|8XwbCv;KEd;WJJ{s;T}8R8Zwi7ssk$QWQ5l5+opKfX z;8D*COFEB#4W^*FIrRU%PDSc?B(}+9ZV?N9(yH>0uSnM?xg!>+>;e z{{7tXQQ|ZFXD*7q3XD!pwnih-=66+Qlqtl9;N-D|PHoI&B5d8>^V#i{mE>V0gQgu3+(DG%B z|8W!pl$lbQERt-0eZA%NSfvE4F>VAYP`DpeoF;Zm4`)2id;6xgSysWl6K$pWANcRZ z!ETRXKIU9G=@9lEB?<{ivj7!8FE9WN;qoo2Lr0#c@DmcF=JzU<73PmM3 zbe!-gs`c26Uc(AKz7%U!a0yZ5gsprdo1i51MjJPeHtV6d@Jy=*+_3dJ^>}p#8N#kPK_4t?hltq>u=?m+t z?em(Y%u3Bp_pyV?c_w-4c}p+?Y$aHr>TuPGs@SUj;Er!b@3GVLDS@T8OTts1JFS-p zKZ=&5zp;DRor*`Gy8MTeWdpVJv2(4-*slRM@XXG+i^F&Ku>7i08vKenZHoS4s(!!h zJE}*MHu7PR_IfdNzu*P}3^87K?f&A1;>NMsgKcR6**;aB74NC7tR(NB?{dHT-9QhXa*KoG!kGU1}$l2D>ypo)fSBuG$ zkTW4?+|I1m?6ZH8tD4^fB{cUpoEoZOo%4hl!EtNtQ#?j*jJR)x-Mn0TrxrX2uT_rh ziOh=Jxsktqbd9x{^s{c5z92Pk$LGoQl53o+=7QXXCp-Z>io998w|DCCCGfr20oiRN zX|`KH$W4)wN~)J$kYB~>4EU;NcS^qH&yzeUzXokpMegg_lX$6ve^4}%bY~Sg)%uJ- zZpb$p4x^GS5d{XJP=STbfpHV`58UBH& zKFg&BgS6bV+#-|^KBGeIBee2B zrM-`uTB^_(eS+{-KK1h3l`-Yjpv8X4z*uBwQ3a~pL0Ae2xvNGyC3A|#MARToe$W~8 z+4{DsyenENye9df1M}gNUM9_Leh6G=`9exL-cdSKQ_CGyEdZ3W5uoR!Lb^D)9!bd=7h@R=M%=|JqX9XP;Z6# zFD15Bw7qTP(ZlG?o@#x@=wG;XxM(>n@4P$9WwY#lW$h=`zMi_zq30HbV-zHheqpE0 zR6kXtxdzl&Ml2D#zDIvflJkb*e zIAI?GMjp?JBK76WW`{l{pFAY|%5?nYUxRnT&y6~Kz19AD;C0(z*7?dM{%HhVtqWEc z%+M$z6u@uQu)kg_%2PO_U|n1JE0V1>iVbekOLEOG$U6X^Umc519WC)L$t%`#Di0$ zY1|5H*440_`onhmXeayq`8EIg?x2r9KWe()q}QayqCMEC?c4meb4}#i`HHPaxO&3SPtSVKj@ND?Y+-@R`CDnf-d`T>vTn8RR<=@3 zNXk=Gloyh#S@3R89WHrXBHr;f(&ZO@I_Uo7;O5Bs@ecGx@7%7{_>Q`Adg&sCeZTYp ztVy{^vAUfOpTDzF*4`h%X0odWn`#uZ4s4igIV^UrVVg?c*{>K)hHq^^RxU2CM;WN> z;oK@^sg`J}BguyvilN{DQ*V+N4rD{X_~KAFj5qyk3(gP#cvSIDXe!zk3B!^InwV{j zCXGPmumQl(m`28618`K37tR+?goD{H>cAkpHyrG$XA89@o8$cOh%gGyG0e^h8y0{y z@CF+jfedLdjsO8i#eispKw=P#1_%GG3**eU%@8o?ZwNI24*pM2Xj=!6If;S;9nsX% zz(S!=&=CVoZ;TfP>*b{m(uQhlL7=)2EnN*L6sBVU)71t2^ME<-DBeCWl!etl&NwSL z*pEsj!yu5*&``}#9ZeF&7oufgU;u$?L$tLuI0%g(I+2Q@X%K^ye=Atvg0K`knTjV7 zLEDNLFH$fS4(5dVpED51|H=}B{>c+3V-OmK4AIhrZlCEl(AM_T0=zuK- zizjYd4*pHCwT0ObgQyrH7H4At2XjO;@px~TsgAA%R9|05PuEIcOUu&SOwUTs^00xK zshI`T;)sF%Z>|Li8%)3vslU12|K;lbk-Oav1Tx371&)Fb!FgLzNCeQ|r-tGG9E;W; z_5R^{|2Y=zKXM_QU?AJI{a>~IZQ?Z0_VnM@jcrt7jKN@*#$ZMzB}>VcEo(xFhBigA zRfKF&B$OpfLSqS8d&l#8dVcR8Z}0is_kGT}&h`CX>-l`{D|W}MM1o0W+qqCz&a@8xmO|M3uh;cln|6OUI z@X7fQ&dki(hqbDStcmq@R)<*FE(x{7@jPF^02^V5=v9ihMb|f1hw)0IhxkF_<1H_} z1sVWgmXE~@Wjrum=ebV>cmZ0s_CATm;a}mEc52Q5C=nO}OHAzGNx%Y4+73-pK+|sE zf&F7oVIUa*{8{JBz(BDGF#W^YNC4<9N*a&_dh_-a2?DV^K)SlsK3W zOCXnR0@miQE9D7uc?!4U4XYLag5q!qVkYiDSh|^JD*)2x1yFk>+xS2jzFcTm?NE^$ zEusR=1Jt#ow51*G(vhl2c`F}0KRYy{Jo3{2p&4FwzqpssC^#!EQ$-Rz!G~$z2>|jd zoi8@^jT0uuM~BC~Cj2=+8uB*%W~pE!<+;Jls%yObfcUWvPM_P@SPvhqk>^2RtzXee zpw9{L8C-GI=@-g9A^bLEC5ENHZn8J$mR*yf;vV50J7!cpZdF6S#2Ee38Kw@!gf4MU zH~T|ofioE<=_Pgf;Tvc0l%P^<+(Zk%8H}<#p|aT+abY8Ff9Htq!&92lSLbk7D(t{E zjjU(bM04fllo5%^3-CFm)D5AeU=e^FXGmfr{&k_>d3a+)aa}=xN$7&sHTfNh zfVj6VoV5%9Nwq8SCK^0ITUx;v0I2%9`_$cJSLF_4$)r9^g5d7-;)ha7k^2JBT`QGyenmoI!B!BgFZa^nPSIjjmHP5e8zHBct z>}g(M=h3f$4B-6LI6_z_Ow{YzNBpU4Q5No3aPn%6GK4Xlo>ROYK@oQ-NLryT2hS1Q z#~TwSIW2hlviM8?O9=^9I1CPTS9MyYOrlcISt$H6?B!qJq`S6dsv#09^-K@M!vvfq zTkX5@UgaFs(|?Idx+S6ai8fy!JtnNIngF-nVeN7Z`Pkld>>sQwike&!d8m z!q}j+#PS5O1l#Lt&96qwr4S9#BN(B)eb|Czi6eSM<1zl*H{oXKxy8rZigMly7Dpp) zp0Fn82H8REqlzST12a_HGG$OL1zP#tZ!<{Vq-7t-B%@O3Q}|wsw6|$peqXmwPE3aX z2;M0YDH7g@_E4AelRGO{xVu~ql8(6}@GdRA$pQKSu8{71L+l3C5qDtez&Yu}Hxem` z6sMHXl!;;o#{fs;ZdUOQhkK4<_f9*Vzhmk6*zQY_(0iGC-9?Iy&x;P0wqt{_@pc`@ z-STVPHZH9aL>@&(Sms8e^BoA~ujOKuWnROHb2zgex)a}&rr!-4kCTs9rZGVRYYIV- zvlx3+K(QCwE72=^{7f5<=%`? zl>Nr(;dCk;g6aw$Opx=3=@VvK69`}ZZjdTEXD<)m-PPh#nON_W-)WuySB2X5DDN+N zOj#o@Hg%5&TlX_@z|RoxL4x-e)E6|2*6eRf_RH|9>@0i7Xl-rM9ANjdo2TOpy0iRp z@HHQ+`qyJ4Zd+tE9Emv?)0oNb81R+irnMuZ>Qj# zxib@y+4A&mNoGlXP$qd$YD6l2f7kv+drBW{dVN}WI%9gX}>;*m9J4X{*B+`P?WbMg?R|_dOLt0YC zJHiM_Ty3A^GkR^rdo$!_RLz|l@F22ACA23r zJ#_ne&f4MCmW}wIwZp7=nYm*E?mRDe#(1hP%3plU=f|hSpU!`KyPiO-!1Ha8okr4T zJB37Cl;}y+I@x)J6@t!yw`NAC^c%r!=@Sa8&{j3f-kx1?ksX4A;-S<#E11dFr-IQ# zR{qfyN+h{-*_HEB`wzg2wZ9!NvuB)PENk|#M_tyutK;V4i>^I8-0%C89^}pT^~d@X zrZX$TDvB#EGNXQ4%%w>%B=-r;Tp6wJtw&z@62Lp*pP`dAn&FVjAe4>`?UC_VILOQnvfFm7kYb}KIe$4b!q%cDFE;P^!}5wFhS$flol=(c zKOH`gTJ?#vwG4c%BV>!!U?s|3f2Oiv<7D3Rncea6%ttMQ=SEEn7*BSKM z{I;U9VyY&6%QWwRxn-WhQPHJ&t+6%>}7+sVXoLpPbO)$>wJq(%cIl{yAd4L zao(3TFdv5v@49^(rE$qwH>D`KxrI{ti`zebVW|0ofEcHjRC^^ydT1 zit!QWV{YB&7Fp!JzRyR>-^@&*rwXPh>}8kQ`$wvMO}pPl&We;M%*Bo=xRH;1X50$# zU5slhYkSkir-#>@IobM@-9LZpVE$4__664#r;U<(Fif+aek4~_5ISPczF+n%G&YJPZd_dwhcM)XK$a~zGT6f@?}u{2kzI_J`y5h z5613ABWPopVbs3NnT+5kv=awJUz(1+_-pXaxwBvFzTRqoHSnr!F#SULqTm#orO}0` z4PcuJ1W{iBF zKEPVWtf%|A9(S$wMs?&E%QC)W%H5Wm7d}tKyUte8et?%f`c=!1mLN-!R-v?wVf6iz z)G6X}%Z#&ODdUID)ZtFfy9=wnb=?6Uetyt)y~(QPyq;Dlr>K3}Q=wY9_%mo}MmAXZ zJ7&N&B%XPHy{2#D+xAtlZx_lo9}?@xLqFZ?+&f;mh;c-PqH;Eqf4z$u?y_pN>Q=E- ziH*-zQc@6+ub%g8PZ}Rf89BiysN>^Vu*|b~eTqQIXzO`L8nmD()4q3juuoh;Z zx{Lc)DaWwDG3=>cj9@&S2$*_OJ%}J{GTxhrCE`61Z>_G%gwd42_vIJi(910C^C-NfacQ^Sl-eB6%Xg&U!Xb8ybq}LqdnpiS{AK90(zP z1Ord7u@T6SiQp2Di3~i5N%p4%Aecz--@FL!dP@uegZ@@w_#wgnaSCT+2SQQlM9?8^ zm=*yFg@O(lXcIm0a1R|XJV6r#hr(eH8234(1v`X*>mXnTpnnFKYmn~gg}|Cy{$q~2 zLxO!63>pFg2@Vd{4%X48(!C)t0|NsH6b^yIwYVBu0W1mw&(xv>sQhLyCk7DcBpQQ6 zrGT~=@gCGb1`^D5_CHaOY5&qv0{+PqH)jwgo(6$wL${*(t!QKO|ErS8|7r&?u*CoR z`+pJ#IIw6$2$mQ?4WtvewewQhGDSn6=tMk&N_U`A{eLIY&WFmN2KZ2EAh?b;45V&@ zCy*#xlKp=}Y-|wLlmG^vLLge3Bf(q}Z4${7VPJ`Z>caJO59#RW!C)3BeO)*VWoc## zg<9yK4D<|sW6i0AKr)fS_>J}aFIMl5*sX>j)3}z+iF8sB(bJMnC4>Hs8bSKAFYrI| z{e$)VvoAV-#6q~vK(=c8ziRzk#BHFh<-g6#-Td4BL<+a(>D=bN76lY@FUB@IjDy9m z(5*YN-4s*8oj}&+rVh+L4|neH1o$j1E!71)pl~xe=$Un0lQ15DzW@MOBBhHB}+m>LbCLY=Y4wK?~kwVKJMeb&hxs?-|t+n40QpA+b4G8*k_>A)gsvzul2%)`{+ zGXO-B3u=_{$d$PU5YEZSn%Bo%6nB$X*pi8HtvlN(j>)<>oU^ms-{SJc!?CVM_kGpq zD|mb=fG|Jac@dmEE>EYKyFP!dPw~V2q0~L3V4zJ7VgZs-lDyFoU9CnK9lA z{|)s3FeAcdMKT|ltq9$x0m1;iQ-6nS!_cqj3MXxM0Gt2}LS)A!gg7{$QQxIe9%xhs z9ymYp6$g?4Aeep95(3@bioPky5s{%vM(c>C~+;D?q3rCl<9Vk3~u)C^5I%(w`)RT2PH zm)f7N?K9(ykBtnC`Hctjzt`uk1dC{xK3DmG+T--QM)Dliz9M@cHh&jC)x2t{F@ZnKih0C+}OXW@w z`v&$?T!Pj1rsQGSiPMN#jg(cf#BeEqd)~3u;mM}Qyx`i%uR_AH()f-rz&vtJ?~1BK z0wCjWh+r=QKw`~Oyt$4L(2|<}2>>cTD<8d+q=bD10syO=GrJ#HY?6E~&#jfte6C(u zt0YX=Xk{+Bqt-;ma^pzUR`Hw4DHbX&wa9MK#}7nQbGD=p$&@~a?~@uIls$T8lCHGT zTRHoMa^-n3QHw^99AP{1;ufE{Zb&OgDJ@PELckbai^>O2T$Dcqsc&TD3l~}jCU{~r zzv(gLjjtXx|H*H&$^=ebjw433!=?SMd>|aXa>3gB5?)oiL6JC$H*$+NBC6x}hAF7kW)t|J z9m26ua#NsV=VV?4pXG3D@mM_ij@FcBscZ$vT`c+>{Ka38#5<0qS`o5Kbu1s`Lk`}C ztNnHRw(Z$k$NrL*^Gd|*kZ!s*;vl|Vi-WL}unWTUV)XKz^G!Qs$eCE}Ne-py;|QoE ziVIFnDC2DAI9^+BdO1=ikF38qj1|k>fy+;lJzzvK8x_5E17Vq#bN5h7VfH)F-HXT@ zhwUgiVNOuz3x#rqq3K#J8H#9LzFuDEn{={2c`*Pw!K@JLkKSgT`X;p_=<}wD@rmf~ z;gVA4rJ@@!K08%{R8FWAD3_@~)3CQUyiHAObb-A`sHOQ|-+Z0sir>Ak`=mm`YuRLE zvRiUw^7vgB*AQ2;PWD|1mwT?8?;UeHb=$`Ek<+I_v3H91It$fZpB3&YZpDS;;+@(K zdF54mt)Bf!lqxwNW0P|pljlM#d!=%9yW%SZX%=tU#c&gu)D60B?{lPNX$l**VOcE< zdIIZ=4!P^c^-J)}8av)1B>n2);EeHy%mc04Tcui0=!xi=={@WUEb=RgEZW->(No>y zGtHP*oSy9AhtjjmvvjlOkrd=&s943GibEAK6}_QtUrgT;C)pEX^RMTnC;HoM=PBRw z=9RwiyZG%Idtrv4Jsg!__&(xHGl%#&=sLN)edgTIoh`h8iiEm=ymq_1zsj}0Uhw~9 z#8NW#s4ujm8iU4JvG{?xr?d;JWxCeN2BzQy;MMf~vb=1*A#83ixqIOEV` zVaGg#~3WwEx!kV?Q+q$;Ioo@pT$VAd^FJUK|pMWk7 z+6G@N*C4B;DJ`9n-?bZYSO3eQQfKCI=Av#Fcf@1azbbAvzVOP^{k?%t7-9b0z+hZ3 zaVn!cs{C&G8PM z+2JN0Mjo7#`(m!krk0qEMuRP#pvsP;1yp-=xo_t(VjQijbFbzedRSI|z~tIkmRs_| zzW)8E&_4stJKBW4G7xjb>97-2u07S9vv;%V`p9kjaQuUwaZ+YdW*$z8oKmXu9#*!q z%+XIrCsAsIJw|!0mU!Xy;)v!_$Xu^Na16FRuM}78B&~>r-qB$lQ9i;d$5deszcU!{ zTl=!4DREZuWEJOuQ~85O-Q_Hg*+EE+^)p4ySZAeheYhvC!k0y!={Us;;FYATIt}A- zuHORLec$46(H*yLp>@u>8zvVfHSws$-w!_}DiD%=UHO5jok!eG?^a6o;?lWyihn$? zDIXhlckt>wInSo_^n5%}_Ii2}Gnqe0E+&@qiXwmuR{ESqQ+U(U)H80A6kIb79 zf%9=Kr7f>pM2rYV(?^=0aC^Vq+>^Huk#*XW=eAmOudMomc28GLfB11cI@{U7;B zQ-8QzAye z?YX)QgQSmUMA3ROrqjb8(+}^Keqk~C{I7xACr^BG`h2tXW#7w|fwa?Q^Pou#Tc-nA z6Ux=gqvW7&R`EYy$;(ndrfyqZ_A8PP|3nOJFp782&dJ(|nq3+>oA{}~w;(&q!3^~- zt&hEkT}cb_JmgvBk8aC0Q(}I_mU%5U&3zn?_nfJue}^pk^lFtIEJ78dY$NHbLzw$V zXp^Kx-n6?(G4s3qJ66M%C`$TCPDSu}Lmjrwww;{p%X+9*d9fjae!jTBR?Bh)&695p|Np`_A@%C6Gkw(!c ztlQ|bD0BfD08GqSbOJGm#02}0{K-@lg#WAt0w(*SAnr!?Fncs1cZ-)AAzU~M!*noC|vOF)r0RvA`FmlWAHx@MBtF&>xaZy+5F>9 zprIfEOeP%(g@%WR>xUcY(-{6xxUsP@6o!Bz5PAX&y%08)Nnq(wLo|OgSdl`A3^JWb zrcuG`j07KAC=&${1pA*XDD;16sUiPVN>DQ>i$I6M^|Nl)Xlz**5m^jjZ zpQ#thS=L9?WiG40+mRzvqC`xB>H5sFVffs4KqX-!S)&$7{TGz=zWF=INHY2 z0tT}-KpPtw|HfL;h@lh`mH8X%`(G^lkJ$BrpwI=Ltw;=V7|GX$L8E~G&KgPnV=RW& zf8_fI>-)!83~m01g$ja!uJ`tT_4@agV1U-ee}`9~{5$?6s$k|Bg5ln!QST+V7#p3i zF4n&y*YC(C3v7{K(X_L&aAEcMczb*MMhV&2h)M`^tW<_XOB8+kL0OWLfY3%j)E-d2 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 7afd3fad83..9070e6363f 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -22,7 +22,7 @@ - [x] Rewrite `gui_const` logic - [ ] Separate protocol name from `fuzzer_proto_items` - [x] Icon in dialog - - [ ] Description and buttons in `field_editor` view + - [x] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` - [x] prototype - [x] UID diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index ad99f0132f..13e2325fd7 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -18,7 +18,7 @@ typedef struct { const char* attack_name; const char* protocol_name; FuzzerAttackState attack_state; - char* uid; + FuriString* uid_str; } FuzzerViewAttackModel; void fuzzer_view_attack_reset_data( @@ -34,38 +34,25 @@ void fuzzer_view_attack_reset_data( model->attack_name = attack_name; model->protocol_name = protocol_name; model->attack_state = FuzzerAttackStateIdle; - strcpy(model->uid, "Not_set"); + furi_string_set_str(model->uid_str, "Not_set"); }, true); } void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid) { furi_assert(view); - - // TODO fix it - uint8_t* data = malloc(uid.data_size); - memcpy(data, uid.data, uid.data_size); + furi_assert(uid.data); with_view_model( view->view, FuzzerViewAttackModel * model, { - snprintf( - model->uid, - uid.data_size * 3, - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", - data[0], - data[1], - data[2], - data[3], - data[4], - data[5], - data[6], - data[7]); + furi_string_printf(model->uid_str, "%02X", uid.data[0]); + for(uint8_t i = 1; i < uid.data_size; i++) { + furi_string_cat_printf(model->uid_str, ":%02X", uid.data[i]); + } }, true); - - free(data); } void fuzzer_view_attack_start(FuzzerViewAttack* view) { @@ -133,10 +120,11 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name); canvas_set_font(canvas, FontPrimary); - if(128 < canvas_string_width(canvas, model->uid)) { + if(128 < canvas_string_width(canvas, furi_string_get_cstr(model->uid_str))) { canvas_set_font(canvas, FontSecondary); } - canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, model->uid); + canvas_draw_str_aligned( + canvas, 64, 38, AlignCenter, AlignTop, furi_string_get_cstr(model->uid_str)); canvas_set_font(canvas, FontSecondary); if(model->attack_state == FuzzerAttackStateRunning) { @@ -245,10 +233,11 @@ FuzzerViewAttack* fuzzer_view_attack_alloc() { FuzzerViewAttackModel * model, { model->time_delay = FUZZ_TIME_DELAY_MIN; - model->uid = malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); + model->uid_str = furi_string_alloc_set_str("Not_set"); + // malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); model->attack_state = FuzzerAttackStateOff; - strcpy(model->uid, "Not_set"); + // strcpy(model->uid_str, "Not_set"); model->attack_name = "Not_set"; model->protocol_name = "Not_set"; }, @@ -260,7 +249,10 @@ void fuzzer_view_attack_free(FuzzerViewAttack* view_attack) { furi_assert(view_attack); with_view_model( - view_attack->view, FuzzerViewAttackModel * model, { free(model->uid); }, true); + view_attack->view, + FuzzerViewAttackModel * model, + { furi_string_free(model->uid_str); }, + true); view_free(view_attack->view); free(view_attack); } diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index 53e15e152f..07a19ae0e3 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -5,8 +5,21 @@ #include #include +#define FIELD_EDITOR_V2 + +#define GUI_DISPLAY_WIDTH 128 +#define GUI_DISPLAY_HEIGHT 64 + +#define GUI_DISPLAY_HORIZONTAL_CENTER 64 +#define GUI_DISPLAY_VERTICAL_CENTER 32 + #define UID_STR_LENGTH 25 + +#ifdef FIELD_EDITOR_V2 +#define EDITOR_STRING_Y 38 +#else #define EDITOR_STRING_Y 50 +#endif struct FuzzerViewFieldEditor { View* view; @@ -14,7 +27,6 @@ struct FuzzerViewFieldEditor { void* context; }; -// TODO model typedef struct { uint8_t* uid; uint8_t uid_size; @@ -72,15 +84,55 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); +#ifdef FIELD_EDITOR_V2 + canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignTop, "Left and right: select byte"); - canvas_draw_str_aligned(canvas, 64, 15, AlignCenter, AlignTop, "Up and down: adjust byte"); + + canvas_draw_icon(canvas, 2, 4, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 8, 4, &I_ButtonRight_4x7); + + canvas_draw_icon_ex(canvas, 62, 3, &I_Pin_arrow_up_7x9, IconRotation180); + canvas_draw_icon(canvas, 69, 3, &I_Pin_arrow_up_7x9); + + canvas_draw_str(canvas, 14, 10, "select byte"); + canvas_draw_str(canvas, 79, 10, "adjust byte"); char msg_index[18]; canvas_set_font(canvas, FontPrimary); snprintf(msg_index, sizeof(msg_index), "Field index : %d", model->index); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); + canvas_draw_str_aligned( + canvas, GUI_DISPLAY_HORIZONTAL_CENTER, 24, AlignCenter, AlignBottom, msg_index); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 4, 52, &I_Pin_back_arrow_10x8); + canvas_draw_icon(canvas, 85, 52, &I_Ok_btn_9x9); + + canvas_draw_str(canvas, 16, 60, "Back"); + canvas_draw_str(canvas, 96, 60, "Attack"); +#else + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER, + 5, + AlignCenter, + AlignTop, + "Left and right: select byte"); + canvas_draw_str_aligned( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER, + 15, + AlignCenter, + AlignTop, + "Up and down: adjust byte"); + + char msg_index[18]; + canvas_set_font(canvas, FontPrimary); + snprintf(msg_index, sizeof(msg_index), "Field index : %d", model->index); + canvas_draw_str_aligned( + canvas, GUI_DISPLAY_HORIZONTAL_CENTER, 28, AlignCenter, AlignTop, msg_index); +#endif // ####### Editor ####### FuriString* temp_s = model->uid_str; canvas_set_font(canvas, FontSecondary); @@ -88,7 +140,7 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m furi_string_reset(temp_s); for(int i = -3; i != 0; i++) { if(0 <= (model->index + i)) { - furi_string_cat_printf(temp_s, "%2X ", model->uid[model->index + i]); + furi_string_cat_printf(temp_s, "%02X ", model->uid[model->index + i]); } } canvas_draw_str_aligned( @@ -97,7 +149,7 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m furi_string_reset(temp_s); for(int i = 1; i != 4; i++) { if((model->index + i) < model->uid_size) { - furi_string_cat_printf(temp_s, " %2X", model->uid[model->index + i]); + furi_string_cat_printf(temp_s, " %02X", model->uid[model->index + i]); } } canvas_draw_str_aligned( @@ -108,16 +160,31 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m furi_string_reset(temp_s); furi_string_cat_printf(temp_s, "<%02X>", model->uid[model->index]); canvas_draw_str_aligned( - canvas, 64, EDITOR_STRING_Y, AlignCenter, AlignBottom, furi_string_get_cstr(temp_s)); + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER, + EDITOR_STRING_Y, + AlignCenter, + AlignBottom, + furi_string_get_cstr(temp_s)); uint16_t w = canvas_string_width(canvas, furi_string_get_cstr(temp_s)); w -= 11; // '<' & '>' w /= 2; if(model->lo) { - canvas_draw_line(canvas, 64 + 1, EDITOR_STRING_Y + 2, 64 + w, EDITOR_STRING_Y + 2); + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER + 1, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER + w, + EDITOR_STRING_Y + 2); } else { - canvas_draw_line(canvas, 64 - w, EDITOR_STRING_Y + 2, 64 - 1, EDITOR_STRING_Y + 2); + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER - w, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER - 1, + EDITOR_STRING_Y + 2); } // ####### Editor ####### } @@ -211,29 +278,6 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { void fuzzer_view_field_editor_enter(void* context) { furi_assert(context); - // TODO delete only for debug - // FuzzerViewFieldEditor* view_edit = context; - // uint8_t temp[8] = { - // 0x12, - // 0x34, - // 0x56, - // 0x78, - // 0x90, - // 0xAB, - // 0xCD, - // 0xEF, - // }; - // with_view_model( - // view_edit->view, - // FuzzerViewFieldEditorModel * model, - // { - // memcpy(model->uid, &temp, 8); - - // // memset(model->uid, 0xCC, 8); - // model->index = 0; - // model->uid_size = 8; - // }, - // true); } void fuzzer_view_field_editor_exit(void* context) { diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/pacs_fuzzer/views/main_menu.c index 49b0a0d9ca..14422145b7 100644 --- a/applications/external/pacs_fuzzer/views/main_menu.c +++ b/applications/external/pacs_fuzzer/views/main_menu.c @@ -14,7 +14,6 @@ struct FuzzerViewMain { void* context; }; -// TODO Furi string for procol name typedef struct { uint8_t proto_index; uint8_t menu_index; From a0638588428301fb6e6f4b07b942210d35750cb0 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 13:03:36 +0300 Subject: [PATCH 056/102] Fuzzer App: notifications --- applications/external/pacs_fuzzer/fuzzer.c | 15 +++ applications/external/pacs_fuzzer/fuzzer_i.h | 7 +- .../pacs_fuzzer/helpers/fuzzer_custom_event.h | 3 +- .../pacs_fuzzer/helpers/fuzzer_types.h | 2 + .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 101 ++++++++++++------ .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 33 ++++++ applications/external/pacs_fuzzer/todo.md | 16 +-- 7 files changed, 137 insertions(+), 40 deletions(-) diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index 0a9aa3f7d7..c80c18130a 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -35,9 +35,16 @@ PacsFuzzerApp* fuzzer_app_alloc() { // Dialog app->dialogs = furi_record_open(RECORD_DIALOGS); + // Open Notification record + app->notifications = furi_record_open(RECORD_NOTIFICATION); + // View Dispatcher app->view_dispatcher = view_dispatcher_alloc(); + // Popup + app->popup = popup_alloc(); + view_dispatcher_add_view(app->view_dispatcher, FuzzerViewIDPopup, popup_get_view(app->popup)); + // Main view app->main_view = fuzzer_view_main_alloc(); view_dispatcher_add_view( @@ -88,6 +95,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) { view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDFieldEditor); fuzzer_view_field_editor_free(app->field_editor_view); + // Popup + view_dispatcher_remove_view(app->view_dispatcher, FuzzerViewIDPopup); + popup_free(app->popup); + scene_manager_free(app->scene_manager); view_dispatcher_free(app->view_dispatcher); @@ -97,6 +108,10 @@ void fuzzer_app_free(PacsFuzzerApp* app) { // Close records furi_record_close(RECORD_GUI); + // Notifications + furi_record_close(RECORD_NOTIFICATION); + app->notifications = NULL; + furi_string_free(app->file_path); fuzzer_worker_free(app->worker); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 2f24ec4311..1dad1608a8 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -3,11 +3,13 @@ #include #include -#include #include #include #include +#include + #include +#include #include "scenes/fuzzer_scene.h" #include "views/main_menu.h" @@ -33,9 +35,12 @@ typedef struct { typedef struct { Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; SceneManager* scene_manager; + Popup* popup; DialogsApp* dialogs; FuzzerViewMain* main_view; FuzzerViewAttack* attack_view; diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h index 930029d3c4..3211877226 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h @@ -5,10 +5,11 @@ typedef enum { // FuzzerCustomEvent FuzzerCustomEventViewMainBack = 100, FuzzerCustomEventViewMainOk, + FuzzerCustomEventViewMainPopupErr, FuzzerCustomEventViewAttackBack, FuzzerCustomEventViewAttackOk, - FuzzerCustomEventViewAttackTick, + // FuzzerCustomEventViewAttackTick, // now not use FuzzerCustomEventViewAttackEnd, FuzzerCustomEventViewFieldEditorBack, diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index 259fc2b52d..e4661ed7bb 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -16,6 +16,8 @@ typedef enum { } FuzzerAttackState; typedef enum { + FuzzerViewIDPopup, + FuzzerViewIDMain, FuzzerViewIDAttack, FuzzerViewIDFieldEditor, diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index 61fa842618..a0bd0e2d36 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -3,10 +3,65 @@ // TODO simlify callbacks and attack state +const NotificationSequence sequence_one_green_50_on_blink_blue = { + &message_red_255, + &message_delay_50, + &message_red_0, + &message_blink_start_10, + &message_blink_set_color_blue, + &message_do_not_reset, + NULL, +}; + +static void fuzzer_scene_attack_update_uid(PacsFuzzerApp* app) { + furi_assert(app); + furi_assert(app->worker); + furi_assert(app->attack_view); + + FuzzerPayload uid; + fuzzer_worker_get_current_key(app->worker, &uid); + + fuzzer_view_attack_set_uid(app->attack_view, uid); + + free(uid.data); +} + +static void fuzzer_scene_attack_set_state(PacsFuzzerApp* app, FuzzerAttackState state) { + furi_assert(app); + + scene_manager_set_scene_state(app->scene_manager, FuzzerSceneAttack, state); + switch(state) { + case FuzzerAttackStateIdle: + notification_message(app->notifications, &sequence_blink_stop); + fuzzer_view_attack_pause(app->attack_view); + break; + + case FuzzerAttackStateRunning: + notification_message(app->notifications, &sequence_blink_start_blue); + fuzzer_view_attack_start(app->attack_view); + break; + + case FuzzerAttackStateEnd: + notification_message(app->notifications, &sequence_blink_stop); + notification_message(app->notifications, &sequence_single_vibro); + fuzzer_view_attack_end(app->attack_view); + break; + + case FuzzerAttackStateOff: + notification_message(app->notifications, &sequence_blink_stop); + fuzzer_view_attack_stop(app->attack_view); + break; + } +} + void fuzzer_scene_attack_worker_tick_callback(void* context) { furi_assert(context); PacsFuzzerApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackTick); + + notification_message(app->notifications, &sequence_one_green_50_on_blink_blue); + fuzzer_scene_attack_update_uid(app); + + // view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewAttackTick); } void fuzzer_scene_attack_worker_end_callback(void* context) { @@ -21,19 +76,6 @@ void fuzzer_scene_attack_callback(FuzzerCustomEvent event, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, event); } -static void fuzzer_scene_attack_update_uid(PacsFuzzerApp* app) { - furi_assert(app); - furi_assert(app->worker); - furi_assert(app->attack_view); - - FuzzerPayload uid; - fuzzer_worker_get_current_key(app->worker, &uid); - - fuzzer_view_attack_set_uid(app->attack_view, uid); - - free(uid.data); -} - void fuzzer_scene_attack_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -68,15 +110,13 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { FuzzerAttackStateRunning) { // Pause if attack running fuzzer_worker_pause(app->worker); - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); - fuzzer_view_attack_pause(app->attack_view); + + fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle); } else { // Exit fuzzer_worker_stop(app->worker); - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateOff); - fuzzer_view_attack_stop(app->attack_view); + + fuzzer_scene_attack_set_state(app, FuzzerAttackStateOff); if(!scene_manager_previous_scene(app->scene_manager)) { scene_manager_stop(app->scene_manager); view_dispatcher_stop(app->view_dispatcher); @@ -89,28 +129,23 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { // Start or Continue Attack if(fuzzer_worker_start( app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateRunning); - fuzzer_view_attack_start(app->attack_view); + fuzzer_scene_attack_set_state(app, FuzzerAttackStateRunning); } else { // Error? } } else if( scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == FuzzerAttackStateRunning) { - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateIdle); - fuzzer_view_attack_pause(app->attack_view); + // Pause if attack running fuzzer_worker_pause(app->worker); // XXX + + fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle); } consumed = true; - } else if(event.event == FuzzerCustomEventViewAttackTick) { - fuzzer_scene_attack_update_uid(app); - consumed = true; + // } else if(event.event == FuzzerCustomEventViewAttackTick) { + // consumed = true; } else if(event.event == FuzzerCustomEventViewAttackEnd) { - scene_manager_set_scene_state( - app->scene_manager, FuzzerSceneAttack, FuzzerAttackStateEnd); - fuzzer_view_attack_end(app->attack_view); + fuzzer_scene_attack_set_state(app, FuzzerAttackStateEnd); consumed = true; } } @@ -122,6 +157,8 @@ void fuzzer_scene_attack_on_exit(void* context) { furi_assert(context); PacsFuzzerApp* app = context; + // fuzzer_worker_stop(); // XXX + fuzzer_worker_set_uid_chaged_callback(app->worker, NULL, NULL); fuzzer_worker_set_end_callback(app->worker, NULL, NULL); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 6990374303..cfa43ad87b 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -9,6 +9,12 @@ void fuzzer_scene_main_callback(FuzzerCustomEvent event, void* context) { view_dispatcher_send_custom_event(app->view_dispatcher, event); } +void fuzzer_scene_main_error_popup_callback(void* context) { + PacsFuzzerApp* app = context; + notification_message(app->notifications, &sequence_reset_rgb); + view_dispatcher_send_custom_event(app->view_dispatcher, FuzzerCustomEventViewMainPopupErr); +} + static bool fuzzer_scene_main_load_custom_dict(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -49,6 +55,15 @@ static bool fuzzer_scene_main_load_key(void* context) { return res; } +static void fuzzer_scene_main_show_error(void* context, const char* erre_str) { + furi_assert(context); + PacsFuzzerApp* app = context; + popup_set_header(app->popup, erre_str, 64, 20, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_set_red_255); + notification_message(app->notifications, &sequence_double_vibro); + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDPopup); +} + void fuzzer_scene_main_on_enter(void* context) { furi_assert(context); PacsFuzzerApp* app = context; @@ -57,6 +72,14 @@ void fuzzer_scene_main_on_enter(void* context) { fuzzer_view_main_update_data(app->main_view, app->fuzzer_state); + // Setup view + Popup* popup = app->popup; + // popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_timeout(popup, 2500); + popup_set_context(popup, app); + popup_set_callback(popup, fuzzer_scene_main_error_popup_callback); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); } @@ -72,6 +95,9 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(app->view_dispatcher); } consumed = true; + } else if(event.event == FuzzerCustomEventViewMainPopupErr) { + view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDMain); + consumed = true; } else if(event.event == FuzzerCustomEventViewMainOk) { fuzzer_view_main_get_state(app->main_view, &app->fuzzer_state); @@ -88,9 +114,11 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { if(!loading_ok) { // error + fuzzer_scene_main_show_error(app, "Default dictionary\nis empty"); } break; case FuzzerAttackIdBFCustomerID: + // TODO uid = malloc(d_size); memset(uid, 0x00, d_size); @@ -114,6 +142,7 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); FURI_LOG_I("Scene", "Load ok"); } else { + fuzzer_scene_main_show_error(app, "Unsupported protocol\nor broken file"); FURI_LOG_W("Scene", "Load err"); } } @@ -125,6 +154,10 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { } else { loading_ok = fuzzer_worker_init_attack_file_dict( app->worker, app->fuzzer_state.proto_index, app->file_path); + if(!loading_ok) { + fuzzer_scene_main_show_error(app, "Incorrect key format\nor length"); + // error + } } break; diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 9070e6363f..98450035c0 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -5,10 +5,13 @@ - [ ] Make the "Load File" independent of the current protocol - [x] Add pause - [ ] Switching UIDs if possible -- [ ] Led and sound Notification -- [ ] Error Notification - - [ ] Custom UIDs dict loading - - [ ] Key file loading +- [x] Led and sound Notification + - [x] Led + - [x] Vibro + - [ ] Sound? +- [x] Error Notification + - [x] Custom UIDs dict loading + - [x] Key file loading - [ ] Anything else #### App functionality @@ -20,7 +23,6 @@ - [ ] GUI - [x] Rewrite `gui_const` logic - - [ ] Separate protocol name from `fuzzer_proto_items` - [x] Icon in dialog - [x] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` @@ -30,4 +32,6 @@ - [x] `UID_MAX_SIZE` - [x] Add pause - [x] Fix `Custom dict` attack when ended -- [x] this can be simplified `fuzzer_proto_items` +- [ ] Worker + - [ ] Use `prtocol_id` instead of protocol name + - [x] this can be simplified `fuzzer_proto_items` \ No newline at end of file From d2b0aa8513e9df980d6d1d94261663e5838605c9 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 15:13:50 +0300 Subject: [PATCH 057/102] Fuzzer App: fix time_delay --- applications/external/pacs_fuzzer/fuzzer_i.h | 1 - .../pacs_fuzzer/lib/worker/fake_worker.c | 59 ++++++++++++------- .../pacs_fuzzer/lib/worker/protocol.c | 4 ++ .../pacs_fuzzer/lib/worker/protocol.h | 6 ++ .../pacs_fuzzer/lib/worker/protocol_i.h | 4 ++ .../external/pacs_fuzzer/views/attack.c | 10 ++-- 6 files changed, 58 insertions(+), 26 deletions(-) diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 1dad1608a8..63bf85d242 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -22,7 +22,6 @@ #include #include "fuzzer_icons.h" -#define FUZZ_TIME_DELAY_MIN (5) #define FUZZ_TIME_DELAY_MAX (80) typedef struct { diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 9d3d89cdfc..8960883085 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -8,7 +8,6 @@ #include #define TAG "Fuzzer worker" -#define FUZZ_TIME_DELAY_DEFAULT (10) #if defined(RFID_125_PROTOCOL) @@ -39,7 +38,8 @@ struct FuzzerWorker { const FuzzerProtocol* protocol; FuzzerWorkerAttackType attack_type; - uint8_t timeer_delay; + uint8_t timer_idle_delay; + uint8_t timer_emu_delay; uint8_t payload[MAX_PAYLOAD_SIZE]; Stream* uids_stream; @@ -47,6 +47,7 @@ struct FuzzerWorker { uint8_t chusen_byte; bool treead_running; + bool in_emu_phase; FuriTimer* timer; FuzzerWorkerUidChagedCallback tick_callback; @@ -147,29 +148,35 @@ static void fuzzer_worker_on_tick_callback(void* context) { FuzzerWorker* instance = context; - if(instance->treead_running) { + if(instance->in_emu_phase) { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_stop(instance->proto_worker); + lfrfid_worker_stop(instance->proto_worker); #else - ibutton_worker_stop(instance->proto_worker); + ibutton_worker_stop(instance->proto_worker); #endif - } - - if(!fuzzer_worker_load_key(instance, true)) { - fuzzer_worker_pause(instance); // XXX - if(instance->end_callback) { - instance->end_callback(instance->end_context); } + instance->in_emu_phase = false; + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_delay * 100)); } else { - if(instance->treead_running) { + if(!fuzzer_worker_load_key(instance, true)) { + fuzzer_worker_pause(instance); // XXX + if(instance->end_callback) { + instance->end_callback(instance->end_context); + } + } else { + if(instance->treead_running) { #if defined(RFID_125_PROTOCOL) - lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); + lfrfid_worker_emulate_start(instance->proto_worker, instance->protocol_id); #else - ibutton_worker_emulate_start(instance->proto_worker, instance->key); + ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif - } - if(instance->tick_callback) { - instance->tick_callback(instance->tick_context); + } + instance->in_emu_phase = true; + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_delay * 100)); + if(instance->tick_callback) { + instance->tick_callback(instance->tick_context); + } } } } @@ -338,13 +345,15 @@ FuzzerWorker* fuzzer_worker_alloc() { instance->attack_type = FuzzerWorkerAttackTypeMax; instance->index = 0; instance->treead_running = false; + instance->in_emu_phase = false; memset(instance->payload, 0x00, sizeof(instance->payload)); - instance->timeer_delay = FUZZ_TIME_DELAY_DEFAULT; + instance->timer_idle_delay = PROTOCOL_MIN_IDLE_DELAY; + instance->timer_emu_delay = PROTOCOL_MIN_IDLE_DELAY; instance->timer = - furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypePeriodic, instance); + furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypeOnce, instance); return instance; } @@ -374,9 +383,15 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { furi_assert(instance); if(instance->attack_type < FuzzerWorkerAttackTypeMax) { - instance->timeer_delay = timer_dellay; + uint8_t temp = timer_dellay / 2; + instance->timer_emu_delay = temp; + instance->timer_idle_delay = temp + timer_dellay % 2; - furi_timer_start(instance->timer, furi_ms_to_ticks(timer_dellay * 100)); + FURI_LOG_D( + TAG, + "Emu_delay %u Idle_delay %u", + instance->timer_emu_delay, + instance->timer_idle_delay); if(!instance->treead_running) { #if defined(RFID_125_PROTOCOL) @@ -397,6 +412,8 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { // ibutton_worker_start_thread(instance->proto_worker); ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif + instance->in_emu_phase = true; + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_delay * 100)); return true; } return false; diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index c295289ae8..fb76519017 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -254,6 +254,10 @@ uint8_t fuzzer_proto_get_max_data_size() { return MAX_PAYLOAD_SIZE; } +uint8_t fuzzer_proto_get_min_delay() { + return PROTOCOL_TIME_DELAY_MIN; +} + const char* fuzzer_proto_get_menu_label(uint8_t index) { return fuzzer_menu_items[index].menu_label; } diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index 4c2c70e0c6..62ce88d5cc 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -37,6 +37,12 @@ struct FuzzerPayload { */ uint8_t fuzzer_proto_get_max_data_size(); +/** + * Get minimum time delay for protocols + * @return Minimum time delay + */ +uint8_t fuzzer_proto_get_min_delay(); + /** * Get protocol name based on its index * @param index protocol index diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h index 841784f167..793b3e043e 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -4,8 +4,12 @@ #if defined(RFID_125_PROTOCOL) #define MAX_PAYLOAD_SIZE (6) +#define PROTOCOL_MIN_IDLE_DELAY (5) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_MIN_IDLE_DELAY + 4 #else #define MAX_PAYLOAD_SIZE (8) +#define PROTOCOL_MIN_IDLE_DELAY (2) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_MIN_IDLE_DELAY + 2 #endif typedef struct ProtoDict ProtoDict; diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 13e2325fd7..1df6d5eb31 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -15,6 +15,7 @@ struct FuzzerViewAttack { typedef struct { uint8_t time_delay; + uint8_t time_delay_min; const char* attack_name; const char* protocol_name; FuzzerAttackState attack_state; @@ -157,14 +158,14 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { if(model->attack_state == FuzzerAttackStateIdle) { // TimeDelay if(event->type == InputTypeShort) { - if(model->time_delay > FUZZ_TIME_DELAY_MIN) { + if(model->time_delay > model->time_delay_min) { model->time_delay--; } } else if(event->type == InputTypeLong) { - if((model->time_delay - 10) >= FUZZ_TIME_DELAY_MIN) { + if((model->time_delay - 10) >= model->time_delay_min) { model->time_delay -= 10; } else { - model->time_delay = FUZZ_TIME_DELAY_MIN; + model->time_delay = model->time_delay_min; } } } else if( @@ -232,7 +233,8 @@ FuzzerViewAttack* fuzzer_view_attack_alloc() { view_attack->view, FuzzerViewAttackModel * model, { - model->time_delay = FUZZ_TIME_DELAY_MIN; + model->time_delay_min = fuzzer_proto_get_min_delay(); + model->time_delay = model->time_delay_min; model->uid_str = furi_string_alloc_set_str("Not_set"); // malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); model->attack_state = FuzzerAttackStateOff; From caab7c8e1092ca7f090ec828ce9857b8197c1950 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 16:55:29 +0300 Subject: [PATCH 058/102] Fuzzer App: Edit Manifests --- applications/external/pacs_fuzzer/application.fam | 14 ++++++-------- .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 2 +- applications/external/pacs_fuzzer/todo.md | 1 + 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam index 8e67af6d40..4d14e55fd9 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/pacs_fuzzer/application.fam @@ -1,6 +1,6 @@ App( - appid="pacs_fuzzer", - name="Fuzzer Gui", + appid="pacs_fuzzer_ibtn", + name="iButton Fuzzer [B]", apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_ibtn", requires=[ @@ -10,8 +10,7 @@ App( "input", "notification", ], - stack_size=2 * 1024, - order=15, + stack_size=1 * 1024, fap_icon="icons/rfid_10px.png", fap_category="Debug", fap_private_libs=[ @@ -25,8 +24,8 @@ App( ) App( - appid="pacs_rfid_fuzzer", - name="Fuzzer Gui rfid", + appid="pacs_fuzzer_rfid", + name="RFID Fuzzer [B]", apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_rfid", requires=[ @@ -36,8 +35,7 @@ App( "input", "notification", ], - stack_size=2 * 1024, - order=15, + stack_size=1 * 1024, fap_icon="icons/125_10px.png", fap_category="Debug", fap_private_libs=[ diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index a0bd0e2d36..36734495b4 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -137,7 +137,7 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == FuzzerAttackStateRunning) { // Pause if attack running - fuzzer_worker_pause(app->worker); // XXX + fuzzer_worker_pause(app->worker); fuzzer_scene_attack_set_state(app, FuzzerAttackStateIdle); } diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 98450035c0..823e2f05ab 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -27,6 +27,7 @@ - [x] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` - [x] prototype + - [ ] Add the ability to edit emulation time and downtime separately - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] `UID_MAX_SIZE` From 7c172c7c064a6afe36d71ae571df4d6e6fa4744d Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 19:23:39 +0300 Subject: [PATCH 059/102] Fuzzer App: revert stack_size --- applications/external/pacs_fuzzer/application.fam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/pacs_fuzzer/application.fam index 4d14e55fd9..6098322ac0 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/pacs_fuzzer/application.fam @@ -10,7 +10,7 @@ App( "input", "notification", ], - stack_size=1 * 1024, + stack_size=2 * 1024, fap_icon="icons/rfid_10px.png", fap_category="Debug", fap_private_libs=[ @@ -35,7 +35,7 @@ App( "input", "notification", ], - stack_size=1 * 1024, + stack_size=2 * 1024, fap_icon="icons/125_10px.png", fap_category="Debug", fap_private_libs=[ From 1e512b6add48bb8eef84962e8f71173041691ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 7 Jun 2023 02:15:50 +0900 Subject: [PATCH 060/102] [FL-3293] FuriHal: add system setting to device info, bump device info version (#2736) --- firmware/targets/f7/furi_hal/furi_hal_info.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index 47672c97a3..a2c9232c05 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -23,10 +24,10 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { // Device Info version if(sep == '.') { property_value_out(&property_context, NULL, 2, "format", "major", "3"); - property_value_out(&property_context, NULL, 2, "format", "minor", "1"); + property_value_out(&property_context, NULL, 2, "format", "minor", "2"); } else { property_value_out(&property_context, NULL, 3, "device", "info", "major", "2"); - property_value_out(&property_context, NULL, 3, "device", "info", "minor", "2"); + property_value_out(&property_context, NULL, 3, "device", "info", "minor", "3"); } // Model name @@ -297,6 +298,18 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { property_value_out(&property_context, NULL, 2, "radio", "alive", "false"); } + property_value_out( + &property_context, + "%u", + 2, + "system", + "debug", + furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)); + property_value_out( + &property_context, "%u", 3, "system", "heap", "track", furi_hal_rtc_get_heap_track_mode()); + property_value_out( + &property_context, "%u", 3, "system", "log", "level", furi_hal_rtc_get_log_level()); + property_value_out( &property_context, "%u", 3, "protobuf", "version", "major", PROTOBUF_MAJOR_VERSION); property_context.last = true; From 76c70bdf2c33437aa3d62205fb37d0a5fd66b7f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 7 Jun 2023 02:46:01 +0900 Subject: [PATCH 061/102] [FL-3316] Settings: add contrast adjustment (#2737) Co-authored-by: hedger --- .../services/notification/notification.h | 2 + .../services/notification/notification_app.c | 55 ++++++++++++------- .../services/notification/notification_app.h | 3 +- .../notification/notification_messages.c | 9 +++ .../notification/notification_messages.h | 6 ++ .../notification_settings_app.c | 44 +++++++++++++++ firmware/targets/f18/api_symbols.csv | 5 +- firmware/targets/f7/api_symbols.csv | 5 +- lib/toolbox/value_index.c | 13 +++++ lib/toolbox/value_index.h | 13 +++++ lib/u8g2/u8g2_glue.c | 20 ++++++- lib/u8g2/u8g2_glue.h | 2 + 12 files changed, 152 insertions(+), 25 deletions(-) diff --git a/applications/services/notification/notification.h b/applications/services/notification/notification.h index b38620f0f5..0e1c07e5df 100644 --- a/applications/services/notification/notification.h +++ b/applications/services/notification/notification.h @@ -75,6 +75,8 @@ typedef enum { NotificationMessageTypeForceDisplayBrightnessSetting, NotificationMessageTypeLedBrightnessSettingApply, + + NotificationMessageTypeLcdContrastUpdate, } NotificationMessageType; typedef struct { diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index f91a73f321..2f947fe8a0 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include + #include "notification.h" #include "notification_messages.h" #include "notification_app.h" @@ -20,14 +23,14 @@ static const uint8_t reset_sound_mask = 1 << 4; static const uint8_t reset_display_mask = 1 << 5; static const uint8_t reset_blink_mask = 1 << 6; -void notification_vibro_on(bool force); -void notification_vibro_off(); -void notification_sound_on(float freq, float volume, bool force); -void notification_sound_off(); +static void notification_vibro_on(bool force); +static void notification_vibro_off(); +static void notification_sound_on(float freq, float volume, bool force); +static void notification_sound_off(); -uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); +static uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app); void notification_message_save_settings(NotificationApp* app) { NotificationAppMessage m = { @@ -39,7 +42,8 @@ void notification_message_save_settings(NotificationApp* app) { }; // internal layer -void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { +static void + notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -52,7 +56,13 @@ void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t } } -bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { +static void notification_apply_lcd_contrast(NotificationApp* app) { + Gui* gui = furi_record_open(RECORD_GUI); + u8x8_d_st756x_set_contrast(&gui->canvas->fb.u8x8, app->settings.contrast); + furi_record_close(RECORD_GUI); +} + +static bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) { bool result = false; if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) || (app->led[2].index == LayerInternal)) { @@ -67,7 +77,7 @@ bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) } // notification layer -void notification_apply_notification_led_layer( +static void notification_apply_notification_led_layer( NotificationLedLayer* layer, const uint8_t layer_value) { furi_assert(layer); @@ -81,7 +91,7 @@ void notification_apply_notification_led_layer( furi_hal_light_set(layer->light, layer->value[LayerNotification]); } -void notification_reset_notification_led_layer(NotificationLedLayer* layer) { +static void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_assert(layer); furi_assert(layer->index < LayerMAX); @@ -94,7 +104,7 @@ void notification_reset_notification_led_layer(NotificationLedLayer* layer) { furi_hal_light_set(layer->light, layer->value[LayerInternal]); } -void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { +static void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) { if(reset_mask & reset_blink_mask) { furi_hal_light_blink_stop(); } @@ -130,28 +140,28 @@ uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8 return (value * app->settings.display_brightness); } -uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { +static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) { return (value * app->settings.led_brightness); } -uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { +static uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) { return ( (float)(app->settings.display_off_delay_ms) / (1000.0f / furi_kernel_get_tick_frequency())); } // generics -void notification_vibro_on(bool force) { +static void notification_vibro_on(bool force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { furi_hal_vibro_on(true); } } -void notification_vibro_off() { +static void notification_vibro_off() { furi_hal_vibro_on(false); } -void notification_sound_on(float freq, float volume, bool force) { +static void notification_sound_on(float freq, float volume, bool force) { if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode) || force) { if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { furi_hal_speaker_start(freq, volume); @@ -159,7 +169,7 @@ void notification_sound_on(float freq, float volume, bool force) { } } -void notification_sound_off() { +static void notification_sound_off() { if(furi_hal_speaker_is_mine()) { furi_hal_speaker_stop(); furi_hal_speaker_release(); @@ -174,7 +184,7 @@ static void notification_display_timer(void* ctx) { } // message processing -void notification_process_notification_message( +static void notification_process_notification_message( NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; @@ -333,6 +343,9 @@ void notification_process_notification_message( reset_mask |= reset_green_mask; reset_mask |= reset_blue_mask; break; + case NotificationMessageTypeLcdContrastUpdate: + notification_apply_lcd_contrast(app); + break; } notification_message_index++; notification_message = (*message->sequence)[notification_message_index]; @@ -361,7 +374,8 @@ void notification_process_notification_message( } } -void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { +static void + notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) { uint32_t notification_message_index = 0; const NotificationMessage* notification_message; notification_message = (*message->sequence)[notification_message_index]; @@ -548,6 +562,7 @@ int32_t notification_srv(void* p) { notification_apply_internal_led_layer(&app->led[0], 0x00); notification_apply_internal_led_layer(&app->led[1], 0x00); notification_apply_internal_led_layer(&app->led[2], 0x00); + notification_apply_lcd_contrast(app); furi_record_create(RECORD_NOTIFICATION, app); diff --git a/applications/services/notification/notification_app.h b/applications/services/notification/notification_app.h index 88194bfbd5..cacc17ffb0 100644 --- a/applications/services/notification/notification_app.h +++ b/applications/services/notification/notification_app.h @@ -32,7 +32,7 @@ typedef struct { Light light; } NotificationLedLayer; -#define NOTIFICATION_SETTINGS_VERSION 0x01 +#define NOTIFICATION_SETTINGS_VERSION 0x02 #define NOTIFICATION_SETTINGS_PATH INT_PATH(NOTIFICATION_SETTINGS_FILE_NAME) typedef struct { @@ -41,6 +41,7 @@ typedef struct { float led_brightness; float speaker_volume; uint32_t display_off_delay_ms; + int8_t contrast; bool vibro_on; } NotificationSettings; diff --git a/applications/services/notification/notification_messages.c b/applications/services/notification/notification_messages.c index 51f3533c32..28ec327c6e 100644 --- a/applications/services/notification/notification_messages.c +++ b/applications/services/notification/notification_messages.c @@ -197,6 +197,10 @@ const NotificationMessage message_force_display_brightness_setting_1f = { .data.forced_settings.display_brightness = 1.0f, }; +const NotificationMessage message_lcd_contrast_update = { + .type = NotificationMessageTypeLcdContrastUpdate, +}; + /****************************** Message sequences ******************************/ // Reset @@ -566,3 +570,8 @@ const NotificationSequence sequence_audiovisual_alert = { &message_vibro_off, NULL, }; + +const NotificationSequence sequence_lcd_contrast_update = { + &message_lcd_contrast_update, + NULL, +}; diff --git a/applications/services/notification/notification_messages.h b/applications/services/notification/notification_messages.h index 1007969176..d87cf74f4e 100644 --- a/applications/services/notification/notification_messages.h +++ b/applications/services/notification/notification_messages.h @@ -63,6 +63,9 @@ extern const NotificationMessage message_force_vibro_setting_on; extern const NotificationMessage message_force_vibro_setting_off; extern const NotificationMessage message_force_display_brightness_setting_1f; +// LCD Messages +extern const NotificationMessage message_lcd_contrast_update; + /****************************** Message sequences ******************************/ // Reset @@ -138,6 +141,9 @@ extern const NotificationSequence sequence_success; extern const NotificationSequence sequence_error; extern const NotificationSequence sequence_audiovisual_alert; +// LCD +extern const NotificationSequence sequence_lcd_contrast_update; + #ifdef __cplusplus } #endif diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index 8efbc5e084..450aaee144 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -20,6 +20,34 @@ static const NotificationSequence sequence_note_c = { NULL, }; +#define CONTRAST_COUNT 11 +const char* const contrast_text[CONTRAST_COUNT] = { + "-5", + "-4", + "-3", + "-2", + "-1", + "0", + "+1", + "+2", + "+3", + "+4", + "+5", +}; +const int32_t contrast_value[CONTRAST_COUNT] = { + -5, + -4, + -3, + -2, + -1, + 0, + 1, + 2, + 3, + 4, + 5, +}; + #define BACKLIGHT_COUNT 5 const char* const backlight_text[BACKLIGHT_COUNT] = { "0%", @@ -64,6 +92,15 @@ const char* const vibro_text[VIBRO_COUNT] = { }; const bool vibro_value[VIBRO_COUNT] = {false, true}; +static void contrast_changed(VariableItem* item) { + NotificationAppSettings* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, contrast_text[index]); + app->notification->settings.contrast = contrast_value[index]; + notification_message(app->notification, &sequence_lcd_contrast_update); +} + static void backlight_changed(VariableItem* item) { NotificationAppSettings* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -136,6 +173,13 @@ static NotificationAppSettings* alloc_settings() { VariableItem* item; uint8_t value_index; + item = variable_item_list_add( + app->variable_item_list, "LCD Contrast", CONTRAST_COUNT, contrast_changed, app); + value_index = + value_index_int32(app->notification->settings.contrast, contrast_value, CONTRAST_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, contrast_text[value_index]); + item = variable_item_list_add( app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); value_index = value_index_float( diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 3c075e0d15..f3c4e31d75 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,+,28.2,, +Version,+,28.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1999,6 +1999,7 @@ Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* @@ -2259,6 +2260,7 @@ Variable,+,message_force_vibro_setting_off,const NotificationMessage, Variable,+,message_force_vibro_setting_on,const NotificationMessage, Variable,+,message_green_0,const NotificationMessage, Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, Variable,+,message_note_a0,const NotificationMessage, Variable,+,message_note_a1,const NotificationMessage, Variable,+,message_note_a2,const NotificationMessage, @@ -2402,6 +2404,7 @@ Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, Variable,+,sequence_display_backlight_on,const NotificationSequence, Variable,+,sequence_double_vibro,const NotificationSequence, Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, Variable,+,sequence_not_charging,const NotificationSequence, Variable,+,sequence_reset_blue,const NotificationSequence, Variable,+,sequence_reset_display,const NotificationSequence, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a9ce0c26ad..a8708ce8c5 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,+,28.2,, +Version,+,28.3,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2923,6 +2923,7 @@ Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* @@ -3192,6 +3193,7 @@ Variable,+,message_force_vibro_setting_off,const NotificationMessage, Variable,+,message_force_vibro_setting_on,const NotificationMessage, Variable,+,message_green_0,const NotificationMessage, Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_lcd_contrast_update,const NotificationMessage, Variable,+,message_note_a0,const NotificationMessage, Variable,+,message_note_a1,const NotificationMessage, Variable,+,message_note_a2,const NotificationMessage, @@ -3335,6 +3337,7 @@ Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, Variable,+,sequence_display_backlight_on,const NotificationSequence, Variable,+,sequence_double_vibro,const NotificationSequence, Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_lcd_contrast_update,const NotificationSequence, Variable,+,sequence_not_charging,const NotificationSequence, Variable,+,sequence_reset_blue,const NotificationSequence, Variable,+,sequence_reset_display,const NotificationSequence, diff --git a/lib/toolbox/value_index.c b/lib/toolbox/value_index.c index e0745e4341..5ec0fb9628 100644 --- a/lib/toolbox/value_index.c +++ b/lib/toolbox/value_index.c @@ -1,5 +1,18 @@ #include "value_index.h" +uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count) { + int64_t last_value = INT64_MIN; + uint8_t index = 0; + for(uint8_t i = 0; i < values_count; i++) { + if((value >= last_value) && (value <= values[i])) { + index = i; + break; + } + last_value = values[i]; + } + return index; +} + uint8_t value_index_uint32(const uint32_t value, const uint32_t values[], uint8_t values_count) { int64_t last_value = INT64_MIN; uint8_t index = 0; diff --git a/lib/toolbox/value_index.h b/lib/toolbox/value_index.h index 9459292a75..5aa768e3d1 100644 --- a/lib/toolbox/value_index.h +++ b/lib/toolbox/value_index.h @@ -7,6 +7,19 @@ extern "C" { #endif +/** Get the index of a int32_t array element which is closest to the given value. + * + * Returned index corresponds to the first element found. + * If no suitable elements were found, the function returns 0. + * + * @param value value to be searched. + * @param values pointer to the array to perform the search in. + * @param values_count array size. + * + * @return value's index. + */ +uint8_t value_index_int32(const int32_t value, const int32_t values[], uint8_t values_count); + /** Get the index of a uint32_t array element which is closest to the given value. * * Returned index corresponds to the first element found. diff --git a/lib/u8g2/u8g2_glue.c b/lib/u8g2/u8g2_glue.c index 17a702b50f..0d4879bce5 100644 --- a/lib/u8g2/u8g2_glue.c +++ b/lib/u8g2/u8g2_glue.c @@ -2,6 +2,9 @@ #include +#define CONTRAST_ERC 32 +#define CONTRAST_MGG 31 + uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { UNUSED(u8x8); UNUSED(arg_ptr); @@ -207,6 +210,19 @@ void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio u8x8_cad_EndTransfer(u8x8); } +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset) { + uint8_t contrast = (furi_hal_version_get_hw_display() == FuriHalVersionDisplayMgg) ? + CONTRAST_MGG : + CONTRAST_ERC; + contrast += contrast_offset; + contrast = contrast & 0b00111111; + + u8x8_cad_StartTransfer(u8x8); + u8x8_cad_SendCmd(u8x8, ST756X_CMD_SET_EV); + u8x8_cad_SendArg(u8x8, contrast); + u8x8_cad_EndTransfer(u8x8); +} + uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) { /* call common procedure first and handle messages there */ if(u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) { @@ -225,7 +241,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 6 (0b110) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 31, 0b110, false); + u8x8_d_st756x_init(u8x8, CONTRAST_MGG, 0b110, false); } else { /* ERC v1(ST7565) and v2(ST7567) * EV = 33 @@ -233,7 +249,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 9.3 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.47 is 5.5 (0b101) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 32, 0b101, false); + u8x8_d_st756x_init(u8x8, CONTRAST_ERC, 0b101, false); } break; case U8X8_MSG_DISPLAY_SET_FLIP_MODE: diff --git a/lib/u8g2/u8g2_glue.h b/lib/u8g2/u8g2_glue.h index 91ba2980a5..af236279ec 100644 --- a/lib/u8g2/u8g2_glue.h +++ b/lib/u8g2/u8g2_glue.h @@ -14,3 +14,5 @@ void u8g2_Setup_st756x_flipper( u8x8_msg_cb gpio_and_delay_cb); void u8x8_d_st756x_init(u8x8_t* u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias); + +void u8x8_d_st756x_set_contrast(u8x8_t* u8x8, int8_t contrast_offset); From b0555d96e989c6b86e67d557fafde060ebe9f134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 7 Jun 2023 02:52:49 +0900 Subject: [PATCH 062/102] [FL-3213] f7: add PB9 to debug pins (#2738) Co-authored-by: hedger --- firmware/targets/f7/furi_hal/furi_hal_resources.c | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 561cef08a7..34b26b831c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -81,6 +81,7 @@ const GpioPinRecord gpio_pins[] = { /* Dangerous pins, may damage hardware */ {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, {.pin = &gpio_speaker, .name = "PB8", .debug = true}, + {.pin = &gpio_infrared_tx, .name = "PB9", .debug = true}, }; const size_t gpio_pins_count = sizeof(gpio_pins) / sizeof(GpioPinRecord); From 61394b8be4dc36c41ea860e08c4887e8a5c0d3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 7 Jun 2023 03:04:27 +0900 Subject: [PATCH 063/102] [FL-3352] Dolphin: new animation (#2735) Co-authored-by: hedger --- assets/dolphin/external/L2_Dj_128x64/frame_0.png | Bin 0 -> 1640 bytes assets/dolphin/external/L2_Dj_128x64/frame_1.png | Bin 0 -> 1687 bytes .../dolphin/external/L2_Dj_128x64/frame_10.png | Bin 0 -> 1630 bytes .../dolphin/external/L2_Dj_128x64/frame_11.png | Bin 0 -> 1660 bytes .../dolphin/external/L2_Dj_128x64/frame_12.png | Bin 0 -> 1637 bytes .../dolphin/external/L2_Dj_128x64/frame_13.png | Bin 0 -> 1654 bytes .../dolphin/external/L2_Dj_128x64/frame_14.png | Bin 0 -> 1667 bytes .../dolphin/external/L2_Dj_128x64/frame_15.png | Bin 0 -> 1344 bytes .../dolphin/external/L2_Dj_128x64/frame_16.png | Bin 0 -> 1251 bytes .../dolphin/external/L2_Dj_128x64/frame_17.png | Bin 0 -> 1292 bytes .../dolphin/external/L2_Dj_128x64/frame_18.png | Bin 0 -> 1498 bytes .../dolphin/external/L2_Dj_128x64/frame_19.png | Bin 0 -> 1530 bytes assets/dolphin/external/L2_Dj_128x64/frame_2.png | Bin 0 -> 1726 bytes .../dolphin/external/L2_Dj_128x64/frame_20.png | Bin 0 -> 1698 bytes .../dolphin/external/L2_Dj_128x64/frame_21.png | Bin 0 -> 1665 bytes .../dolphin/external/L2_Dj_128x64/frame_22.png | Bin 0 -> 1809 bytes .../dolphin/external/L2_Dj_128x64/frame_23.png | Bin 0 -> 1775 bytes .../dolphin/external/L2_Dj_128x64/frame_24.png | Bin 0 -> 1758 bytes .../dolphin/external/L2_Dj_128x64/frame_25.png | Bin 0 -> 1725 bytes .../dolphin/external/L2_Dj_128x64/frame_26.png | Bin 0 -> 1835 bytes .../dolphin/external/L2_Dj_128x64/frame_27.png | Bin 0 -> 1759 bytes .../dolphin/external/L2_Dj_128x64/frame_28.png | Bin 0 -> 1462 bytes .../dolphin/external/L2_Dj_128x64/frame_29.png | Bin 0 -> 1407 bytes assets/dolphin/external/L2_Dj_128x64/frame_3.png | Bin 0 -> 1777 bytes .../dolphin/external/L2_Dj_128x64/frame_30.png | Bin 0 -> 1408 bytes .../dolphin/external/L2_Dj_128x64/frame_31.png | Bin 0 -> 1404 bytes .../dolphin/external/L2_Dj_128x64/frame_32.png | Bin 0 -> 1327 bytes .../dolphin/external/L2_Dj_128x64/frame_33.png | Bin 0 -> 1306 bytes .../dolphin/external/L2_Dj_128x64/frame_34.png | Bin 0 -> 1341 bytes .../dolphin/external/L2_Dj_128x64/frame_35.png | Bin 0 -> 1255 bytes .../dolphin/external/L2_Dj_128x64/frame_36.png | Bin 0 -> 1059 bytes assets/dolphin/external/L2_Dj_128x64/frame_4.png | Bin 0 -> 1727 bytes assets/dolphin/external/L2_Dj_128x64/frame_5.png | Bin 0 -> 1641 bytes assets/dolphin/external/L2_Dj_128x64/frame_6.png | Bin 0 -> 1635 bytes assets/dolphin/external/L2_Dj_128x64/frame_7.png | Bin 0 -> 1588 bytes assets/dolphin/external/L2_Dj_128x64/frame_8.png | Bin 0 -> 1608 bytes assets/dolphin/external/L2_Dj_128x64/frame_9.png | Bin 0 -> 1610 bytes assets/dolphin/external/L2_Dj_128x64/meta.txt | 14 ++++++++++++++ assets/dolphin/external/manifest.txt | 7 +++++++ 39 files changed, 21 insertions(+) create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_0.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_1.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_10.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_11.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_12.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_13.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_14.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_15.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_16.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_17.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_18.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_19.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_2.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_20.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_21.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_22.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_23.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_24.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_25.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_26.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_27.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_28.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_29.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_3.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_30.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_31.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_32.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_33.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_34.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_35.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_36.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_4.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_5.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_6.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_7.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_8.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_9.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/meta.txt diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_0.png b/assets/dolphin/external/L2_Dj_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..95f72f901a5e03e63373957cbb188c9ae4673162 GIT binary patch literal 1640 zcmaJ=eNfY87!O~VNeuA$d_XjMR_@J904APtJopZ9>n2_@i7=U z7bDo5qK$XJUPju&uN5^?pmsbByVGQPN$hY<6bqD1?xCG>Ooqzsc5iND-YzkV`(MUu zy+r^u)WlR3N~EUfj2LD#=ya%2uU9Bh zW1=F3L=`@4y2B;X4uf5Kfw#kBWmel>Bf*-eP~sNUg5V^jLI@Q}A{L4z5lWGWsgxvv zlZ;h5foESS-*~G63rNH1Q==wv5?9fj0#Tuu0wI+Ij*tw6A_QqAX(dfqX^TQC2NLDp z&UvMid?wJI-tEiT03R=>i*taH62R;jKKImvxto-^G}{Q)(b3Vbd+KgLkUuD{F5A;P zl-J?7g@(qAj(+>#&CXml|0Zr_S82ZHZeDvy*Ju;jJ-hsezA0>E#l3uYha5U^Z>Hrj;ykkbVy( zL46TIbF*1n{J{pT?v6xo9<)U43Tx~)`z5sFVb6jaPj47mAKvhutpZ*b7P-a!N8;9@ z3ro9Gg5bIvmscG*Qq}j>>KVNk`=Zg*T_+_c-ONz+{$JhD?ojH!F6!FjbH_(KGvo4T zNzE+3jDH7L?fGc0wxi?flfyyRHyoX}sd35a<>mwOs(`D<6810nBs+(le86u<`ih#^ zy}u>s^$RmR4?>Tb!eblzdgovIYz}*P@xhY*>qgA)p<$=CdWoYxzNapsLOtW%ntDsQ z4(XcR_@lDhoOz~JcYDjDsQpb_D|=EW^|a>Zb?vTdi!SxwV{T1quXIbck(b-2s<>lA z=A4kxkjeloaz{{gXXn+Y0edb!OMh$%zf~Jw|8dQ2`@z*mMF^XA;pRVs=Vx#4^z56% zZH|gqDAqKcY&;qg@kMJ$LtJNE;i=`Vk5=CdHl3_0S3S)2d> literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_1.png b/assets/dolphin/external/L2_Dj_128x64/frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..32e13541d83eb0571414851957a4b8a5647a8c03 GIT binary patch literal 1687 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 F(|@~CN2UM( literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_11.png b/assets/dolphin/external/L2_Dj_128x64/frame_11.png new file mode 100644 index 0000000000000000000000000000000000000000..eca4a1296ee3dedf9ebd3b010c7be8763eaaa44d GIT binary patch literal 1660 zcmaJ?eM}Q)7{5{wsDfm1laGv?g=~Cy?e%)Jca>^OTLfY&D~+Nvlk448D)z3l2egQz z&P`+lb&HF*5tVG6ZU%;lafqm>bCdXm&S7L5V{Aj6_<=^iIbm0z&OeNoyL;dFx##yh zzvnAgn{QeiC5jgT01%a%qc`)bkAE}~LjL_%#5NS?f3o1<(va*E4De$9i*@h7#onB$x(skEY^LQ)}@u&y;y8II!t8wdjN8j#z|aD)e9+(|(T zdfH98>`uZsFu3yd|`-ndX_A6&C63R}g zNCl2rQA`ODq!ouyxeSL08ID0Ti6W>>VO3Bt6}0mryh4W~a-|N<&eCSfWCp!f3mbGg zxdJw1$g?n59>nG{ZjN9`I_TTZ`<}+iUy4=fTr|P4E(^=93wA<&G0U;;V%7;Fs00O5 zj09ZYE@pshv5veQDF#%C<$5)DPfr$!W1Y1VI&SiC}zV51%cWKt6VJQ zDM|yK^IRtdMSOb(w$EmRKRlZ*n&C&v#m`RjuGTz$?q=jKu^9MuVq(I7eb27|5aP|% zYb~DMKO62ao8Vc74Sf^-H=EP3ttUd7empeZ(3|R&+hf0Q;(q`5)48`byTd&P(!KSar-7QgDb=rJ z&T`&d`M9wt0jL?BCvFSNU#0tZ>{H)9Anj>VXyYf=wq5Vog}8LpwTUB};}U-})+H7T zyCc2c9;bG~psDNE`jSGAgkRkqo!QyGaG<8BCY%ur<(!Py7+XdCH-GNfn*7VCuWO4S zW?$NYWptGfwe?u|!gQD4GPjN0FE>PQ@LxoifZAKo*1Jp5wymV%+~(7C3= zug)%Nm)_oJr5ZLj?B5mH6a|b_e7;%|e<5CDB7|^Q^al=e`ddFmJQ3vi1m(_9>yz(d z_m21ku{QINt;uwuD_q^`&-l;zS;6s_ZjuPu#^3Jhie;4 zyLPa-gK5c88&ma)<23R0+<~1tYBdc)&5FT<^wL4?>*-}9<3k^IS%z2iSJmAJsg5Z) z=-GC=_CRD(3>tlY{eqT(^ME0G`&$14Z_}mH{MDggW7cQ?+;Du^+dJH8x4jJ)Ua2{> z>~g(N{eyW!S9Mu|rl%ztC@&cbi+K=%9jh01KLkF$DySH{zaTx*GV;{|W z(mqGi4mm_>8_+bWlaD8 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_12.png b/assets/dolphin/external/L2_Dj_128x64/frame_12.png new file mode 100644 index 0000000000000000000000000000000000000000..5f92e47fdd7cdffb93e2cb52b64dd87858071c6b GIT binary patch literal 1637 zcmaJ>dr;GM98U}4iz153Ly=JC&`Fv!P1~fSyxO9@S=M1UVS3s$Noj#LAq}>8J|gGy zrW?~u-E?z$zHcWJ(Xs6We9hwyH@Tfo*ii8?<=n3w_Ddp2oF7Az&gJk~7)NuB}nY1YMnRv%f$ z6f-1UQ`N>~13=U|ucJgLvCTD5tWQOTbyOiAC!ql#J2%9UR0Sh|E~d=uH!AzSX;Fe+ z+NhkbwIMdn#CW`Omh#NJr9}>EX$3{l%G?|D~;}^&f<3u(8^i0oS1JryX=bTvALAFSW}{O2qN2UDfzlDU z3&ZsgNx29J)2IoER1-MFP#B7-buJx^&=EV&!|O~0s?nS9!h-xlwc29N&qpjKlSYSF zw3-4O(L}ITe?TDp6ch37m3$9l^)JL4OguvhEbn01%19^JJ*>b6JS+#Im)P=Yp6esi$rpIx}MbJ1zXtZj*4#!ZO zaw~`N^fT!jYE?-hg~3JG7;qFv^(3Q#^oUvm;W`Y3aEd@640q$Cj>O!gOQVD(ig37d zp6R5BNNUgU_UUX$ho{rU_@$BJrP)#7ubq|VF2+9BVUgB>fq{LUySf2D?y{Qm9igsY zx3_IRgN!ow4h-}jIOpQsRJJK?UC=?6Mvx9J;F@;a+2+2oO1 zzFu|8wr5WPm{rl>cBR_o?VslQ;wJ2+GX8Bz5(}ap-f{vhE4e9VYWf%j&fiQpdyJY` zOU$$%*xg^6bf)SndlFoK{Crc!jXgK~$GZ9)*^TqA3itBjoK&zWy7utr?zXq&2mdHJ zxxBe<)8);DE#u-x^glR%B{nZXFAt4KzbMW(T^ftZpO_NMM+YaZPEL9#-!QKd(N3|?l$+N920Vr zFP%*C{FK(bA_hDPFNyz4eOL>;GC6hA)+a3)7y5K@qDQ2c_;&$X6VUwqHlTK-%;Ou| zfQ)WaC?*2STDRRjQ@UF=ry+4_IhW8oL3Dfqsm|GNR+p#JT(`^_Q)>Mgd}BGEQg`r! zhDCXn)?hKFtqTFdw>`ZNj>NZL+dsWDkQU`!d}o1jBL$*qqDNK!5nhZ?zPej}$m- F{{s^?M>YTe literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_13.png b/assets/dolphin/external/L2_Dj_128x64/frame_13.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1017ce259d7b511bfb2d6c565cbe8350ca929f GIT binary patch literal 1654 zcmaJ?dr;GM98X(TD+i*^sccSTIXtJPP1CeZdK6j-NN*H-(8=R)Gfh$`&?cmT7Iu#6 zx%0&-6BVc898TSx;Hg99oDZhsd~ABCi0_-5!tmxD+t_?;Se62H{^6NR^85Y1`F_6N z&-Zm%ZO>W|8~t`P006Pp3?f@xgW_XW$i#P#V!cgV;)S$4A%}Gdev)SZ3(Yzi(CQ(J zm~4imtIE$aNdO>Q>2~A^dA3X=#d=g^SVtA`a3UH2l2QU3NtH4J=wyoAUX!x_+fz!= zO`DXtIvZ@`Qki0RMm5jmRA)J;>QV})l_|+!Qotx0@Gt@i20Ue6pD|!k4%;<~`>CoRG!Tarm{Q(;wl)TU|0hha1{k3FzQ5T#x)N_U_`5i5%GpJh*5(Y5fmJ~ zl%hwTb{VsY^ihA}$)qe61kR{d`~7~EU#nvIBDDs`@vsd9fkX|+SLGGR0Oa*0L@W@D zkK)~&;AXvG*dpmmSS)Z_YN}Qb zr|GowF<2YHTD?Aj^ioX3w_Ef*iq*dsYfR-CQeb%p%a%nt!CuS?tgo2mKm=8x;A|U7 zxxHb@d*LdL1Who!yON>PdDa6Cr`hO!1qGK*t4Ex$6GAZD0HFp9gPb^xLAX|@G3YTA z!6=t<6i>gDzL8cHC1My{gpCnHFvLJITF3xvv=F985eTDj7(y`@M(Rn_MLM-gwMbDN z?wpr8DIyZvGrWB<8{*-`bTM9Wq ztw&pOz2Czz1pefd5QH{lj9a@_|D7_VI224w&N(ydvp>vR>5|1K zPA)D6LQyN|o{oI!{$+p9>*{yNexKO+bO$#6cz(h~cv^Ew5882L_OS=V(n-Lf-uP9a zRbc!50(0nn-M!Ymi<&N-Q0&rVOg;Hi_d%D=UK^8d?5nsUZV&00oqu@dl6=44_kExD zKJR_sOUjLz^FGOa40A zApvPyvSe9;0XDePCc(98qBlk|m?0vJ6Hrjqy+*E`=d049h_kF15l4j9L&IWuFToFrtvb2zNtrL@h_v z2nvo}63(N?W>=et^f7i0Y}cQ9lj!B~r&l#A69$r5fy zYNKsxs}jd7D5io)s|ANpg&c=SIgUY;6-7|F(xSA%wy>QS;W4=i!L>wcda5>EF4qxS zEv!pRQz&6wf+7`z6=AI2)#^x1Qws?O#M!Xq?%Z%IiyVW>>^H#9CtWE2;BZHH%gY{ax9> zC)HKLoj@ynAyGVdW}pYi-+d>lv*q>s_e8*!d-?!-{^s#Mkx@3S4ghlgou8e}8w!e( zo|e7I+wsUVyr^6^#C3}9o~_>}zP;?9_57YcelI$Alo{-4NMmufIf zueUd?-I3BUNI+E6Kz>!b)aW;L$IX)5-hF!t0+qd*p?LS1!jfsT z?gkSAZId@$l73gx`nIv0f9xsCyL*^Th_Kfh9_(zX<2iyA9m3NwRBY6_*jWKz*%zXtfZu9Xi~;@8j}*ap0E4 r14aC?w7aOMg&+C!?=^uYKr=29SRN~Sr=Tku5B(POx=f-uHE-R2HCSU* literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_15.png b/assets/dolphin/external/L2_Dj_128x64/frame_15.png new file mode 100644 index 0000000000000000000000000000000000000000..9b796498c0c48c2c9125e98dbfea9c0c812345ef GIT binary patch literal 1344 zcmaJ>YfKzf6rKgPB1nVPmPk@M9co&mv-4zjX2KR0cA?vJaiwdam25kYyD))yI5V&d z{%}KUjP(aL#-t@ptfo~HZSg^DLSwYs z@0s(R^PO|gnb#6+trca}We9>)#P&qmq0GUjv9uVz7fMIsP(0yAJKgf-Jl&v>fno4Ks|yKzp5oW-Ol(vQ8D#^M^_8p1m2;$+-v20hxIAsg%;YD+3Zy^5e>4ZG3$tOyDo8(r(j?6gBn@wzqD6`oX%=0* zFc{HR(_(w1Wi=k$g|HsiwM2r*WHNya6EN*=f)WJ5^Fh-%w7{J~!@?YQmWe;VsrJIO%{ z5beM*`)viDNxHTGhV^@JAkPr85udd+cra35#8moqV7ReJ2!me&s-}txC-5@M2XRS} z1)OCl0hcI&$AQAqEXB#3N~#4v>*0ASNDJXeOG{I@g`%R7aF~oXH!~a=tz(*ak||(g zhT}?x0t&G;7<&~PeIQnBwt?iDcG5Ka3Z0PXFntSWt`@PAkGGP9+w4`#|5U23UWM4^GX_9#jE#~Z>3eC z2Bu*Z*oZvM(?JO^c#xzRoaa~?=M{m(Sw786oW!OjnZXE1k?=a_zD_ErV0(JYwb_81 zwdn!|94Q;l&SPbN`~>H2Lt9@`cUJ=oZ-7!G~sv4Z*RGdpLt(;QFk`(M1HN3K3S3x z?h*foSF4IX?k2lNh@xByx$Xb`^^t|eft<`8dnruc+pu@~*vV4j;ATZxtVWa@+h?~b zAKm-(&eCFaY&_K3@Yn9CrTJH8o$=WevIB~D-@hQ>q4&yD>PQtJ6~on$xmA#|9Td*{sg zzVn@P&v~ypxqrvDC$|9rc9cu`8d*E!$qXjQ``X|_g)C1*g{kO(XGJX(V30LE9aCio zHE<0hbMEK`JO)7Gq+OqirYaMf;W;ddeb~0+6Epy0>9&uIX&g~HZrE;${`1RoG-aD9 z`nAytU-5IeX_w{$d|-aEZp=>`(4^C^P-AV45I8tORNI+xL#>^n*LXFukIfuSt+_>$k#Zo)oQUViS>d8CqM{e4pC%?2NTY@5o$ATIMQRt9rDs^R}-Z0a4>=X}i7aOlkj200UJ zqz5MTdvKuGk*LvL4Qz5SXeRF&vkrEnay~_qKdfn+nxQ~dm(?VL3>`AEBtQlUP-U2{M}m zxqGNOQ7@2nb#?X3Pam(4-PLkFTW|mT$CYn}&QseH)w`?5=P&=b=aZ!$sL6>UOx}E? ze`{Z3VDQ4?q0HHi+qZJ!SMYAf&9*X!GiMjm)XTRaej`)8)PMSg+Ye_-nOl!u1k1Ru zZ-4*Z>EFQa{^vT4Q^$6_4{FQC<*#3o|8x3-qRjV+yf zhlH=c+dVen?|J(B1@P>hcfzX&hlV?U{WG}u`Ri{dmbV-{HVi&{Jo7>NE_-Fyjpr|2 zTL4D}sArCE_upE&xcB$dO&9!q{oi4KUt%P4Zs1<$BuIdp=XWj~{U~dZTtT@onO_<| Ga^gRXW|=zx literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_17.png b/assets/dolphin/external/L2_Dj_128x64/frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..80863f0b6935ebcdc353deb69a6b5cf4d0c4d4ad GIT binary patch literal 1292 zcmaJ>Z)n_P7|*p`cWu3O-I%%|8ZwZ%HJ9W~E|~=*oE27&jfM8X;EgN@(wi zDFRHO&Nw4+p4zXX5s%dHq>@&r(Imqp;?XFbh{af*PK4M&fo7Z7lEZ|nc-Xltt%@l=YD+WXAJXoq~g(Bb#dwD4_l7W{^N3W<Y-MP88j#+;%e)*wV`r+Q!^6&d*!PZ}nesb;Ey{|`4O@H^E zwHM}3|8eWz{nxu@dbl(Br3>c^cWWQC9SPidt0z+1Om{ACD`0t~x literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_18.png b/assets/dolphin/external/L2_Dj_128x64/frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..b4527bc83306f43a5ee138258cee037f073fc276 GIT binary patch literal 1498 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 BgtY(w literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_20.png b/assets/dolphin/external/L2_Dj_128x64/frame_20.png new file mode 100644 index 0000000000000000000000000000000000000000..f63904f29b2f44112bcec74fe738a623b2d9f1c8 GIT binary patch literal 1698 zcmaJ?dr;GM91joWAtK1;K-?K3r>LY&)6k}cN=r+z&LYE3!P|s1O&^0cr46(KL#|9- zn};{2r`s5cn~F}y&BIgY1|lfp&f()w<_XGl^9eqVbHYiN0(Ji3Etll?`+f8Me7~RX z>r#`Eo;EXhZZH4P$O!qoHCl07u%@S#pxQdIFA(RpyXv>l2gnYFjEWxI?eeOyVxm#ChUsYy+_Q2 zzzG#5PXbMn%2H>5a@t0LA`SwR2*Lw}A`Xe52&P4Kls+Cr5tPqGQ1*s-sF;U|Q4E~A zAl9Qzrx#}`lvDoLlLRs{j8)9#Ivfs;gU_LD1};w|5_xQ(D9mcW_6-(>aKaY*0|#Jld{u=Hj=H0et{Rv=I{RiTNol=L6e1M%EG7=5{Ui6(V28& zQXs;$7%qefQY(TnK2HP_JP{64B!*%o<-AG$O6ysoE zoSGm_7LR1Hr%IDSD=3?(h|(!-v>BX8v)J?o3i<@T0M#N|7{x_G7!%?+tQF~SSj11@ z2?aQY;-ns$!s}j3-(;(@5;hF0myH-laa2f9d{~I^_%JTOP#7mg2#n!+oDdM0p3w3k zE=!T?>73U($tz;p)3bdw8|>lLbWs*|q-^Z$Sax10Vdrjo#!5{pySm-(@B12V0Dy0U zN+HuY|LDjLvUDJU^W5&shfb(Mc97N&&EKt1yG}dtkoa=q$2;L=4PA!F!QPzKvZ8Hc zrBR_SpW&_dKk+HQveD;pf6U9hM8~c31N5$es%9uKE;mrxxX~JhxoUm-$0K8BC?WaH z)pfaJbr(-ss~u~ul_U3BOmT+&y~w&!V_^I6`_WRVaZ{jlJg8}-czauB{Gzz#x^P27 z-8va<4hU=7bTs*MS{WV`8VVDQp}l98`YS@nfG-OL&Byx+D}LQPXWHXE@ag7Rzl&am zjXvFe?^MsK`m>8Gm9x_;G*tV-871A4S+Z}}3dkFW3huX8G+p?L7tyOpdlXa~o}Z}s z=9&A#7fU{#QPocYj=_0PvxevOJdGbZV%_&}=gI3ML62R&)y0{;Ys@30f9EwGuwS}+ zMRos@uG*sU)`~L>qQuXs9DPxGg_4ZT_jGno4_4gL?1T*uNv>&o=k{Rr+GRuF`2GEoX$eX;jmbqa)9i>YV9q z(A{mL>RW$~Uca<&%sUtm1R#hl@4Frf3s6%T<53TGiW?9wj})A>}Aon4&N`}L>w58Jsjn05N)j;9{8cm aKN6?|A`4=UbUyd|HB_nTio+?nu73e(FMSmN literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_21.png b/assets/dolphin/external/L2_Dj_128x64/frame_21.png new file mode 100644 index 0000000000000000000000000000000000000000..076448fa940b42f0740c62feeb1943e00616371f GIT binary patch literal 1665 zcmaJ?c~BE)6kotbz=O_+tyqsGqP55-dnMVd1PBBKAs8W5v>j!$*+2-%#?4{^s6(mX zF)gOmv6Sji4=T0}W(4VBhoYk3RmXx>@MzFt$I&S`)3H>ebOXfthtr+i{l4$*`@Q#j z*X%Z3_L3>#Gs6J@n39pM$r08H;YkV&5#9r#JG8saj+#EE!4y`F8~(u_y3OkiUJ#Q8D|y>LltXSy2Q%Z_zaCoEPRm|%|?=z zD+vQmC?JY9C?Om}ln{j|35cO_6i4I+xe+!7?7RxE(WD>*f@so`)oBQls!^-q)RYuV z4yVenWCF$l*bJM4r))G6@NE`+k7HB*7fYsa48^mYo@I*zouDgZdDc};o2?Golrq|4H$3hT(oNJDS^EdU7Kl%Y}U zU40#^dbtj8a`W?%LyH%`6}ly&*VFx1gE{omDr;oyu{FcK{O{^Fb-JF$&zg8K7dr4+ z?~w=CS(C?*)wptKuE=0Xn&zI_b}E1Bg&0r5*LNmPmX~}PUNL1y=Sb7>y<1o8Y4jdw zZwI{t+h-?EjY?Yk!SuPF)76JR{zTM0i402I&`O?c{Az0W(ETd!jF?BcS1P;S-@NUT zw|O-ud+50|P~8crXXo}~y|vc>Ji+6%sFxQQYDJe*KRO238~!=9sJwO0a&JHNr24_H zf9hdKeX(DBrn+rw{q7)DO`oOcc{H+PZDZq=?CH@WXlH---V0|tkIZM~(+^a(INuw( zT4OqFQk|cX*MerlNwcD^H)tO04-2liX_40fK_NG-Md{Os%+j6hzpSY8w60E!o94c) zQ;GI%=^Nl6$i7s(j zdVXBY@C|y|hNy>;k>!u(HFk)Epp~_O|TUWV$e-xag2+53mE$H?f-@US&5NnTHw%hKG{ubHTrC$+!GWa?jx>IuR=61uGuuCUn-P(;VNI@mp(>qsi0_UI(@nS&LHE==?~46>Y3P-=bBIav+ZV(4UU$iMgUUA8-f& literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_22.png b/assets/dolphin/external/L2_Dj_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..8651f12f8bd91d684d5943af135697e9e7d700d0 GIT binary patch literal 1809 zcmaJ?dr;GM9FMfpg1~U&alT`4t}1DhG_+|2ZJ`AVR-x9T&KGH#N)OxAHcDYbD|!NR zDpSun>vN~>oKBs}eBcH?PQ|I9PCZbZcy@=6gQ@rcL6#~w|M1Ku`Tc(1d_Ujs=li;p zWll;94;>f^06@5Ig2uqDUhYu|dEDDCT&3rh0j$=Jr~4z zbXlxQgC@1dA9qTEa#_}?lt}XP^Tqj6G2@yeK@|!`zy^lFoCfSJaImBYcDUnuEof*r z<+3?h8{+^27D+QRpG|@|wr|6*JN5c^h8^ynWN~Secu1!N6(bV6JrLU)Z8vM6|EKXm zZMU(&NlOg0o0;#TxSCkwdcj=&KJF->$gxpQcG-5HL;Q7>>%!G7DnqweumoMw5aPD2Apct5Z=_t5K^FZAyw% zhG-L{$pj+p#p)bxmUK{buWuXYy9bMW6st^e(Im^bj0`ikw-YjR8J2P9GENY~#W*O^ zla$R7kc21&&+GSfnTT)$&9ek5!rR@V0tO-&XW=6~~MkwSkE++`stgsNULYjce zWdx29lojg1Ti#3G+g9ZyTo}$?HcA2`FgZy}VL5_IVM2yuFhMC07$>X*DI;+!X_i6~ zj-n*cIq!8+uZU~U!1mp2aEEu(MLW2Wa&fcMYI2R_<}NsMqEXAO-QC?g>bEp+YX!u9OJ&N0#opWU_y|KR&zITY9Ip_V0eMJ9@lr5#9y+=*fn)byNzk7lC0BorAn|VQRv$u&DmcOnOTTQ7J>G zhekI#=Dnzt+{!u@RNbtPSw2{-EV#in0n@Srpi{@2x@pMC8^Ydx{vsjItU&pdSwt-PK6 zS#91uzkh93W838TKX{SGFAjy2XD+JPiNxPi?ZGqDUD3RhoxbRnlB(asvjpeDc{lUj zv5%xHy?x|)qk^-W_*<2p^{;2$-gaQ!6ID$GPfH)q{0bT(hl$$bfOqq}+0Tj_59@Bl zjU9S4v>`9m{LA*Tk~ZfPzb;+?y6=^15A7P(rRI$glsD#HWVeqmsb4sALi%jlynp4V za<~K7N`<9QE9(ZzJC96RKl(Ud6*}uwwpg@c)ssK#D^c&&(jToVK{cst%vzNZeD3QV zg(0gJjm|w`l%o{tsyvoy!0K literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_23.png b/assets/dolphin/external/L2_Dj_128x64/frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..d2d8e7e5144697f5932a8e920d673c29de730372 GIT binary patch literal 1775 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_27.png b/assets/dolphin/external/L2_Dj_128x64/frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..39ddf46ab3bd195b3e75567a3f91021720c9e1c8 GIT binary patch literal 1759 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_3.png b/assets/dolphin/external/L2_Dj_128x64/frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..40d1314c95fb07190f995925e69f77df558f8cbf GIT binary patch literal 1777 zcmaJ?dsNeA6ptX!!4ySyz*i{vnx;+D7Scc`l(rV7h)@;h)TU`F9ki)QfP#v}Q*{jR zdFIq1zQHL+Jvtd4buwS`wZTzgQykzFA7ew9=wLFtRH*Y0k2y)c@4LCbdw=(JQk0TB zEzm#A9{_;BM71K7TkYHv=j+M6+kDq)xFwWTrn703k6 zxlG0afae;MHl0n^Bw++)=Ho6MzRhgq&;SrS)n>(sOp*ol z%6R>x(lsey0!5Rcln+A$3=6?%DW8B5Sgc13q;U#}z=%iyBis!M5lkq?5HUD#@i>pP z!HA_QR0ICFlZ=V0wEt3n9Z)(UTHIID)~Q+w`w!m zJS!{%gg+iqwJ|0#kB#5G5 zr9>o0VUZh~Xkl2~LXd9XCeHT&7I`NYOQ1=drD!chWw|>cC4*uqCWEqqh?p-1Cu(rQ zWN}F*xvJD3w1T8fIix{FQ)cj0nlaN`C>SN8C`1qIAq161L*i%@h4fMb3Q0v0VRRHK zMo_}Y8^9ahNMC=eauO~KtD6mmA}A7#lOiY@7K$J=N{m1#A%!6^YDDoUTx`VkBA$Sw zC~$So8=d49aqa2azMc*4@OrvP3pY|UH#>C0@?dW6yi=02N^W&Jo%?R>_!R&=IuaG} zTHEbE<_%hVAND=xbap>JwCSSv#`WJY*yHNSr8x~ruy?@aZU4qjty|%2&41}Ps)ZTS z&~SME%!2JJ+GanU&~aKNubvfj*2q3x(4}Lv!`8HnpS+}FOw~qo3sp?4yxSN6+UJI6 z72g=TXIoO>V`^=^v)M|&)ZeWeyY$Ph_Jp9F+a8cbS9pEB^t2=Ifm>A4AL0V{EP1mIw>30iOq)uLK>okRiy>lBR_MbYy7I(x7p2uiCOf!87(} zk5z|#q>DSg_b(_iyk=9Yj@Nvp&^OL;0t-6rr`t2MQ@y$I`QhH~oe^hz07hD{rf}=> z9+CXV+XsOP-|`fDw)Y&gb_S5;p(o|;L)Y>I~$)hhJ)JV4SL__$2QpOs=CTkLiU2}Zy^Ck6b=tZFpw6s>+jai0+s<# z^N0?}R1^f9de$R(`qlBW62E&tLqLxVj2K8B>;-$hl`ZNR@@)IWI8|WPqcW(p?quk`u<+*0)$>P* ziZ*rVhj^E4+BFC zJF#+Jeazt5`P9_F!lmPGhd8q@gs07T@cjKlF_)j!%eOkN?RIpXE*gHe_u|cez*Vto z|AtCQL*c}l9bcn2gFoJKY{Vd6AO4iJeTzH-fQAKrUcxWfcGpiRQJJhbB-gF{58GOv ANdN!< literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_30.png b/assets/dolphin/external/L2_Dj_128x64/frame_30.png new file mode 100644 index 0000000000000000000000000000000000000000..27c297e8d59c983d0a06ce9afafdc39064216039 GIT binary patch literal 1408 zcmaJ>YfKzf6rP0z)~)4H)TFJ3af+4#v-4zkX37ft;Mp#$+cgr4hIuVaV0YHpp}TC` zWUFb~{;1#&joKz9n$}lR5lz%I^00|sYcrp@Gh{}AEc8zbMHO( zeCK@U+;irLx1naed6OA}p!IdNZXcKv;H@;JgYPeQznpE;XP>c}<2Kk}Ak> zU65~;eKId~z4(n>4ngUM6n~T6*iKh9X;17Tj_~ z)ReHUs3ACMkr&iX-HrgZ%V7k=9?z=bP-G=pAZ=KT4`YM{$AZCRY)je^-6#J~V@>Ud zzbhd3k*Wl5o2sjkHZ=^Bw1tL?JiMrcl9Fx7 zTUrjI}Ii=#9XV0bG}2Y7)(FhCJYR?a<@loEmR zOfFY@18i2iOAdjS(m?M#)c2JN`YzME(_aPV#l^*wmrq=QAjAGTx5FR%@%PJrWWS7? zGR7|49sFy*wFU^`;UMVO|*cWakyoStc6*)cm!&`@H_nlwp zF*0}a)EyHKOdH1AzQu0*-9MH0ld-SA$%!u=fd-^IjZyKO4L24ZYPkN0@rcQlQp|;`e;wT z!GC)6s?uWge(8EM@qK>%lf7TuY&N5b(0pO#_Qxp0+f&D1r{rP@#gJW*+n|L}axNu4OzVvwe9JU;vjA4Jq zh#}s1JneAzVjwa1dMUhMYAG5>&wUl@9Nq!_S~Bo(?&b%_kdQPJ~{u%69q#* e7k@BOlQ2NG;zy>fj4vzsPpqqIaG!Rzy!0Pj5bA>f literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_31.png b/assets/dolphin/external/L2_Dj_128x64/frame_31.png new file mode 100644 index 0000000000000000000000000000000000000000..f2aefbfae2f1ed51e8e87c69f47d42b6d57b6a7b GIT binary patch literal 1404 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 UNHl%vIsb2!jCaRAjU1c&4=x3@iU0rr literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_34.png b/assets/dolphin/external/L2_Dj_128x64/frame_34.png new file mode 100644 index 0000000000000000000000000000000000000000..81f133ac5592569c6963b813e7eee54087a5195b GIT binary patch literal 1341 zcmaJ>YfKzf6dr^uO>wc>#DuoN>0qKnXXm-QGZVJmC+%vNlI_M4k(7DdWk;AtX9jn` zqy_t<`k(ow@g( zd%knNbM85FsIg(2ckPC?2!eQ%+v90C_rN#2rWAfJujxy{sme(-IlGLklNT+3L=-~; zXi^i~KpKcj*WPbI9fFh|R5MLZQ)-7G8=6ln==faCglGh*+v=L4+zuR60&S`u!e+<5 zz))2QVY~e)GG)d_+=< zkCu`Z(((Fbe{dJVa*ksP1d-3@eR;-bSZxHw^L)VuP2*4lx4U#lbaCCTDO!jFTeeiw zQ4Jj}SQI6r(+Oda?NS(;nM$n~*6rnF!L$*sXcCld| zWhfpODW1cD%+f3skOB&+6z!~rrM|($R-x}@Y~+DhA!Y&5F|3SXbQCM0F=sf2oij|7W_>LBbV`&}y&$PA+|p9eabT&h z0HxkCG;}e|g1QQYte*+c5-H&{#|LpX$Z@#DD;&-)_nFO2{78XwZa_kaP&;`s z^86 zSUq@pTkhoJga3XP9cY=_GmpMM7uzXC{=`}2Ir8(V-PEWZzWVV$rB99_?|#t3jSb&D zG1W6sO5ahlsJpK9YyZ!`hdtM;-btp$kBDg4~+HYXme{t=gri$nls2pT3LVj*o_N?A6hce5Fd%Q GzV;v5Zqt+i literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_35.png b/assets/dolphin/external/L2_Dj_128x64/frame_35.png new file mode 100644 index 0000000000000000000000000000000000000000..c828207d33ed6ec94a71c904597dd24b4805b3b3 GIT binary patch literal 1255 zcmaJ=U1%It6dt>whW_M5Y@;ZQ(+X8PGk1P=cV?2!-_E94%%;m`!T1t-XXftiG?|~y zOuCblYMP4L2dhGbRV)#;STGjEKd=xj6j5KaNeZtO$oFf;eg zne%<;JLjHrB0scm`<92d5CpM3JD?WuI*p&6));Tnc@?~m%q&H& zx&&h>YE9HgE>ET%50NqhXdM8KOv;Q7cpz%Lfl7Nx9`FJSczn|wuW+Kmi)7=Xa7522 zDFwB^5f6W*s7esH3d>fjRi-L1j#p+mS(YOXo~N+~?N8bP3~Afn)nrhSuY0B&n2t?G zj8JnXf)s_-uBBnQx!gLj?Kg^r%f^P#WjO}0mKEi;>g@*w^uLS^z5U{(i`WA4oe59J zXHweL1mpVMJ5c0^)hPQt6CVs5R~>!ALUxc?JlW9@lWEyg^ zL?d0~MJ}Nw3}7^QHo^-6P(`37`uq9-$7R&sUXV$rg#^gNg+2)gO>EZo18D208Qa9M z8`$_gu}a!QFmSx07njEsKIvB=dj zE9M3gN^v2t}wNUBbI7S+%zqE-u$Dv>gZA(l_taKlmb6d2eFRBM&489_+hz z;oRS%y1e+=hxU!5->~+!w`-g+9qTS^J^3%!Rd4_0{H@X1+h;c|-u(Kt>8>Yhm#FZN zPIPSkw4VL>^Sw*Z`U@RseQS$A5P8p$J+jU16A`K z2Y(-ZyuRG}QEjL#{A=d>(F2!fj?A69zV+hL^Eb*X?~Zl9v-{2p^RoC@aI~eJIOg8x U{4;a%BF>k{W`@*reFtX#193vGHUIzs literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_36.png b/assets/dolphin/external/L2_Dj_128x64/frame_36.png new file mode 100644 index 0000000000000000000000000000000000000000..fc923b40236dee851d9c3b1f60a64d0050b67350 GIT binary patch literal 1059 zcmaJ=&2G~`5O!4wRh2k$fYZvA_>ow9y>^nd)zBt!N+Z=JN+S_*Vr{P*)A|Q{OWdAN zC4@Nf25{mE55NI9@&pJZj@*#?2w|PXrH863+dDI!Z~o^^fA`__)my8QBwg=q+XHdG z6z}HBviSa4dFzPlChv~;UJ~#L&6w2k6PJNrMEBW%QUCb)ceXA`%df-Xh>x5d%S$4K zmO4rir2;KU>l;N%y#vO9%l5;#DW84*B7@Lx%8zRfa?%bPhubF^+dJ7EdM5{->B}1r zzng(Hff}|n!@>qw99ekG{Jv{)cg?c0N2#nL1#3a+G^0qFPSQg-NPJVmwRod^ZGh zzFjO9E0u}ed3UFHBKGcDe`nYg_jEe_@bmp|L4ls#8Wz9)ep^}++$orTinljQvOH(E Yze-?JTKaM0*{hQ8b$9KL?Wd>z0Hr%cDgXcg literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_4.png b/assets/dolphin/external/L2_Dj_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..d372ff643b40c4cf27bc810fb25a3ed239535b5d GIT binary patch literal 1727 zcmaJ?c~BE)6kiBRhRdT$MX|0y8B{jeY(lcTLx4aMi4uws6;RO4W&<_xrxL@AuyCU9;nM`l^5xU6V3nUzLjT25#P7;MKG0|tsFK)WH`XvOUk&Y)eK**nBM4mhYn zr%548t5Y5Cmd0AZwNjFRMp;RV z-c0LFM$loA(3mo435Q`j6o$d9QoS*3v<@eWNgK~jn0b6I%rh7qu?=cl=~(K28t>G$ zsx!?LFP5^JGAtxh6P@1(n91M!9XS*kHu%RDJyRGWU1lOP43v>p$|M}-3s3WwtkP(u2_s33_|`MNhq0pfV(|zIMbIXT+GI)}>4cb66K%4ln#>>~m4iCG2GgPtCJ5q- zMW_%#NgZbxuYD_hL#@h4m@v#EY;Y7o5ivmtATi7rK&VKFKq!g9kPy|OgoqI82#tWl zV<_?*o%2>Fjfj}`bZpYwXXb8nOq5#AtOElB`~N6w0|4h! zN|{t`Z#k9V9^C+Y6g^v|cS*1A&?cSPF?M`c%kkols=C1J<@3kDFJ4_`ClV*XJO8+! zox!f^_vyi+_Sly-^|^R=-`s(FaNBrO*MH{L9GK+s-Q$JN>Smw6rTXpA*nNQ;8eA2X z9w*z$n~!S!{wE~ffz64j7pBc;6$59x;t87FbTPD|9L4h&xE59jLtLzJo4L-T0dTU< z-<`VZ2eW{r2_bi@XY}TG^pqRQI@~Ebj>uyfq2m5tBmo(HY z__3&Mpdz+)c+p zEi2yNr+jBFH==i9Nqyr1<@Ur!KF8FpE8{#%XU_1cJ&<0os!v`}=~>dus{scZn=&U{ z-G6S>QO@NJe`?}a#Y#JW)$b02JK8(8*vgiEOAD?=`ezsHE4nxPNQ}QUVaB8l;+-=KlMOI!zDSddDuouQvmIZjsBsC!Fjr4Os1%)+;r1jmjZSE;h9VFzIpQee$VfD z-sjnnVVoB_ar#6607CWY+AMjkke}q>0Qub?ywxBt(ULA#$`&nB3G3tmjafADpx(yj z^I1G=E-gCCCjmgfIw2=l$~7!tIMJqHy*>)J%^{-!AZf1K!E(!a2{iHff?W;WKY9WJ z1+yAjlxRQ@s5xSGJs^&CuLAV3M1W32eN?f^J)(-Nm@o&l8F$N_Vi~z?j zNRH?<)3RUk^6&70eZw@b?6|CjMn zZ&yyKgI8wpF0t6j$!B7T@qy*~y*iNBQC7pe?-b;NVGFe)S8U_$l3uHZf~8TbmoX+p|2A%tfC~ktfB+N2n7MYV_-SK z?zO~uPiZu1E$mEO*I zp_6UZqZfQKqt4>>;xjM z^8PHOwjt_zU~pS+L{S8m^zfUe#+G2q@4#fs@xV*_1Uz{|?YxsM->)fCLNVHs=M&a@PPedfEmY;l0M93#=g}bGU*!Tk%Yz zPkH-Z==ck+4_@og4?2$*o4Xe&4sOhnxVNG!>Tb1EDx=nS3GrlSf`5CEc~4!{hr^kX zU^x9pdxWr&udCkP&l&a|>i7FAB~toiO5v_W?wT5O>-PFzvFL)vnEJACzn->bHMzSj zJMlhE*tOFsD_d=Rxv8Y=*ur$n-U-=@&)0Jgn;G`AM@#g1GnzEFTS_m*9y{e9IDJQE z@wu*B$H>_quhq7MZB4Rd85gd9FC=i+uDUq#>mTz&_I2%Eqe?%tEpE!Y2T!&>uoI&hrEhO+t@MJ8-10H(1?aH75N0`nbEpTRxhfH&ZWW7d2gq>8$F!Z2WfVx#EJR z1=R<-kakt;o3lyd{!Kf|Gp6pn_T}wl=5XSpye9pUwJh&n9kBgWwI{X%Xd4dJ?Hpg* QB=;Vm*BQ0VDS4~^1HhwTkpKVy literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_6.png b/assets/dolphin/external/L2_Dj_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1e84a11eeb499fcd47e7709e166c714229afe5 GIT binary patch literal 1635 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 a;?v;Bx|Au6L*e|`zuj&tqdJRfTmA#Jq%n&C literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_9.png b/assets/dolphin/external/L2_Dj_128x64/frame_9.png new file mode 100644 index 0000000000000000000000000000000000000000..05de5d5c698a86baccd74cfe33d1109ee566f793 GIT binary patch literal 1610 zcmaJ?eNfY87*FY-RYd$4;Mce;$Tm0ACTVDsj7keF*o^{*I5_7`lQe}4+JrQW!qls% zb4>51c<1~=xedSADd@J-`89URhTeJNoJ=VSH#ZzR*%XIt?mU(Pb^hU*OY*+&ljrw5 zzvt^xo0pRnGjY~L003ef3vKzz8c-gKR;|2$)qd<$mYK4>NM0nk9l8c<#6f1aibVx_%_lOD_08-QZBF&a^GRSbnyw?oD#SE^5Usx@1i>h-9*y>W2bU|qgz*Ik_81Qg14f;K0ULWN*L!)*nWgimj zA#hYhE;U1Aq>7w*V1^)ZAgM!O7C}(ZMCw=sLvRLjaqjsbhF}Igf+;tQVibx~7!HnK zkm6Btxv6|x=D0uQWQIy)S)}y(%F0Szr9mf1#d?$^$&d{UgB1q%nM#HWRYfs3fT-PY{$azh)(BY!(QKbvXr#x{j{h@b%@^M3B@+5?UVDl|7ko` z+gDH}a{7GECsatRQWJMl7_8*)*^WYr3L7d{;+4YCWj2AW@Niz)VKYO@7oCfDQLK?9 z7@RP{G|P}MZa_(xMo9wZSRBJqBV%+SuCSe_;W3k)w51!xRbht|!tr)~bp`2}2CCp$Lq?Of+YJO$cg$2_ufd1WO_?PPhr$NaJprF+h5SqCV6) zPjpgPq_k&f`*=2#!{h1Vyvj&P%ItLP`YBGCyQsYE0=u$~jEwBPzUKx2L}WQ^)&hUm zo$YT+XA!Oa>wkOX;<~QBn7V_(e%mts)!T`xh`H12)j!U;=6Bz#Oan$@fu38(fL6=y zNHIRq(5=b)VDjF(NkH}=L-&SmJLV1qs({niGM?YH=EceC#=$8`Q-hj7{gn3o&jo?E z(zd2eh*B{t|4ynq`_6-?nxLh{r!^MdY zC$;gLk^YF851H$lZ!L#-^{JECRQ0OaSb|!JnRBA?=;}?wvpR!l3lKP7f3iO!5`2)@ z_=UCtys5c+DeGLeba8gAs{MdZb@N$M@Qcm}vksJ!osZl6;O; zqZVUKS5pJ1I`6?&2&DzA+N}en>UA zv$^-BzOD#!)VsT4T$(-3&eWm3`4IgLP`WPP Date: Tue, 6 Jun 2023 21:11:23 +0300 Subject: [PATCH 064/102] [FL-2872] Remove unused resources (#2740) Co-authored-by: hedger --- .../scenes/nfc_scene_restore_original_confirm.c | 2 +- assets/icons/NFC/Reader_detect_43x40.png | Bin 3799 -> 0 bytes assets/icons/NFC/Restoring_38x32.png | Bin 3794 -> 0 bytes assets/icons/NFC/Tap_reader_36x38.png | Bin 3748 -> 0 bytes 4 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 assets/icons/NFC/Reader_detect_43x40.png delete mode 100644 assets/icons/NFC/Restoring_38x32.png delete mode 100644 assets/icons/NFC/Tap_reader_36x38.png diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c index 730dd41e85..16b0953f80 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original_confirm.c @@ -11,7 +11,7 @@ void nfc_scene_restore_original_confirm_on_enter(void* context) { DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_header(dialog_ex, "Restore Card Data?", 64, 0, AlignCenter, AlignTop); - dialog_ex_set_icon(dialog_ex, 5, 15, &I_Restoring_38x32); + dialog_ex_set_icon(dialog_ex, 5, 11, &I_ArrowC_1_36x36); dialog_ex_set_text( dialog_ex, "It will be returned\nto its original state.", 47, 21, AlignLeft, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Cancel"); diff --git a/assets/icons/NFC/Reader_detect_43x40.png b/assets/icons/NFC/Reader_detect_43x40.png deleted file mode 100644 index d833a5277fcf067cce9ae363ffb2bccf31c6acb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3799 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 Date: Tue, 6 Jun 2023 20:18:24 +0200 Subject: [PATCH 065/102] Serial_CLI: Fixing serial cli logger error so it sounds more concise (#2721) Co-authored-by: hedger --- scripts/serial_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/serial_cli.py b/scripts/serial_cli.py index 2fa37d7512..6dae68be6f 100644 --- a/scripts/serial_cli.py +++ b/scripts/serial_cli.py @@ -9,7 +9,7 @@ def main(): logger = logging.getLogger() if not (port := resolve_port(logger, "auto")): - logger.error("Is Flipper connected over USB and is it not in DFU mode?") + logger.error("Is Flipper connected via USB and not in DFU mode?") return 1 subprocess.call( [ From 3e1f209d64981fb806b8819be1806e61a1a6f1ac Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Tue, 6 Jun 2023 12:00:43 -0700 Subject: [PATCH 066/102] Furi: smaller critical enter and critical exit macro (#2716) * Furi: smaller critical enter and critical exit macro * api: bumped version --------- Co-authored-by: hedger Co-authored-by: hedger --- firmware/targets/f18/api_symbols.csv | 4 +++- firmware/targets/f7/api_symbols.csv | 4 +++- furi/core/common_defines.h | 31 +++++++++++----------------- furi/core/critical.c | 29 ++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 furi/core/critical.c diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index f3c4e31d75..1dfe4c7a25 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,+,28.3,, +Version,+,28.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -288,6 +288,8 @@ Function,+,__clear_cache,void,"void*, void*" Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" Function,+,__errno,int*, Function,+,__furi_crash,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a8708ce8c5..2f6d8ffd81 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,+,28.3,, +Version,+,28.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -323,6 +323,8 @@ Function,+,__errno,int*, Function,-,__fpclassifyd,int,double Function,-,__fpclassifyf,int,float Function,+,__furi_crash,void, +Function,+,__furi_critical_enter,__FuriCriticalInfo, +Function,+,__furi_critical_exit,void,__FuriCriticalInfo Function,+,__furi_halt,void, Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" Function,-,__getline,ssize_t,"char**, size_t*, FILE*" diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index d7bfaf2076..5bd218d357 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -31,29 +31,22 @@ extern "C" { #define FURI_IS_ISR() (FURI_IS_IRQ_MODE() || FURI_IS_IRQ_MASKED()) #endif +typedef struct { + uint32_t isrm; + bool from_isr; + bool kernel_running; +} __FuriCriticalInfo; + +__FuriCriticalInfo __furi_critical_enter(void); + +void __furi_critical_exit(__FuriCriticalInfo info); + #ifndef FURI_CRITICAL_ENTER -#define FURI_CRITICAL_ENTER() \ - uint32_t __isrm = 0; \ - bool __from_isr = FURI_IS_ISR(); \ - bool __kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); \ - if(__from_isr) { \ - __isrm = taskENTER_CRITICAL_FROM_ISR(); \ - } else if(__kernel_running) { \ - taskENTER_CRITICAL(); \ - } else { \ - __disable_irq(); \ - } +#define FURI_CRITICAL_ENTER() __FuriCriticalInfo __furi_critical_info = __furi_critical_enter(); #endif #ifndef FURI_CRITICAL_EXIT -#define FURI_CRITICAL_EXIT() \ - if(__from_isr) { \ - taskEXIT_CRITICAL_FROM_ISR(__isrm); \ - } else if(__kernel_running) { \ - taskEXIT_CRITICAL(); \ - } else { \ - __enable_irq(); \ - } +#define FURI_CRITICAL_EXIT() __furi_critical_exit(__furi_critical_info); #endif #ifdef __cplusplus diff --git a/furi/core/critical.c b/furi/core/critical.c new file mode 100644 index 0000000000..57fe2403be --- /dev/null +++ b/furi/core/critical.c @@ -0,0 +1,29 @@ +#include "common_defines.h" + +__FuriCriticalInfo __furi_critical_enter(void) { + __FuriCriticalInfo info; + + info.isrm = 0; + info.from_isr = FURI_IS_ISR(); + info.kernel_running = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING); + + if(info.from_isr) { + info.isrm = taskENTER_CRITICAL_FROM_ISR(); + } else if(info.kernel_running) { + taskENTER_CRITICAL(); + } else { + __disable_irq(); + } + + return info; +} + +void __furi_critical_exit(__FuriCriticalInfo info) { + if(info.from_isr) { + taskEXIT_CRITICAL_FROM_ISR(info.isrm); + } else if(info.kernel_running) { + taskEXIT_CRITICAL(); + } else { + __enable_irq(); + } +} \ No newline at end of file From dbd48a04d4eced78903f79ffd06499ced58de843 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Tue, 6 Jun 2023 23:13:41 +0400 Subject: [PATCH 067/102] [FL-3331] SubGhz: add subghz_protocol_registry external API (#2712) * [FL-3331] SubGhz: add subghz_protocol_registry external API * F18: fix API version --------- Co-authored-by: hedger --- firmware/targets/f18/api_symbols.csv | 2 +- firmware/targets/f7/api_symbols.csv | 8 +++++--- lib/subghz/SConscript | 1 + lib/subghz/environment.c | 7 ++++--- lib/subghz/environment.h | 7 +++++-- lib/subghz/protocols/protocol_items.h | 3 +-- lib/subghz/registry.h | 1 + lib/subghz/subghz_protocol_registry.h | 13 +++++++++++++ lib/subghz/types.h | 7 +++++-- 9 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 lib/subghz/subghz_protocol_registry.h diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 1dfe4c7a25..f551a09c15 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,+,28.4,, +Version,+,29.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 2f6d8ffd81..b6eb8c7658 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,+,28.4,, +Version,+,29.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -189,6 +189,7 @@ Header,+,lib/subghz/environment.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, +Header,+,lib/subghz/subghz_protocol_registry.h,, Header,+,lib/subghz/subghz_setting.h,, Header,+,lib/subghz/subghz_tx_rx_worker.h,, Header,+,lib/subghz/subghz_worker.h,, @@ -2662,12 +2663,12 @@ Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char* Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" -Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* +Function,+,subghz_environment_get_protocol_registry,const SubGhzProtocolRegistry*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" -Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" +Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, const SubGhzProtocolRegistry*" Function,-,subghz_keystore_alloc,SubGhzKeystore*, Function,-,subghz_keystore_free,void,SubGhzKeystore* Function,-,subghz_keystore_get_data,SubGhzKeyArray_t*,SubGhzKeystore* @@ -3361,6 +3362,7 @@ Variable,+,sequence_success,const NotificationSequence, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, Variable,-,suboptarg,char*, Variable,+,usb_cdc_dual,FuriHalUsbInterface, Variable,+,usb_cdc_single,FuriHalUsbInterface, diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 6d9c0cd063..3a0325b71d 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -18,6 +18,7 @@ env.Append( File("blocks/generic.h"), File("blocks/math.h"), File("subghz_setting.h"), + File("subghz_protocol_registry.h"), ], ) diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index 5ded243c41..3794dbad84 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -92,16 +92,17 @@ const char* void subghz_environment_set_protocol_registry( SubGhzEnvironment* instance, - void* protocol_registry_items) { + const SubGhzProtocolRegistry* protocol_registry_items) { furi_assert(instance); const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; instance->protocol_registry = protocol_registry; } -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { furi_assert(instance); furi_assert(instance->protocol_registry); - return (void*)instance->protocol_registry; + return instance->protocol_registry; } const char* diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index 7bd38ba2fe..c15b8b211b 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -1,6 +1,7 @@ #pragma once #include +#include "registry.h" #include "subghz_keystore.h" @@ -9,6 +10,7 @@ extern "C" { #endif typedef struct SubGhzEnvironment SubGhzEnvironment; +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; /** * Allocate SubGhzEnvironment. @@ -93,14 +95,15 @@ const char* */ void subghz_environment_set_protocol_registry( SubGhzEnvironment* instance, - void* protocol_registry_items); + const SubGhzProtocolRegistry* protocol_registry_items); /** * Get list of protocols to work. * @param instance Pointer to a SubGhzEnvironment instance * @return Pointer to a SubGhzProtocolRegistry */ -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); +const SubGhzProtocolRegistry* + subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); /** * Get list of protocols names. diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 4ca1c4679d..f1a28ac9b5 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -1,5 +1,6 @@ #pragma once #include "../registry.h" +#include "../subghz_protocol_registry.h" #include "princeton.h" #include "keeloq.h" @@ -43,5 +44,3 @@ #include "alutech_at_4n.h" #include "kinggates_stylo_4k.h" #include "bin_raw.h" - -extern const SubGhzProtocolRegistry subghz_protocol_registry; diff --git a/lib/subghz/registry.h b/lib/subghz/registry.h index 91027807e8..8529c10970 100644 --- a/lib/subghz/registry.h +++ b/lib/subghz/registry.h @@ -9,6 +9,7 @@ extern "C" { typedef struct SubGhzEnvironment SubGhzEnvironment; typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzProtocol SubGhzProtocol; struct SubGhzProtocolRegistry { const SubGhzProtocol** items; diff --git a/lib/subghz/subghz_protocol_registry.h b/lib/subghz/subghz_protocol_registry.h new file mode 100644 index 0000000000..6a27da9925 --- /dev/null +++ b/lib/subghz/subghz_protocol_registry.h @@ -0,0 +1,13 @@ +#pragma once + +#include "registry.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzProtocolRegistry subghz_protocol_registry; + +#ifdef __cplusplus +} +#endif diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 09eb07eeaa..719beff45f 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -21,6 +21,9 @@ #define SUBGHZ_RAW_FILE_VERSION 1 #define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; +typedef struct SubGhzEnvironment SubGhzEnvironment; + // Radio Preset typedef struct { FuriString* name; @@ -115,11 +118,11 @@ typedef enum { SubGhzProtocolFlag_BinRAW = (1 << 10), } SubGhzProtocolFlag; -typedef struct { +struct SubGhzProtocol { const char* name; SubGhzProtocolType type; SubGhzProtocolFlag flag; const SubGhzProtocolEncoder* encoder; const SubGhzProtocolDecoder* decoder; -} SubGhzProtocol; +}; From 6f6ead1726bae2de28b7aac8df3f97338de3be13 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Tue, 6 Jun 2023 23:33:04 +0400 Subject: [PATCH 068/102] [FL-3045] Fix core2 permisions (#2742) * Fix core2 permisions * Fix Python code style * scripts: copro: changed int literals * scripts: copro: shorter string line in code --------- Co-authored-by: hedger Co-authored-by: hedger --- scripts/flipper/assets/copro.py | 11 ++++++++++- scripts/flipper/assets/coprobin.py | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/flipper/assets/copro.py b/scripts/flipper/assets/copro.py index f176e3b2e8..25c072899e 100644 --- a/scripts/flipper/assets/copro.py +++ b/scripts/flipper/assets/copro.py @@ -58,14 +58,23 @@ def loadCubeInfo(self, cube_dir, reference_cube_version): def _getFileName(self, name): return posixpath.join(self.COPRO_TAR_DIR, name) + def _addFileReadPermission(self, tarinfo): + tarinfo.mode = 0o644 + return tarinfo + def addFile(self, array, filename, **kwargs): source_file = os.path.join(self.mcu_copro, filename) - self.output_tar.add(source_file, arcname=self._getFileName(filename)) + self.output_tar.add( + source_file, + arcname=self._getFileName(filename), + filter=self._addFileReadPermission, + ) array.append({"name": filename, "sha256": file_sha256(source_file), **kwargs}) def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None): self.output_tar = tarfile.open(output_file, "w:gz", format=tarfile.USTAR_FORMAT) fw_directory = tarfile.TarInfo(self.COPRO_TAR_DIR) + fw_directory.mode = 0o755 fw_directory.type = tarfile.DIRTYPE self.output_tar.addfile(fw_directory) diff --git a/scripts/flipper/assets/coprobin.py b/scripts/flipper/assets/coprobin.py index 75bf76d766..84f52fbb3e 100644 --- a/scripts/flipper/assets/coprobin.py +++ b/scripts/flipper/assets/coprobin.py @@ -46,7 +46,10 @@ class CoproFooterBase: _SIG_BIN_COMMON_SIZE = 2 * 4 def get_version(self): - return f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + return ( + f"Version {self.version_major}.{self.version_minor}.{self.version_sub}, " + f"branch {self.version_branch}, build {self.version_build} (magic {self.magic:X})" + ) def get_details(self): raise CoproException("Not implemented") From 28f4cd3d3ce172f9c60283120294de1a606d7165 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 6 Jun 2023 22:43:44 +0300 Subject: [PATCH 069/102] Fuzzer App: Zero idle time --- .../pacs_fuzzer/lib/worker/fake_worker.c | 32 +-- .../pacs_fuzzer/lib/worker/fake_worker.h | 5 +- .../pacs_fuzzer/lib/worker/protocol.c | 8 +- .../pacs_fuzzer/lib/worker/protocol.h | 8 +- .../pacs_fuzzer/lib/worker/protocol_i.h | 10 +- .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 5 +- applications/external/pacs_fuzzer/todo.md | 3 +- .../external/pacs_fuzzer/views/attack.c | 217 ++++++++++++++++-- .../external/pacs_fuzzer/views/attack.h | 4 +- 9 files changed, 237 insertions(+), 55 deletions(-) diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index 8960883085..e48b1dd32e 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -38,8 +38,8 @@ struct FuzzerWorker { const FuzzerProtocol* protocol; FuzzerWorkerAttackType attack_type; - uint8_t timer_idle_delay; - uint8_t timer_emu_delay; + uint8_t timer_idle_time; + uint8_t timer_emu_time; uint8_t payload[MAX_PAYLOAD_SIZE]; Stream* uids_stream; @@ -157,7 +157,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { #endif } instance->in_emu_phase = false; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_delay * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time * 100)); } else { if(!fuzzer_worker_load_key(instance, true)) { fuzzer_worker_pause(instance); // XXX @@ -173,7 +173,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { #endif } instance->in_emu_phase = true; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_delay * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time * 100)); if(instance->tick_callback) { instance->tick_callback(instance->tick_context); } @@ -349,8 +349,8 @@ FuzzerWorker* fuzzer_worker_alloc() { memset(instance->payload, 0x00, sizeof(instance->payload)); - instance->timer_idle_delay = PROTOCOL_MIN_IDLE_DELAY; - instance->timer_emu_delay = PROTOCOL_MIN_IDLE_DELAY; + instance->timer_idle_time = PROTOCOL_DEF_IDLE_TIME; + instance->timer_emu_time = PROTOCOL_DEF_EMU_TIME; instance->timer = furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypeOnce, instance); @@ -379,19 +379,21 @@ void fuzzer_worker_free(FuzzerWorker* instance) { free(instance); } -bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_time) { furi_assert(instance); if(instance->attack_type < FuzzerWorkerAttackTypeMax) { - uint8_t temp = timer_dellay / 2; - instance->timer_emu_delay = temp; - instance->timer_idle_delay = temp + timer_dellay % 2; + // if(emu_time == 0) { + // uint8_t temp = idle_time / 2; + // instance->timer_emu_time = temp; + // instance->timer_idle_time = temp + idle_time % 2; + // } else { + instance->timer_idle_time = idle_time; + instance->timer_emu_time = emu_time; + // } FURI_LOG_D( - TAG, - "Emu_delay %u Idle_delay %u", - instance->timer_emu_delay, - instance->timer_idle_delay); + TAG, "Emu_time %u Idle_time %u", instance->timer_emu_time, instance->timer_idle_time); if(!instance->treead_running) { #if defined(RFID_125_PROTOCOL) @@ -413,7 +415,7 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay) { ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif instance->in_emu_phase = true; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_delay * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time * 100)); return true; } return false; diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 04635169b3..6396525bef 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -35,10 +35,11 @@ void fuzzer_worker_free(FuzzerWorker* instance); * Start or continue emulation * * @param instance Pointer to a FuzzerWorker - * @param timer_dellay Emulation time of one UID in tenths of a second + * @param idle_time Delay between emulations in tenths of a second + * @param emu_time Emulation time of one UID in tenths of a second * @return bool True if emulation has started */ -bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t timer_dellay); +bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_time); /** * Stop emulation and deinit worker diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index fb76519017..f520037ac0 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -254,8 +254,12 @@ uint8_t fuzzer_proto_get_max_data_size() { return MAX_PAYLOAD_SIZE; } -uint8_t fuzzer_proto_get_min_delay() { - return PROTOCOL_TIME_DELAY_MIN; +uint8_t fuzzer_proto_get_def_emu_time() { + return PROTOCOL_DEF_EMU_TIME; +} + +uint8_t fuzzer_proto_get_def_idle_time() { + return PROTOCOL_DEF_IDLE_TIME; } const char* fuzzer_proto_get_menu_label(uint8_t index) { diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index 62ce88d5cc..68632b0292 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -37,11 +37,9 @@ struct FuzzerPayload { */ uint8_t fuzzer_proto_get_max_data_size(); -/** - * Get minimum time delay for protocols - * @return Minimum time delay - */ -uint8_t fuzzer_proto_get_min_delay(); +// TODO add description +uint8_t fuzzer_proto_get_def_emu_time(); +uint8_t fuzzer_proto_get_def_idle_time(); /** * Get protocol name based on its index diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h index 793b3e043e..074c50d9d9 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -4,12 +4,14 @@ #if defined(RFID_125_PROTOCOL) #define MAX_PAYLOAD_SIZE (6) -#define PROTOCOL_MIN_IDLE_DELAY (5) -#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_MIN_IDLE_DELAY + 4 +#define PROTOCOL_DEF_IDLE_TIME (4) +#define PROTOCOL_DEF_EMU_TIME (5) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME #else #define MAX_PAYLOAD_SIZE (8) -#define PROTOCOL_MIN_IDLE_DELAY (2) -#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_MIN_IDLE_DELAY + 2 +#define PROTOCOL_DEF_IDLE_TIME (2) +#define PROTOCOL_DEF_EMU_TIME (2) +#define PROTOCOL_TIME_DELAY_MIN PROTOCOL_DEF_IDLE_TIME + PROTOCOL_DEF_EMU_TIME #endif typedef struct ProtoDict ProtoDict; diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index 36734495b4..836bbdef5e 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -127,8 +127,11 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == FuzzerAttackStateIdle) { // Start or Continue Attack + // TODO emu_time if(fuzzer_worker_start( - app->worker, fuzzer_view_attack_get_time_delay(app->attack_view))) { + app->worker, + fuzzer_view_attack_get_time_delay(app->attack_view), + fuzzer_view_attack_get_emu_time(app->attack_view))) { fuzzer_scene_attack_set_state(app, FuzzerAttackStateRunning); } else { // Error? diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 823e2f05ab..d0bab30d64 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -27,7 +27,8 @@ - [x] Description and buttons in `field_editor` view - [ ] Protocol carousel in `main_menu` - [x] prototype - - [ ] Add the ability to edit emulation time and downtime separately + - [x] Add the ability to edit emulation time and downtime separately + - [ ] Decide on the display - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] `UID_MAX_SIZE` diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 1df6d5eb31..a29e2d966f 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -4,8 +4,13 @@ #include #include +#define ATACK_VIEW_V2 +// #define ATACK_VIEW_V2_1 +#define ATACK_VIEW_V2_2 + #define ATTACK_SCENE_MAX_UID_LENGTH 25 #define UID_MAX_DISPLAYED_LEN (8U) +#define LIFT_RIGHT_OFFSET (3) struct FuzzerViewAttack { View* view; @@ -14,8 +19,11 @@ struct FuzzerViewAttack { }; typedef struct { - uint8_t time_delay; - uint8_t time_delay_min; + uint8_t time_delay; // 1 = 100ms + uint8_t time_delay_min; // 1 = 100ms + uint8_t emu_time; // 1 = 100ms + uint8_t emu_time_min; // 1 = 100ms + bool td_emt_cursor; // false - time_delay, true - emu_time const char* attack_name; const char* protocol_name; FuzzerAttackState attack_state; @@ -107,8 +115,7 @@ void fuzzer_view_attack_set_callback( } void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { - char time_delay[16]; - snprintf(time_delay, sizeof(time_delay), "Time delay: %d", model->time_delay); + char temp_str[50]; canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); @@ -116,8 +123,101 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->attack_name); +#ifndef ATACK_VIEW_V2 + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, + sizeof(temp_str), + "Time delay: %d.%d", + model->time_delay / 10, + model->time_delay % 10); + canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, temp_str); +#elif defined(ATACK_VIEW_V2_1) + + canvas_set_font(canvas, FontSecondary); + if(!model->td_emt_cursor) { + snprintf( + temp_str, + sizeof(temp_str), + "Time delay: %d.%d EmT: %d.%d", + model->time_delay / 10, + model->time_delay % 10, + model->emu_time / 10, + model->emu_time % 10); + } else { + snprintf( + temp_str, + sizeof(temp_str), + "TD: %d.%d Emulation time: %d.%d", + model->time_delay / 10, + model->time_delay % 10, + model->emu_time / 10, + model->emu_time % 10); + } + canvas_draw_str_aligned(canvas, 64, 21, AlignCenter, AlignBottom, temp_str); + +#elif defined(ATACK_VIEW_V2_2) + + uint16_t crt; + canvas_set_font(canvas, FontPrimary); + + if(!model->td_emt_cursor) { + canvas_set_font(canvas, FontSecondary); + snprintf(temp_str, sizeof(temp_str), "Time delay:"); + canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str); + crt = canvas_string_width(canvas, temp_str); + + canvas_set_font(canvas, FontPrimary); + snprintf( + temp_str, sizeof(temp_str), "%d.%d", model->time_delay / 10, model->time_delay % 10); + canvas_draw_str_aligned( + canvas, crt + LIFT_RIGHT_OFFSET + 3, 21, AlignLeft, AlignBottom, temp_str); + + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, sizeof(temp_str), "EmT: %d.%d", model->emu_time / 10, model->emu_time % 10); + canvas_draw_str_aligned( + canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str); + + } else { + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, + sizeof(temp_str), + "TD: %d.%d", + model->time_delay / 10, + model->time_delay % 10); + + canvas_draw_str_aligned(canvas, LIFT_RIGHT_OFFSET, 21, AlignLeft, AlignBottom, temp_str); + + canvas_set_font(canvas, FontPrimary); + snprintf(temp_str, sizeof(temp_str), "%d.%d", model->emu_time / 10, model->emu_time % 10); + canvas_draw_str_aligned( + canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str); + crt = canvas_string_width(canvas, temp_str); + + canvas_set_font(canvas, FontSecondary); + snprintf(temp_str, sizeof(temp_str), "Emulation time:"); + canvas_draw_str_aligned( + canvas, 128 - LIFT_RIGHT_OFFSET - crt - 3, 21, AlignRight, AlignBottom, temp_str); + } + +#else + + canvas_set_font(canvas, FontSecondary); + snprintf( + temp_str, + sizeof(temp_str), + "Time delay: %d.%d Emu time: %d.%d", + model->time_delay / 10, + model->time_delay % 10, + model->emu_time / 10, + model->emu_time % 10); + canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, temp_str); + +#endif + canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, time_delay); canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name); canvas_set_font(canvas, FontPrimary); @@ -131,9 +231,21 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { if(model->attack_state == FuzzerAttackStateRunning) { elements_button_center(canvas, "Stop"); } else if(model->attack_state == FuzzerAttackStateIdle) { +#ifndef ATACK_VIEW_V2 elements_button_center(canvas, "Start"); elements_button_left(canvas, "TD -"); elements_button_right(canvas, "+ TD"); +#else + if(model->td_emt_cursor) { + elements_button_center(canvas, "Start"); + elements_button_left(canvas, "EmT -"); + elements_button_right(canvas, "+ EmT"); + } else { + elements_button_center(canvas, "Start"); + elements_button_left(canvas, "TD -"); + elements_button_right(canvas, "+ TD"); + } +#endif } else if(model->attack_state == FuzzerAttackStateEnd) { // elements_button_center(canvas, "Restart"); // Reset elements_button_left(canvas, "Exit"); @@ -156,16 +268,31 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { FuzzerViewAttackModel * model, { if(model->attack_state == FuzzerAttackStateIdle) { - // TimeDelay - if(event->type == InputTypeShort) { - if(model->time_delay > model->time_delay_min) { - model->time_delay--; + if(!model->td_emt_cursor) { + // TimeDelay -- + if(event->type == InputTypeShort) { + if(model->time_delay > model->time_delay_min) { + model->time_delay--; + } + } else if(event->type == InputTypeLong) { + if((model->time_delay - 10) >= model->time_delay_min) { + model->time_delay -= 10; + } else { + model->time_delay = model->time_delay_min; + } } - } else if(event->type == InputTypeLong) { - if((model->time_delay - 10) >= model->time_delay_min) { - model->time_delay -= 10; - } else { - model->time_delay = model->time_delay_min; + } else { + // EmuTime -- + if(event->type == InputTypeShort) { + if(model->emu_time > model->emu_time_min) { + model->emu_time--; + } + } else if(event->type == InputTypeLong) { + if((model->emu_time - 10) >= model->emu_time_min) { + model->emu_time -= 10; + } else { + model->emu_time = model->emu_time_min; + } } } } else if( @@ -183,15 +310,29 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { FuzzerViewAttackModel * model, { if(model->attack_state == FuzzerAttackStateIdle) { - // TimeDelay - if(event->type == InputTypeShort) { - if(model->time_delay < FUZZ_TIME_DELAY_MAX) { - model->time_delay++; + if(!model->td_emt_cursor) { + // TimeDelay ++ + if(event->type == InputTypeShort) { + if(model->time_delay < FUZZ_TIME_DELAY_MAX) { + model->time_delay++; + } + } else if(event->type == InputTypeLong) { + model->time_delay += 10; + if(model->time_delay > FUZZ_TIME_DELAY_MAX) { + model->time_delay = FUZZ_TIME_DELAY_MAX; + } } - } else if(event->type == InputTypeLong) { - model->time_delay += 10; - if(model->time_delay > FUZZ_TIME_DELAY_MAX) { - model->time_delay = FUZZ_TIME_DELAY_MAX; + } else { + // EmuTime ++ + if(event->type == InputTypeShort) { + if(model->emu_time < FUZZ_TIME_DELAY_MAX) { + model->emu_time++; + } + } else if(event->type == InputTypeLong) { + model->emu_time += 10; + if(model->emu_time > FUZZ_TIME_DELAY_MAX) { + model->emu_time = FUZZ_TIME_DELAY_MAX; + } } } } else { @@ -200,6 +341,15 @@ bool fuzzer_view_attack_input(InputEvent* event, void* context) { }, true); return true; + } else if( + (event->key == InputKeyUp || event->key == InputKeyDown) && + event->type == InputTypeShort) { + with_view_model( + view_attack->view, + FuzzerViewAttackModel * model, + { model->td_emt_cursor = !model->td_emt_cursor; }, + true); + return true; } return true; @@ -211,6 +361,9 @@ void fuzzer_view_attack_enter(void* context) { void fuzzer_view_attack_exit(void* context) { furi_assert(context); + FuzzerViewAttack* view_attack = context; + with_view_model( + view_attack->view, FuzzerViewAttackModel * model, { model->td_emt_cursor = false; }, true); } FuzzerViewAttack* fuzzer_view_attack_alloc() { @@ -233,11 +386,17 @@ FuzzerViewAttack* fuzzer_view_attack_alloc() { view_attack->view, FuzzerViewAttackModel * model, { - model->time_delay_min = fuzzer_proto_get_min_delay(); - model->time_delay = model->time_delay_min; + model->time_delay = fuzzer_proto_get_def_idle_time(); + model->time_delay_min = 0; // model->time_delay; + + model->emu_time = fuzzer_proto_get_def_emu_time(); + + model->emu_time_min = 2; // model->emu_time; + model->uid_str = furi_string_alloc_set_str("Not_set"); // malloc(ATTACK_SCENE_MAX_UID_LENGTH + 1); model->attack_state = FuzzerAttackStateOff; + model->td_emt_cursor = false; // strcpy(model->uid_str, "Not_set"); model->attack_name = "Not_set"; @@ -272,4 +431,14 @@ uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view) { view->view, FuzzerViewAttackModel * model, { time_delay = model->time_delay; }, false); return time_delay; +} + +uint8_t fuzzer_view_attack_get_emu_time(FuzzerViewAttack* view) { + furi_assert(view); + uint8_t emu_time; + + with_view_model( + view->view, FuzzerViewAttackModel * model, { emu_time = model->emu_time; }, false); + + return emu_time; } \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index 41fd857bfa..9341ae7e2b 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -37,4 +37,6 @@ void fuzzer_view_attack_pause(FuzzerViewAttack* view); void fuzzer_view_attack_end(FuzzerViewAttack* view); -uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view); \ No newline at end of file +uint8_t fuzzer_view_attack_get_time_delay(FuzzerViewAttack* view); + +uint8_t fuzzer_view_attack_get_emu_time(FuzzerViewAttack* view); \ No newline at end of file From d86eb870d4651e463241e465bf3198dd21c52579 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:28:26 +0300 Subject: [PATCH 070/102] pre-merge fix --- lib/subghz/SConscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 8fbec94ad9..3a0325b71d 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -7,11 +7,10 @@ env.Append( SDK_HEADERS=[ File("environment.h"), File("receiver.h"), + File("registry.h"), File("subghz_worker.h"), File("subghz_tx_rx_worker.h"), File("transmitter.h"), - File("registry.h"), - File("protocols/protocol_items.h"), File("protocols/raw.h"), File("blocks/const.h"), File("blocks/decoder.h"), @@ -19,6 +18,7 @@ env.Append( File("blocks/generic.h"), File("blocks/math.h"), File("subghz_setting.h"), + File("subghz_protocol_registry.h"), ], ) From ac15621e74c69bc390c46fc2b71549b57abaa35c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:41:14 +0300 Subject: [PATCH 071/102] fix API --- firmware/targets/f7/api_symbols.csv | 636 ---------------------------- 1 file changed, 636 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3f501e8058..2950b419b6 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -186,7 +186,6 @@ Header,+,lib/subghz/blocks/encoder.h,, Header,+,lib/subghz/blocks/generic.h,, Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, -Header,+,lib/subghz/protocols/protocol_items.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, @@ -548,8 +547,6 @@ Function,-,atoff,float,const char* Function,+,atoi,int,const char* Function,-,atol,long,const char* Function,-,atoll,long long,const char* -Function,-,atomo_decrypt,void,uint8_t* -Function,-,atomo_encrypt,void,uint8_t* Function,-,basename,char*,const char* Function,-,bcmp,int,"const void*, const void*, size_t" Function,-,bcopy,void,"const void*, void*, size_t" @@ -2733,7 +2730,6 @@ Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, u Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" Function,-,subghz_keystore_reset_kl,void,SubGhzKeystore* Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" -Function,-,subghz_protocol_alutech_at_4n_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_add_to_128_bit,void,"SubGhzBlockDecoder*, uint8_t, uint64_t*" @@ -2755,547 +2751,22 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" -Function,-,subghz_protocol_came_atomo_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_alutech_at_4n_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_alutech_at_4n_free,void,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_alutech_at_4n_reset,void,void* -Function,-,subghz_protocol_decoder_alutech_at_4n_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ansonic_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ansonic_free,void,void* -Function,-,subghz_protocol_decoder_ansonic_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ansonic_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ansonic_reset,void,void* -Function,-,subghz_protocol_decoder_ansonic_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_base_deserialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" Function,+,subghz_protocol_decoder_base_serialize,SubGhzProtocolStatus,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" -Function,-,subghz_protocol_decoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_bett_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_bett_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_bett_free,void,void* -Function,-,subghz_protocol_decoder_bett_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_bett_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_bett_reset,void,void* -Function,-,subghz_protocol_decoder_bett_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_bin_raw_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_bin_raw_data_input_rssi,void,"SubGhzProtocolDecoderBinRAW*, float" -Function,-,subghz_protocol_decoder_bin_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_bin_raw_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_bin_raw_free,void,void* -Function,-,subghz_protocol_decoder_bin_raw_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_bin_raw_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_bin_raw_reset,void,void* -Function,-,subghz_protocol_decoder_bin_raw_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_atomo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_atomo_free,void,void* -Function,-,subghz_protocol_decoder_came_atomo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_atomo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_atomo_reset,void,void* -Function,-,subghz_protocol_decoder_came_atomo_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_free,void,void* -Function,-,subghz_protocol_decoder_came_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_reset,void,void* -Function,-,subghz_protocol_decoder_came_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_twee_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_twee_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_twee_free,void,void* -Function,-,subghz_protocol_decoder_came_twee_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_twee_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_twee_reset,void,void* -Function,-,subghz_protocol_decoder_came_twee_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_chamb_code_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_chamb_code_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_chamb_code_free,void,void* -Function,-,subghz_protocol_decoder_chamb_code_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_chamb_code_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_chamb_code_reset,void,void* -Function,-,subghz_protocol_decoder_chamb_code_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_clemsa_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_clemsa_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_clemsa_free,void,void* -Function,-,subghz_protocol_decoder_clemsa_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_clemsa_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_clemsa_reset,void,void* -Function,-,subghz_protocol_decoder_clemsa_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_doitrand_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_doitrand_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_doitrand_free,void,void* -Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_doitrand_reset,void,void* -Function,-,subghz_protocol_decoder_doitrand_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_dooya_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_dooya_free,void,void* -Function,-,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_dooya_reset,void,void* -Function,-,subghz_protocol_decoder_dooya_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_faac_slh_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_faac_slh_free,void,void* -Function,-,subghz_protocol_decoder_faac_slh_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_faac_slh_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_faac_slh_reset,void,void* -Function,-,subghz_protocol_decoder_faac_slh_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_gate_tx_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_gate_tx_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_gate_tx_free,void,void* -Function,-,subghz_protocol_decoder_gate_tx_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_gate_tx_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_gate_tx_reset,void,void* -Function,-,subghz_protocol_decoder_gate_tx_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_free,void,void* -Function,-,subghz_protocol_decoder_holtek_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_th12x_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_th12x_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_th12x_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_honeywell_wdb_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_honeywell_wdb_reset,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_hormann_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_hormann_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_hormann_free,void,void* -Function,-,subghz_protocol_decoder_hormann_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_hormann_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_hormann_reset,void,void* -Function,-,subghz_protocol_decoder_hormann_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ido_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ido_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ido_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ido_free,void,void* -Function,-,subghz_protocol_decoder_ido_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ido_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ido_reset,void,void* -Function,-,subghz_protocol_decoder_ido_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_intertechno_v3_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_intertechno_v3_reset,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_keeloq_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_keeloq_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_keeloq_free,void,void* -Function,-,subghz_protocol_decoder_keeloq_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_keeloq_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_keeloq_reset,void,void* -Function,-,subghz_protocol_decoder_keeloq_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_kia_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kia_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_kia_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_kia_free,void,void* -Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_kia_reset,void,void* -Function,-,subghz_protocol_decoder_kia_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* -Function,-,subghz_protocol_decoder_kinggates_stylo_4k_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_linear_delta3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_linear_delta3_free,void,void* -Function,-,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_linear_delta3_reset,void,void* -Function,-,subghz_protocol_decoder_linear_delta3_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_linear_free,void,void* -Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_linear_reset,void,void* -Function,-,subghz_protocol_decoder_linear_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_magellan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_magellan_free,void,void* -Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_magellan_reset,void,void* -Function,-,subghz_protocol_decoder_magellan_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_marantec_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_marantec_free,void,void* -Function,-,subghz_protocol_decoder_marantec_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_marantec_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_marantec_reset,void,void* -Function,-,subghz_protocol_decoder_marantec_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_megacode_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_megacode_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_megacode_free,void,void* -Function,-,subghz_protocol_decoder_megacode_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_megacode_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_megacode_reset,void,void* -Function,-,subghz_protocol_decoder_megacode_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_radio_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_radio_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_radio_free,void,void* -Function,-,subghz_protocol_decoder_nero_radio_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_radio_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_radio_reset,void,void* -Function,-,subghz_protocol_decoder_nero_radio_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_sketch_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_sketch_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_sketch_reset,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flo_free,void,void* -Function,-,subghz_protocol_decoder_nice_flo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flo_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flo_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flor_s_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flor_s_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_phoenix_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_phoenix_v2_reset,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_power_smart_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_power_smart_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_power_smart_free,void,void* -Function,-,subghz_protocol_decoder_power_smart_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_power_smart_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_power_smart_reset,void,void* -Function,-,subghz_protocol_decoder_power_smart_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_princeton_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_princeton_free,void,void* -Function,-,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_princeton_reset,void,void* -Function,-,subghz_protocol_decoder_princeton_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" Function,+,subghz_protocol_decoder_raw_free,void,void* Function,+,subghz_protocol_decoder_raw_get_string,void,"void*, FuriString*" Function,+,subghz_protocol_decoder_raw_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_scher_khan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_scher_khan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_scher_khan_free,void,void* -Function,-,subghz_protocol_decoder_scher_khan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_scher_khan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_scher_khan_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v1_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v1_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v1_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v2_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_smc5326_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_smc5326_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_smc5326_free,void,void* -Function,-,subghz_protocol_decoder_smc5326_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_smc5326_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_smc5326_reset,void,void* -Function,-,subghz_protocol_decoder_smc5326_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_keytis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_keytis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_keytis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_telis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_telis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_telis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_telis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_star_line_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_star_line_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_star_line_free,void,void* -Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_star_line_reset,void,void* -Function,-,subghz_protocol_decoder_star_line_serialize,SubGhzProtocolStatus,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_encoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_alutech_at_4n_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_alutech_at_4n_free,void,void* -Function,-,subghz_protocol_encoder_alutech_at_4n_stop,void,void* -Function,-,subghz_protocol_encoder_alutech_at_4n_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_ansonic_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_ansonic_free,void,void* -Function,-,subghz_protocol_encoder_ansonic_stop,void,void* -Function,-,subghz_protocol_encoder_ansonic_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bett_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_bett_free,void,void* -Function,-,subghz_protocol_encoder_bett_stop,void,void* -Function,-,subghz_protocol_encoder_bett_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_bin_raw_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bin_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_bin_raw_free,void,void* -Function,-,subghz_protocol_encoder_bin_raw_stop,void,void* -Function,-,subghz_protocol_encoder_bin_raw_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_atomo_free,void,void* -Function,-,subghz_protocol_encoder_came_atomo_stop,void,void* -Function,-,subghz_protocol_encoder_came_atomo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_free,void,void* -Function,-,subghz_protocol_encoder_came_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_twee_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_twee_free,void,void* -Function,-,subghz_protocol_encoder_came_twee_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_chamb_code_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_chamb_code_free,void,void* -Function,-,subghz_protocol_encoder_chamb_code_stop,void,void* -Function,-,subghz_protocol_encoder_chamb_code_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_clemsa_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_clemsa_free,void,void* -Function,-,subghz_protocol_encoder_clemsa_stop,void,void* -Function,-,subghz_protocol_encoder_clemsa_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_doitrand_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_doitrand_free,void,void* -Function,-,subghz_protocol_encoder_doitrand_stop,void,void* -Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_dooya_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_dooya_free,void,void* -Function,-,subghz_protocol_encoder_dooya_stop,void,void* -Function,-,subghz_protocol_encoder_dooya_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_faac_slh_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_faac_slh_free,void,void* -Function,-,subghz_protocol_encoder_faac_slh_stop,void,void* -Function,-,subghz_protocol_encoder_faac_slh_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_gate_tx_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_gate_tx_free,void,void* -Function,-,subghz_protocol_encoder_gate_tx_stop,void,void* -Function,-,subghz_protocol_encoder_gate_tx_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_free,void,void* -Function,-,subghz_protocol_encoder_holtek_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_th12x_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_holtek_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_honeywell_wdb_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_stop,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_hormann_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_hormann_free,void,void* -Function,-,subghz_protocol_encoder_hormann_stop,void,void* -Function,-,subghz_protocol_encoder_hormann_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_intertechno_v3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_stop,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_keeloq_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_keeloq_free,void,void* -Function,-,subghz_protocol_encoder_keeloq_stop,void,void* -Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_free,void,void* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_stop,void,void* -Function,-,subghz_protocol_encoder_kinggates_stylo_4k_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_linear_delta3_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_linear_delta3_free,void,void* -Function,-,subghz_protocol_encoder_linear_delta3_stop,void,void* -Function,-,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_linear_free,void,void* -Function,-,subghz_protocol_encoder_linear_stop,void,void* -Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_magellan_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_magellan_free,void,void* -Function,-,subghz_protocol_encoder_magellan_stop,void,void* -Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_marantec_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_marantec_free,void,void* -Function,-,subghz_protocol_encoder_marantec_stop,void,void* -Function,-,subghz_protocol_encoder_marantec_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_megacode_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_megacode_free,void,void* -Function,-,subghz_protocol_encoder_megacode_stop,void,void* -Function,-,subghz_protocol_encoder_megacode_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_radio_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_radio_free,void,void* -Function,-,subghz_protocol_encoder_nero_radio_stop,void,void* -Function,-,subghz_protocol_encoder_nero_radio_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_sketch_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_stop,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flo_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flo_free,void,void* -Function,-,subghz_protocol_encoder_nice_flo_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flor_s_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_phoenix_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_stop,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_power_smart_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_power_smart_free,void,void* -Function,-,subghz_protocol_encoder_power_smart_stop,void,void* -Function,-,subghz_protocol_encoder_power_smart_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_princeton_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_princeton_free,void,void* -Function,-,subghz_protocol_encoder_princeton_stop,void,void* -Function,-,subghz_protocol_encoder_princeton_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_encoder_raw_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v1_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v2_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_smc5326_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_smc5326_free,void,void* -Function,-,subghz_protocol_encoder_smc5326_stop,void,void* -Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_somfy_keytis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_somfy_keytis_free,void,void* -Function,-,subghz_protocol_encoder_somfy_keytis_stop,void,void* -Function,-,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_somfy_telis_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_somfy_telis_free,void,void* -Function,-,subghz_protocol_encoder_somfy_telis_stop,void,void* -Function,-,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_star_line_deserialize,SubGhzProtocolStatus,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_star_line_free,void,void* -Function,-,subghz_protocol_encoder_star_line_stop,void,void* -Function,-,subghz_protocol_encoder_star_line_yield,LevelDuration,void* -Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*, _Bool" -Function,-,subghz_protocol_nice_flor_s_encrypt,uint64_t,"uint64_t, const char*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* @@ -3305,11 +2776,6 @@ Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" -Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t -Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_somfy_keytis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_somfy_telis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* @@ -3962,108 +3428,6 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, -Variable,-,subghz_protocol_alutech_at_4n,const SubGhzProtocol, -Variable,-,subghz_protocol_alutech_at_4n_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_alutech_at_4n_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, -Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_bett,const SubGhzProtocol, -Variable,-,subghz_protocol_bett_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_bett_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_bin_raw,const SubGhzProtocol, -Variable,-,subghz_protocol_bin_raw_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_bin_raw_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_atomo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_twee,const SubGhzProtocol, -Variable,-,subghz_protocol_came_twee_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_twee_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_chamb_code,const SubGhzProtocol, -Variable,-,subghz_protocol_chamb_code_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_chamb_code_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_clemsa,const SubGhzProtocol, -Variable,-,subghz_protocol_clemsa_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_clemsa_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_doitrand,const SubGhzProtocol, -Variable,-,subghz_protocol_doitrand_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_doitrand_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_dooya,const SubGhzProtocol, -Variable,-,subghz_protocol_dooya_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_dooya_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_faac_slh,const SubGhzProtocol, -Variable,-,subghz_protocol_faac_slh_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_faac_slh_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_gate_tx,const SubGhzProtocol, -Variable,-,subghz_protocol_gate_tx_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_gate_tx_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek_th12x,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_th12x_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_th12x_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_honeywell_wdb,const SubGhzProtocol, -Variable,-,subghz_protocol_honeywell_wdb_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_honeywell_wdb_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_hormann,const SubGhzProtocol, -Variable,-,subghz_protocol_hormann_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_hormann_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ido,const SubGhzProtocol, -Variable,-,subghz_protocol_ido_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ido_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_intertechno_v3,const SubGhzProtocol, -Variable,-,subghz_protocol_intertechno_v3_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_intertechno_v3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_keeloq,const SubGhzProtocol, -Variable,-,subghz_protocol_keeloq_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_keeloq_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_kia,const SubGhzProtocol, -Variable,-,subghz_protocol_kia_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_kinggates_stylo_4k,const SubGhzProtocol, -Variable,-,subghz_protocol_kinggates_stylo_4k_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_kinggates_stylo_4k_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear,const SubGhzProtocol, -Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_linear_delta3,const SubGhzProtocol, -Variable,-,subghz_protocol_linear_delta3_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_linear_delta3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_magellan,const SubGhzProtocol, -Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_magellan_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_marantec,const SubGhzProtocol, -Variable,-,subghz_protocol_marantec_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_marantec_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_megacode,const SubGhzProtocol, -Variable,-,subghz_protocol_megacode_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_megacode_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_radio,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_radio_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_radio_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_sketch,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_sketch_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_sketch_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flo,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flor_s,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flor_s_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flor_s_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_phoenix_v2,const SubGhzProtocol, -Variable,-,subghz_protocol_phoenix_v2_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_phoenix_v2_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_power_smart,const SubGhzProtocol, -Variable,-,subghz_protocol_power_smart_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_power_smart_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_princeton,const SubGhzProtocol, -Variable,-,subghz_protocol_princeton_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, From 58338ff51f6f9857f39ef07d5eb4495cdc02290d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:59:19 +0300 Subject: [PATCH 072/102] Fix RGB patch --- .ci_files/rgb.patch | 42 ++++++++++++------------------------------ 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/.ci_files/rgb.patch b/.ci_files/rgb.patch index 804034bab3..b5141abb9b 100644 --- a/.ci_files/rgb.patch +++ b/.ci_files/rgb.patch @@ -1,8 +1,8 @@ diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c -index f91a73f32..b559a79ad 100644 +index 2f947fe..03c4c76 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c -@@ -6,6 +6,7 @@ +@@ -9,6 +9,7 @@ #include "notification.h" #include "notification_messages.h" #include "notification_app.h" @@ -10,7 +10,7 @@ index f91a73f32..b559a79ad 100644 #define TAG "NotificationSrv" -@@ -564,6 +565,7 @@ int32_t notification_srv(void* p) { +@@ -579,6 +580,7 @@ int32_t notification_srv(void* p) { break; case SaveSettingsMessage: notification_save_settings(app); @@ -19,7 +19,7 @@ index f91a73f32..b559a79ad 100644 } diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c -index f5d7a82ca..930c0bd1f 100644 +index 565d4f1..bae9299 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -3,6 +3,7 @@ @@ -30,15 +30,7 @@ index f5d7a82ca..930c0bd1f 100644 #define MAX_NOTIFICATION_SETTINGS 4 -@@ -73,7 +74,6 @@ const bool vibro_value[VIBRO_COUNT] = {false, true}; - static void backlight_changed(VariableItem* item) { - NotificationAppSettings* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); -- - variable_item_set_current_value_text(item, backlight_text[index]); - app->notification->settings.display_brightness = backlight_value[index]; - notification_message(app->notification, &sequence_display_backlight_on); -@@ -125,6 +125,14 @@ static void vibro_changed(VariableItem* item) { +@@ -162,6 +163,14 @@ static void vibro_changed(VariableItem* item) { notification_message(app->notification, &sequence_single_vibro); } @@ -53,8 +45,8 @@ index f5d7a82ca..930c0bd1f 100644 static uint32_t notification_app_settings_exit(void* context) { UNUSED(context); return VIEW_NONE; -@@ -143,7 +151,13 @@ static NotificationAppSettings* alloc_settings() { - uint8_t value_index; +@@ -187,7 +196,13 @@ static NotificationAppSettings* alloc_settings() { + variable_item_set_current_value_text(item, contrast_text[value_index]); item = variable_item_list_add( - app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app); @@ -68,17 +60,9 @@ index f5d7a82ca..930c0bd1f 100644 value_index = value_index_float( app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT); variable_item_set_current_value_index(item, value_index); -@@ -215,6 +229,7 @@ int32_t notification_settings_app(void* p) { - NotificationAppSettings* app = alloc_settings(); - view_dispatcher_run(app->view_dispatcher); - notification_message_save_settings(app->notification); -+ - free_settings(app); - return 0; - } diff --git a/applications/settings/notification_settings/rgb_backlight.c b/applications/settings/notification_settings/rgb_backlight.c new file mode 100644 -index 000000000..269b544ae +index 0000000..269b544 --- /dev/null +++ b/applications/settings/notification_settings/rgb_backlight.c @@ -0,0 +1,171 @@ @@ -255,7 +239,7 @@ index 000000000..269b544ae +} diff --git a/applications/settings/notification_settings/rgb_backlight.h b/applications/settings/notification_settings/rgb_backlight.h new file mode 100644 -index 000000000..b63d223e6 +index 0000000..b63d223 --- /dev/null +++ b/applications/settings/notification_settings/rgb_backlight.h @@ -0,0 +1,79 @@ @@ -340,7 +324,7 @@ index 000000000..b63d223e6 +const char* rgb_backlight_get_color_text(uint8_t index); \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c -index 83e1603b7..cad5b86cb 100644 +index 83e1603..cad5b86 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -3,6 +3,7 @@ @@ -389,7 +373,7 @@ index 83e1603b7..cad5b86cb 100644 void furi_hal_light_blink_start(Light light, uint8_t brightness, uint16_t on_time, uint16_t period) { diff --git a/lib/drivers/SK6805.c b/lib/drivers/SK6805.c new file mode 100644 -index 000000000..572e1df97 +index 0000000..572e1df --- /dev/null +++ b/lib/drivers/SK6805.c @@ -0,0 +1,101 @@ @@ -496,7 +480,7 @@ index 000000000..572e1df97 +} diff --git a/lib/drivers/SK6805.h b/lib/drivers/SK6805.h new file mode 100644 -index 000000000..7c58956fa +index 0000000..7c58956 --- /dev/null +++ b/lib/drivers/SK6805.h @@ -0,0 +1,51 @@ @@ -552,5 +536,3 @@ index 000000000..7c58956fa + +#endif /* SK6805_H_ */ \ No newline at end of file - -\ No newline at end of file From 754e640c8d2cdcb45e6420d549452251c6e171da Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 7 Jun 2023 06:14:33 +0400 Subject: [PATCH 073/102] [FL-3246] fbt, ufbt: added checks for appid in app manifests(#2720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- scripts/fbt/appmanifest.py | 9 ++++++++- scripts/ufbt/SConstruct | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index eb265cee86..820f5a8c55 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -1,7 +1,8 @@ import os +import re from dataclasses import dataclass, field from enum import Enum -from typing import Callable, List, Optional, Tuple, Union +from typing import Callable, ClassVar, List, Optional, Tuple, Union class FlipperManifestException(Exception): @@ -23,6 +24,8 @@ class FlipperAppType(Enum): @dataclass class FlipperApplication: + APP_ID_REGEX: ClassVar[re.Pattern] = re.compile(r"^[a-z0-9_]+$") + @dataclass class ExternallyBuiltFile: path: str @@ -84,6 +87,10 @@ def is_default_deployable(self): def __post_init__(self): if self.apptype == FlipperAppType.PLUGIN: self.stack_size = 0 + if not self.APP_ID_REGEX.match(self.appid): + raise FlipperManifestException( + f"Invalid appid '{self.appid}'. Must match regex '{self.APP_ID_REGEX}'" + ) if isinstance(self.fap_version, str): try: self.fap_version = tuple(int(v) for v in self.fap_version.split(".")) diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index d72de380c3..a1acd270a7 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -75,7 +75,7 @@ from fbt.util import ( wrap_tempfile, path_as_posix, ) -from fbt.appmanifest import FlipperAppType +from fbt.appmanifest import FlipperAppType, FlipperApplication from fbt.sdk.cache import SdkCache # Base environment with all tools loaded from SDK @@ -410,6 +410,12 @@ dist_env.Alias("vscode_dist", vscode_dist) # Creating app from base template dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template") +if fbt_appid := dist_env.subst("$FBT_APPID"): + if not FlipperApplication.APP_ID_REGEX.match(fbt_appid): + raise UserError( + f"Invalid app id '{fbt_appid}'. App id must match {FlipperApplication.APP_ID_REGEX.pattern}" + ) + app_template_dir = project_template_dir.Dir("app_template") app_template_dist = [] for template_file in app_template_dir.glob("*"): From 6ce098064aed2282437a5df8208f63ec2e263da5 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Wed, 7 Jun 2023 11:51:15 +0300 Subject: [PATCH 074/102] Fuzzer App: use FuzzerPayload & smal fixes --- applications/external/pacs_fuzzer/fuzzer.c | 2 + applications/external/pacs_fuzzer/fuzzer_i.h | 1 + .../pacs_fuzzer/lib/worker/fake_worker.c | 42 ++++++++++--------- .../pacs_fuzzer/lib/worker/fake_worker.h | 6 +-- .../pacs_fuzzer/lib/worker/protocol.c | 16 +++++++ .../pacs_fuzzer/lib/worker/protocol.h | 14 +++++++ .../pacs_fuzzer/lib/worker/protocol_i.h | 12 +----- .../pacs_fuzzer/scenes/fuzzer_scene_attack.c | 13 ++---- .../scenes/fuzzer_scene_field_editor.c | 11 ++--- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 9 ++-- applications/external/pacs_fuzzer/todo.md | 4 ++ .../external/pacs_fuzzer/views/attack.c | 10 ++--- .../external/pacs_fuzzer/views/attack.h | 2 +- .../external/pacs_fuzzer/views/field_editor.c | 20 +++++---- .../external/pacs_fuzzer/views/field_editor.h | 5 +-- 15 files changed, 96 insertions(+), 71 deletions(-) diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/pacs_fuzzer/fuzzer.c index c80c18130a..113291d0c4 100644 --- a/applications/external/pacs_fuzzer/fuzzer.c +++ b/applications/external/pacs_fuzzer/fuzzer.c @@ -26,6 +26,7 @@ PacsFuzzerApp* fuzzer_app_alloc() { app->fuzzer_state.proto_index = 0; app->worker = fuzzer_worker_alloc(); + app->payload = fuzzer_payload_alloc(); app->file_path = furi_string_alloc(); @@ -114,6 +115,7 @@ void fuzzer_app_free(PacsFuzzerApp* app) { furi_string_free(app->file_path); + fuzzer_payload_free(app->payload); fuzzer_worker_free(app->worker); free(app); diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/pacs_fuzzer/fuzzer_i.h index 63bf85d242..5b58e59d8b 100644 --- a/applications/external/pacs_fuzzer/fuzzer_i.h +++ b/applications/external/pacs_fuzzer/fuzzer_i.h @@ -51,4 +51,5 @@ typedef struct { FuzzerConsts* fuzzer_const; FuzzerWorker* worker; + FuzzerPayload* payload; } PacsFuzzerApp; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c index e48b1dd32e..07b0479b49 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.c @@ -38,8 +38,8 @@ struct FuzzerWorker { const FuzzerProtocol* protocol; FuzzerWorkerAttackType attack_type; - uint8_t timer_idle_time; - uint8_t timer_emu_time; + uint16_t timer_idle_time_ms; + uint16_t timer_emu_time_ms; uint8_t payload[MAX_PAYLOAD_SIZE]; Stream* uids_stream; @@ -157,7 +157,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { #endif } instance->in_emu_phase = false; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_idle_time_ms)); } else { if(!fuzzer_worker_load_key(instance, true)) { fuzzer_worker_pause(instance); // XXX @@ -173,7 +173,7 @@ static void fuzzer_worker_on_tick_callback(void* context) { #endif } instance->in_emu_phase = true; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms)); if(instance->tick_callback) { instance->tick_callback(instance->tick_context); } @@ -187,7 +187,6 @@ void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output furi_assert(instance->protocol); output_key->data_size = instance->protocol->data_size; - output_key->data = malloc(sizeof(output_key->data_size)); memcpy(output_key->data, instance->payload, instance->protocol->data_size); } @@ -258,7 +257,7 @@ bool fuzzer_worker_init_attack_file_dict( bool fuzzer_worker_init_attack_bf_byte( FuzzerWorker* instance, FuzzerProtocolsID protocol_index, - const uint8_t* uid, + const FuzzerPayload* new_uid, uint8_t chusen) { furi_assert(instance); @@ -268,7 +267,7 @@ bool fuzzer_worker_init_attack_bf_byte( instance->attack_type = FuzzerWorkerAttackTypeLoadFile; instance->index = chusen; - memcpy(instance->payload, uid, instance->protocol->data_size); + memcpy(instance->payload, new_uid->data, instance->protocol->data_size); res = true; @@ -349,8 +348,8 @@ FuzzerWorker* fuzzer_worker_alloc() { memset(instance->payload, 0x00, sizeof(instance->payload)); - instance->timer_idle_time = PROTOCOL_DEF_IDLE_TIME; - instance->timer_emu_time = PROTOCOL_DEF_EMU_TIME; + instance->timer_idle_time_ms = PROTOCOL_DEF_IDLE_TIME * 100; + instance->timer_emu_time_ms = PROTOCOL_DEF_EMU_TIME * 100; instance->timer = furi_timer_alloc(fuzzer_worker_on_tick_callback, FuriTimerTypeOnce, instance); @@ -383,17 +382,22 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_ furi_assert(instance); if(instance->attack_type < FuzzerWorkerAttackTypeMax) { - // if(emu_time == 0) { - // uint8_t temp = idle_time / 2; - // instance->timer_emu_time = temp; - // instance->timer_idle_time = temp + idle_time % 2; - // } else { - instance->timer_idle_time = idle_time; - instance->timer_emu_time = emu_time; - // } + if(idle_time == 0) { + instance->timer_idle_time_ms = 10; + } else { + instance->timer_idle_time_ms = idle_time * 100; + } + if(emu_time == 0) { + instance->timer_emu_time_ms = 10; + } else { + instance->timer_emu_time_ms = emu_time * 100; + } FURI_LOG_D( - TAG, "Emu_time %u Idle_time %u", instance->timer_emu_time, instance->timer_idle_time); + TAG, + "Emu_time %u ms Idle_time %u ms", + instance->timer_emu_time_ms, + instance->timer_idle_time_ms); if(!instance->treead_running) { #if defined(RFID_125_PROTOCOL) @@ -415,7 +419,7 @@ bool fuzzer_worker_start(FuzzerWorker* instance, uint8_t idle_time, uint8_t emu_ ibutton_worker_emulate_start(instance->proto_worker, instance->key); #endif instance->in_emu_phase = true; - furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time * 100)); + furi_timer_start(instance->timer, furi_ms_to_ticks(instance->timer_emu_time_ms)); return true; } return false; diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h index 6396525bef..8b934f300a 100644 --- a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h +++ b/applications/external/pacs_fuzzer/lib/worker/fake_worker.h @@ -82,21 +82,21 @@ bool fuzzer_worker_init_attack_file_dict( * * @param instance Pointer to a FuzzerWorker * @param protocol_index index of the selected protocol - * @param uid UID for brute force + * @param new_uid Pointer to a FuzzerPayload with UID for brute force * @param chosen index of chusen byte * @return bool True if initialization is successful */ bool fuzzer_worker_init_attack_bf_byte( FuzzerWorker* instance, FuzzerProtocolsID protocol_index, - const uint8_t* uid, + const FuzzerPayload* new_uid, uint8_t chusen); /** * Get current UID * * @param instance Pointer to a FuzzerWorker - * @param output_key Pointer to a FuzzerWorker, memory for data will be allocated + * @param output_key Pointer to a FuzzerPayload */ void fuzzer_worker_get_current_key(FuzzerWorker* instance, FuzzerPayload* output_key); diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/pacs_fuzzer/lib/worker/protocol.c index f520037ac0..a64fe8767d 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.c +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.c @@ -242,6 +242,22 @@ const FuzzerMenuItems fuzzer_menu_items[] = { {"Load UIDs from file", FuzzerAttackIdLoadFileCustomUids}, }; +FuzzerPayload* fuzzer_payload_alloc() { + FuzzerPayload* payload = malloc(sizeof(FuzzerPayload)); + payload->data = malloc(sizeof(payload->data[0]) * MAX_PAYLOAD_SIZE); + + return payload; +} + +void fuzzer_payload_free(FuzzerPayload* payload) { + furi_assert(payload); + + if(payload->data) { + free(payload->data); + } + free(payload); +} + const char* fuzzer_proto_get_name(FuzzerProtocolsID index) { return fuzzer_proto_items[index].name; } diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/pacs_fuzzer/lib/worker/protocol.h index 68632b0292..9c5315d00d 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol.h @@ -31,6 +31,20 @@ struct FuzzerPayload { uint8_t data_size; }; +/** + * Allocate FuzzerPayload + * + * @return FuzzerPayload* pointer to FuzzerPayload + */ +FuzzerPayload* fuzzer_payload_alloc(); + +/** + * Free FuzzerPayload + * + * @param instance Pointer to a FuzzerPayload + */ +void fuzzer_payload_free(FuzzerPayload*); + /** * Get maximum length of UID among all supported protocols * @return Maximum length of UID diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h index 074c50d9d9..2f1c65fd75 100644 --- a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h +++ b/applications/external/pacs_fuzzer/lib/worker/protocol_i.h @@ -19,7 +19,7 @@ typedef struct FuzzerProtocol FuzzerProtocol; struct ProtoDict { const uint8_t* val; - const uint8_t len; // TODO + const uint8_t len; }; struct FuzzerProtocol { @@ -34,20 +34,10 @@ struct FuzzerProtocol { // #define FUZZ_TIME_DELAY_DEFAULT (10) // #define FUZZ_TIME_DELAY_MAX (70) -// #define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" -// #define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/rfidfuzzer" -// #define FUZZER_APP_KEY_EXTENSION ".rfid" -// #define FUZZER_APP_PATH_KEY_FOLDER "/ext/lfrfid" - // #define MAX_PAYLOAD_SIZE 8 // #define FUZZ_TIME_DELAY_MIN (4) // #define FUZZ_TIME_DELAY_DEFAULT (8) // #define FUZZ_TIME_DELAY_MAX (80) -// #define FUZZER_APP_CUSTOM_DICT_EXTENSION ".txt" -// #define FUZZER_APP_CUSTOM_DICT_FOLDER "/ext/ibtnfuzzer" -// #define FUZZER_APP_KEY_EXTENSION ".ibtn" -// #define FUZZER_APP_PATH_KEY_FOLDER "/ext/ibutton" - extern const FuzzerProtocol fuzzer_proto_items[]; \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c index 836bbdef5e..6424e62b52 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c @@ -1,8 +1,6 @@ #include "../fuzzer_i.h" #include "../helpers/fuzzer_custom_event.h" -// TODO simlify callbacks and attack state - const NotificationSequence sequence_one_green_50_on_blink_blue = { &message_red_255, &message_delay_50, @@ -18,12 +16,9 @@ static void fuzzer_scene_attack_update_uid(PacsFuzzerApp* app) { furi_assert(app->worker); furi_assert(app->attack_view); - FuzzerPayload uid; - fuzzer_worker_get_current_key(app->worker, &uid); - - fuzzer_view_attack_set_uid(app->attack_view, uid); + fuzzer_worker_get_current_key(app->worker, app->payload); - free(uid.data); + fuzzer_view_attack_set_uid(app->attack_view, app->payload); } static void fuzzer_scene_attack_set_state(PacsFuzzerApp* app, FuzzerAttackState state) { @@ -127,7 +122,6 @@ bool fuzzer_scene_attack_on_event(void* context, SceneManagerEvent event) { if(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneAttack) == FuzzerAttackStateIdle) { // Start or Continue Attack - // TODO emu_time if(fuzzer_worker_start( app->worker, fuzzer_view_attack_get_time_delay(app->attack_view), @@ -160,7 +154,8 @@ void fuzzer_scene_attack_on_exit(void* context) { furi_assert(context); PacsFuzzerApp* app = context; - // fuzzer_worker_stop(); // XXX + // XXX the scene has no descendants, and the return will be processed in on_event + // fuzzer_worker_stop(); fuzzer_worker_set_uid_chaged_callback(app->worker, NULL, NULL); fuzzer_worker_set_end_callback(app->worker, NULL, NULL); diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index 637eff2d77..4c45bd1540 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -14,12 +14,9 @@ void fuzzer_scene_field_editor_on_enter(void* context) { fuzzer_view_field_editor_set_callback( app->field_editor_view, fuzzer_scene_field_editor_callback, app); - FuzzerPayload uid; - fuzzer_worker_get_current_key(app->worker, &uid); + fuzzer_worker_get_current_key(app->worker, app->payload); - fuzzer_view_field_editor_reset_data(app->field_editor_view, uid); - - free(uid.data); + fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload); view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); } @@ -37,11 +34,11 @@ bool fuzzer_scene_field_editor_on_event(void* context, SceneManagerEvent event) } consumed = true; } else if(event.event == FuzzerCustomEventViewFieldEditorOk) { - // TODO + fuzzer_view_field_editor_get_uid(app->field_editor_view, app->payload); if(fuzzer_worker_init_attack_bf_byte( app->worker, app->fuzzer_state.proto_index, - fuzzer_view_field_editor_get_uid(app->field_editor_view), + app->payload, fuzzer_view_field_editor_get_index(app->field_editor_view))) { scene_manager_next_scene(app->scene_manager, FuzzerSceneAttack); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index cfa43ad87b..8ed7e09d4c 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -103,8 +103,6 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { // TODO error logic bool loading_ok = false; - uint8_t d_size = fuzzer_proto_get_max_data_size(); - uint8_t* uid; switch(fuzzer_proto_get_attack_id_by_index(app->fuzzer_state.menu_index)) { case FuzzerAttackIdDefaultValues: @@ -119,13 +117,12 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { break; case FuzzerAttackIdBFCustomerID: // TODO - uid = malloc(d_size); - memset(uid, 0x00, d_size); + app->payload->data_size = fuzzer_proto_get_max_data_size(); + memset(app->payload->data, 0x00, app->payload->data_size); loading_ok = fuzzer_worker_init_attack_bf_byte( - app->worker, app->fuzzer_state.proto_index, uid, 0); + app->worker, app->fuzzer_state.proto_index, app->payload, 0); - free(uid); if(!loading_ok) { // error } diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index d0bab30d64..1cbd53c464 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -31,9 +31,13 @@ - [ ] Decide on the display - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` + - [x] Using `FuzzerPayload` to store the uid - [x] `UID_MAX_SIZE` - [x] Add pause - [x] Fix `Custom dict` attack when ended +- [ ] Pause V2 + - [ ] Save logic + - [ ] Switching UIDs if possible - [ ] Worker - [ ] Use `prtocol_id` instead of protocol name - [x] this can be simplified `fuzzer_proto_items` \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index a29e2d966f..9787278a6b 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -48,17 +48,17 @@ void fuzzer_view_attack_reset_data( true); } -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid) { +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid) { furi_assert(view); - furi_assert(uid.data); + furi_assert(uid->data); with_view_model( view->view, FuzzerViewAttackModel * model, { - furi_string_printf(model->uid_str, "%02X", uid.data[0]); - for(uint8_t i = 1; i < uid.data_size; i++) { - furi_string_cat_printf(model->uid_str, ":%02X", uid.data[i]); + furi_string_printf(model->uid_str, "%02X", uid->data[0]); + for(uint8_t i = 1; i < uid->data_size; i++) { + furi_string_cat_printf(model->uid_str, ":%02X", uid->data[i]); } }, true); diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/pacs_fuzzer/views/attack.h index 9341ae7e2b..66e96d7d6c 100644 --- a/applications/external/pacs_fuzzer/views/attack.h +++ b/applications/external/pacs_fuzzer/views/attack.h @@ -27,7 +27,7 @@ void fuzzer_view_attack_reset_data( const char* attack_name, const char* protocol_name); -void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload uid); +void fuzzer_view_attack_set_uid(FuzzerViewAttack* view, const FuzzerPayload* uid); void fuzzer_view_attack_start(FuzzerViewAttack* view); diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index 07a19ae0e3..45b5f70a18 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -49,27 +49,33 @@ void fuzzer_view_field_editor_set_callback( void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - const FuzzerPayload new_uid) { + const FuzzerPayload* new_uid) { furi_assert(view_edit); + furi_assert(new_uid->data); with_view_model( view_edit->view, FuzzerViewFieldEditorModel * model, { - memcpy(model->uid, new_uid.data, new_uid.data_size); + memcpy(model->uid, new_uid->data, new_uid->data_size); model->index = 0; model->lo = false; - model->uid_size = new_uid.data_size; + model->uid_size = new_uid->data_size; }, true); } -const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit) { +void fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit, FuzzerPayload* output_uid) { furi_assert(view_edit); - uint8_t* uid; + furi_assert(output_uid); with_view_model( - view_edit->view, FuzzerViewFieldEditorModel * model, { uid = model->uid; }, true); - return uid; + view_edit->view, + FuzzerViewFieldEditorModel * model, + { + output_uid->data_size = model->uid_size; + memcpy(output_uid->data, model->uid, model->uid_size); + }, + true); } uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit) { diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h index f76b5d3369..72c5de5e5f 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.h +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -21,9 +21,8 @@ View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - const FuzzerPayload new_uid); + const FuzzerPayload* new_uid); -// TODO -const uint8_t* fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit); +void fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit, FuzzerPayload* output_uid); uint8_t fuzzer_view_field_editor_get_index(FuzzerViewFieldEditor* view_edit); \ No newline at end of file From c763ae6d5cefcc332487478405ad64c8e8fcece0 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Wed, 7 Jun 2023 11:54:20 +0300 Subject: [PATCH 075/102] Fuzzer App: cleanup attack view --- applications/external/pacs_fuzzer/todo.md | 2 +- .../external/pacs_fuzzer/views/attack.c | 62 +------------------ 2 files changed, 2 insertions(+), 62 deletions(-) diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 1cbd53c464..136ec5e99f 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -28,7 +28,7 @@ - [ ] Protocol carousel in `main_menu` - [x] prototype - [x] Add the ability to edit emulation time and downtime separately - - [ ] Decide on the display + - [x] Decide on the display - [x] UID - [x] Simplify the storage and exchange of `uids.data` `uid.data_size` in `views` - [x] Using `FuzzerPayload` to store the uid diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/pacs_fuzzer/views/attack.c index 9787278a6b..87aa9f659c 100644 --- a/applications/external/pacs_fuzzer/views/attack.c +++ b/applications/external/pacs_fuzzer/views/attack.c @@ -4,10 +4,6 @@ #include #include -#define ATACK_VIEW_V2 -// #define ATACK_VIEW_V2_1 -#define ATACK_VIEW_V2_2 - #define ATTACK_SCENE_MAX_UID_LENGTH 25 #define UID_MAX_DISPLAYED_LEN (8U) #define LIFT_RIGHT_OFFSET (3) @@ -123,41 +119,6 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, model->attack_name); -#ifndef ATACK_VIEW_V2 - canvas_set_font(canvas, FontSecondary); - snprintf( - temp_str, - sizeof(temp_str), - "Time delay: %d.%d", - model->time_delay / 10, - model->time_delay % 10); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, temp_str); -#elif defined(ATACK_VIEW_V2_1) - - canvas_set_font(canvas, FontSecondary); - if(!model->td_emt_cursor) { - snprintf( - temp_str, - sizeof(temp_str), - "Time delay: %d.%d EmT: %d.%d", - model->time_delay / 10, - model->time_delay % 10, - model->emu_time / 10, - model->emu_time % 10); - } else { - snprintf( - temp_str, - sizeof(temp_str), - "TD: %d.%d Emulation time: %d.%d", - model->time_delay / 10, - model->time_delay % 10, - model->emu_time / 10, - model->emu_time % 10); - } - canvas_draw_str_aligned(canvas, 64, 21, AlignCenter, AlignBottom, temp_str); - -#elif defined(ATACK_VIEW_V2_2) - uint16_t crt; canvas_set_font(canvas, FontPrimary); @@ -178,7 +139,6 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { temp_str, sizeof(temp_str), "EmT: %d.%d", model->emu_time / 10, model->emu_time % 10); canvas_draw_str_aligned( canvas, 128 - LIFT_RIGHT_OFFSET, 21, AlignRight, AlignBottom, temp_str); - } else { canvas_set_font(canvas, FontSecondary); snprintf( @@ -202,21 +162,6 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { canvas, 128 - LIFT_RIGHT_OFFSET - crt - 3, 21, AlignRight, AlignBottom, temp_str); } -#else - - canvas_set_font(canvas, FontSecondary); - snprintf( - temp_str, - sizeof(temp_str), - "Time delay: %d.%d Emu time: %d.%d", - model->time_delay / 10, - model->time_delay % 10, - model->emu_time / 10, - model->emu_time % 10); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, temp_str); - -#endif - canvas_set_font(canvas, FontSecondary); canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, model->protocol_name); @@ -231,11 +176,6 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { if(model->attack_state == FuzzerAttackStateRunning) { elements_button_center(canvas, "Stop"); } else if(model->attack_state == FuzzerAttackStateIdle) { -#ifndef ATACK_VIEW_V2 - elements_button_center(canvas, "Start"); - elements_button_left(canvas, "TD -"); - elements_button_right(canvas, "+ TD"); -#else if(model->td_emt_cursor) { elements_button_center(canvas, "Start"); elements_button_left(canvas, "EmT -"); @@ -245,7 +185,7 @@ void fuzzer_view_attack_draw(Canvas* canvas, FuzzerViewAttackModel* model) { elements_button_left(canvas, "TD -"); elements_button_right(canvas, "+ TD"); } -#endif + } else if(model->attack_state == FuzzerAttackStateEnd) { // elements_button_center(canvas, "Restart"); // Reset elements_button_left(canvas, "Exit"); From 82de8145b0b123954183e18b688b18b51744c485 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:44:33 +0300 Subject: [PATCH 076/102] Fuzzer App: improved BFCustomerID attack --- .../pacs_fuzzer/helpers/fuzzer_types.h | 6 ++ .../scenes/fuzzer_scene_field_editor.c | 13 +++- .../pacs_fuzzer/scenes/fuzzer_scene_main.c | 18 +++-- applications/external/pacs_fuzzer/todo.md | 1 + .../external/pacs_fuzzer/views/field_editor.c | 69 ++++++++++++------- .../external/pacs_fuzzer/views/field_editor.h | 3 +- 6 files changed, 81 insertions(+), 29 deletions(-) diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h index e4661ed7bb..bb608a5f14 100644 --- a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h +++ b/applications/external/pacs_fuzzer/helpers/fuzzer_types.h @@ -15,6 +15,12 @@ typedef enum { } FuzzerAttackState; +typedef enum { + FuzzerFieldEditorStateEditingOn = 0, + FuzzerFieldEditorStateEditingOff, + +} FuzzerFieldEditorState; + typedef enum { FuzzerViewIDPopup, diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c index 4c45bd1540..ccea123dc9 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c @@ -16,7 +16,18 @@ void fuzzer_scene_field_editor_on_enter(void* context) { fuzzer_worker_get_current_key(app->worker, app->payload); - fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload); + switch(scene_manager_get_scene_state(app->scene_manager, FuzzerSceneFieldEditor)) { + case FuzzerFieldEditorStateEditingOn: + fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload, true); + break; + + case FuzzerFieldEditorStateEditingOff: + fuzzer_view_field_editor_reset_data(app->field_editor_view, app->payload, false); + break; + + default: + break; + } view_dispatcher_switch_to_view(app->view_dispatcher, FuzzerViewIDFieldEditor); } diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c index 8ed7e09d4c..0d074a1219 100644 --- a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c +++ b/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c @@ -106,7 +106,6 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { switch(fuzzer_proto_get_attack_id_by_index(app->fuzzer_state.menu_index)) { case FuzzerAttackIdDefaultValues: - loading_ok = fuzzer_worker_init_attack_dict(app->worker, app->fuzzer_state.proto_index); @@ -115,15 +114,21 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { fuzzer_scene_main_show_error(app, "Default dictionary\nis empty"); } break; + case FuzzerAttackIdBFCustomerID: // TODO app->payload->data_size = fuzzer_proto_get_max_data_size(); memset(app->payload->data, 0x00, app->payload->data_size); - loading_ok = fuzzer_worker_init_attack_bf_byte( - app->worker, app->fuzzer_state.proto_index, app->payload, 0); + if(fuzzer_worker_init_attack_bf_byte( + app->worker, app->fuzzer_state.proto_index, app->payload, 0)) { + scene_manager_set_scene_state( + app->scene_manager, + FuzzerSceneFieldEditor, + FuzzerFieldEditorStateEditingOff); + scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); - if(!loading_ok) { + } else { // error } break; @@ -136,6 +141,10 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { app->worker, app->fuzzer_state.proto_index, furi_string_get_cstr(app->file_path))) { + scene_manager_set_scene_state( + app->scene_manager, + FuzzerSceneFieldEditor, + FuzzerFieldEditorStateEditingOn); scene_manager_next_scene(app->scene_manager, FuzzerSceneFieldEditor); FURI_LOG_I("Scene", "Load ok"); } else { @@ -159,6 +168,7 @@ bool fuzzer_scene_main_on_event(void* context, SceneManagerEvent event) { break; default: + fuzzer_scene_main_show_error(app, "Unsuported attack"); break; } diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/pacs_fuzzer/todo.md index 136ec5e99f..4e3b0e17de 100644 --- a/applications/external/pacs_fuzzer/todo.md +++ b/applications/external/pacs_fuzzer/todo.md @@ -17,6 +17,7 @@ #### App functionality - [x] Add `BFCustomerID` attack + - [x] Add the ability to select index - [ ] Save key logic ## Code Improvement diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/pacs_fuzzer/views/field_editor.c index 45b5f70a18..bdce0a516e 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.c +++ b/applications/external/pacs_fuzzer/views/field_editor.c @@ -35,6 +35,7 @@ typedef struct { uint8_t index; bool lo; + bool allow_edit; } FuzzerViewFieldEditorModel; void fuzzer_view_field_editor_set_callback( @@ -49,7 +50,8 @@ void fuzzer_view_field_editor_set_callback( void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - const FuzzerPayload* new_uid) { + const FuzzerPayload* new_uid, + bool allow_edit) { furi_assert(view_edit); furi_assert(new_uid->data); @@ -61,6 +63,7 @@ void fuzzer_view_field_editor_reset_data( model->index = 0; model->lo = false; model->uid_size = new_uid->data_size; + model->allow_edit = allow_edit; }, true); } @@ -93,15 +96,20 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m #ifdef FIELD_EDITOR_V2 canvas_set_font(canvas, FontSecondary); + if(model->allow_edit) { + canvas_draw_icon(canvas, 2, 4, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 8, 4, &I_ButtonRight_4x7); - canvas_draw_icon(canvas, 2, 4, &I_ButtonLeft_4x7); - canvas_draw_icon(canvas, 8, 4, &I_ButtonRight_4x7); + canvas_draw_icon_ex(canvas, 62, 3, &I_Pin_arrow_up_7x9, IconRotation180); + canvas_draw_icon(canvas, 69, 3, &I_Pin_arrow_up_7x9); - canvas_draw_icon_ex(canvas, 62, 3, &I_Pin_arrow_up_7x9, IconRotation180); - canvas_draw_icon(canvas, 69, 3, &I_Pin_arrow_up_7x9); - - canvas_draw_str(canvas, 14, 10, "select byte"); - canvas_draw_str(canvas, 79, 10, "adjust byte"); + canvas_draw_str(canvas, 14, 10, "select byte"); + canvas_draw_str(canvas, 79, 10, "adjust byte"); + } else { + canvas_draw_icon(canvas, 35, 4, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 41, 4, &I_ButtonRight_4x7); + canvas_draw_str(canvas, 49, 10, "select byte"); + } char msg_index[18]; canvas_set_font(canvas, FontPrimary); @@ -177,20 +185,29 @@ void fuzzer_view_field_editor_draw(Canvas* canvas, FuzzerViewFieldEditorModel* m w -= 11; // '<' & '>' w /= 2; - if(model->lo) { - canvas_draw_line( - canvas, - GUI_DISPLAY_HORIZONTAL_CENTER + 1, - EDITOR_STRING_Y + 2, - GUI_DISPLAY_HORIZONTAL_CENTER + w, - EDITOR_STRING_Y + 2); + if(model->allow_edit) { + if(model->lo) { + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER + 1, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER + w, + EDITOR_STRING_Y + 2); + } else { + canvas_draw_line( + canvas, + GUI_DISPLAY_HORIZONTAL_CENTER - w, + EDITOR_STRING_Y + 2, + GUI_DISPLAY_HORIZONTAL_CENTER - 1, + EDITOR_STRING_Y + 2); + } } else { - canvas_draw_line( - canvas, - GUI_DISPLAY_HORIZONTAL_CENTER - w, - EDITOR_STRING_Y + 2, - GUI_DISPLAY_HORIZONTAL_CENTER - 1, - EDITOR_STRING_Y + 2); + // canvas_draw_line( + // canvas, + // GUI_DISPLAY_HORIZONTAL_CENTER - w, + // EDITOR_STRING_Y + 2, + // GUI_DISPLAY_HORIZONTAL_CENTER + w, + // EDITOR_STRING_Y + 2); } // ####### Editor ####### } @@ -211,6 +228,9 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { FuzzerViewFieldEditorModel * model, { if(event->type == InputTypeShort) { + if(!model->allow_edit) { + model->lo = false; + } if(model->index > 0 || model->lo) { if(!model->lo) { model->index--; @@ -230,6 +250,9 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { FuzzerViewFieldEditorModel * model, { if(event->type == InputTypeShort) { + if(!model->allow_edit) { + model->lo = true; + } if(model->index < (model->uid_size - 1) || !model->lo) { if(model->lo) { model->index++; @@ -248,7 +271,7 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { view_edit->view, FuzzerViewFieldEditorModel * model, { - if(event->type == InputTypeShort) { + if(event->type == InputTypeShort && model->allow_edit) { if(model->lo) { model->uid[model->index] = (model->uid[model->index] & 0xF0) | ((model->uid[model->index] + 1) & 0x0F); @@ -265,7 +288,7 @@ bool fuzzer_view_field_editor_input(InputEvent* event, void* context) { view_edit->view, FuzzerViewFieldEditorModel * model, { - if(event->type == InputTypeShort) { + if(event->type == InputTypeShort && model->allow_edit) { if(model->lo) { model->uid[model->index] = (model->uid[model->index] & 0xF0) | ((model->uid[model->index] - 1) & 0x0F); diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/pacs_fuzzer/views/field_editor.h index 72c5de5e5f..d81538bf8d 100644 --- a/applications/external/pacs_fuzzer/views/field_editor.h +++ b/applications/external/pacs_fuzzer/views/field_editor.h @@ -21,7 +21,8 @@ View* fuzzer_view_field_editor_get_view(FuzzerViewFieldEditor* view_attack); void fuzzer_view_field_editor_reset_data( FuzzerViewFieldEditor* view_edit, - const FuzzerPayload* new_uid); + const FuzzerPayload* new_uid, + bool allow_edit); void fuzzer_view_field_editor_get_uid(FuzzerViewFieldEditor* view_edit, FuzzerPayload* output_uid); From 09fae620d9bd4fd6ce6bfb263df074739fd122f8 Mon Sep 17 00:00:00 2001 From: glebmashanov <65850300+glebmashanov@users.noreply.github.com> Date: Wed, 7 Jun 2023 16:30:26 +0300 Subject: [PATCH 077/102] Map parser licence description (#2739) * Add map parser licence description * Add map parser copyright text & licence note --------- Co-authored-by: hedger --- scripts/map_parser.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/scripts/map_parser.py b/scripts/map_parser.py index c0c34e3d1d..1efc4fe82f 100755 --- a/scripts/map_parser.py +++ b/scripts/map_parser.py @@ -3,6 +3,29 @@ # Requiremets: # cxxfilt==0.3.0 +# Most part of this code written by Lars-Dominik Braun https://github.com/PromyLOPh/linkermapviz +# and distributes under MIT licence + +# Copyright (c) 2017 Lars-Dominik Braun +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + import sys import re import os From 921ab90e467af697d0de484a79e931606b049549 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 19:58:42 +0100 Subject: [PATCH 078/102] Lowercase appid's --- applications/debug/uart_echo/application.fam | 2 +- applications/external/airmouse/application.fam | 2 +- applications/external/avr_isp_programmer/avr_isp_app_i.h | 2 +- applications/external/bpmtapper/application.fam | 2 +- applications/external/bpmtapper/bpm.c | 2 +- applications/external/brainfuck/application.fam | 2 +- applications/external/brainfuck/brainfuck_i.h | 2 +- applications/external/caesarcipher/application.fam | 2 +- applications/external/calculator/application.fam | 2 +- applications/external/esp32cam_camera/application.fam | 2 +- .../external/esp32cam_marauder_companion/application.fam | 2 +- .../external/esp32cam_morseflasher/application.fam | 2 +- .../external/esp32cam_morseflasher/uart_text_input.c | 2 +- .../external/esp32cam_motion_detection/application.fam | 2 +- applications/external/esp32cam_nannycam/application.fam | 2 +- applications/external/esp32cam_qrcode/application.fam | 2 +- applications/external/game_of_life/application.fam | 2 +- applications/external/ifttt/application.fam | 2 +- applications/external/mandelbrot/application.fam | 2 +- applications/external/music_beeper/application.fam | 2 +- applications/external/music_beeper/music_beeper.c | 2 +- applications/external/nrf24scan/application.fam | 2 +- applications/external/ocarina/application.fam | 2 +- applications/external/orgasmotron/application.fam | 2 +- applications/external/paint/application.fam | 2 +- applications/external/sam/application.fam | 8 ++++---- applications/external/subghz_bruteforcer/application.fam | 2 +- applications/external/subghz_bruteforcer/subbrute_i.h | 4 ++-- applications/external/subghz_remote/application.fam | 2 +- applications/external/tama_p1/application.fam | 2 +- applications/external/tanksgame/application.fam | 2 +- applications/external/tanksgame/tanks_game.c | 2 +- applications/external/timelapse/application.fam | 2 +- applications/external/timelapse/zeitraffer.c | 2 +- applications/external/videopoker/application.fam | 2 +- applications/external/wifi_deauther/application.fam | 2 +- 36 files changed, 40 insertions(+), 40 deletions(-) diff --git a/applications/debug/uart_echo/application.fam b/applications/debug/uart_echo/application.fam index ecdc847cd1..3e262a09df 100644 --- a/applications/debug/uart_echo/application.fam +++ b/applications/debug/uart_echo/application.fam @@ -1,5 +1,5 @@ App( - appid="UART_Echo", + appid="uart_echo", name="[GPIO] UART Echo", apptype=FlipperAppType.DEBUG, entry_point="uart_echo_app", diff --git a/applications/external/airmouse/application.fam b/applications/external/airmouse/application.fam index 7bdba948a7..1164b66de1 100644 --- a/applications/external/airmouse/application.fam +++ b/applications/external/airmouse/application.fam @@ -1,5 +1,5 @@ App( - appid="Air_Mouse", + appid="air_mouse", name="[BMI160] Air Mouse", apptype=FlipperAppType.EXTERNAL, entry_point="air_mouse_app", diff --git a/applications/external/avr_isp_programmer/avr_isp_app_i.h b/applications/external/avr_isp_programmer/avr_isp_app_i.h index 17c69f8f2e..819e3f45f0 100644 --- a/applications/external/avr_isp_programmer/avr_isp_app_i.h +++ b/applications/external/avr_isp_programmer/avr_isp_app_i.h @@ -41,4 +41,4 @@ typedef struct { AvrIspError error; } AvrIspApp; -bool avr_isp_load_from_file(AvrIspApp* app); \ No newline at end of file +bool avr_isp_load_from_file(AvrIspApp* app); diff --git a/applications/external/bpmtapper/application.fam b/applications/external/bpmtapper/application.fam index 93c4179c59..ec8ca5baf0 100644 --- a/applications/external/bpmtapper/application.fam +++ b/applications/external/bpmtapper/application.fam @@ -1,5 +1,5 @@ App( - appid="BPM_Tapper", + appid="bpm_tapper", name="BPM Tapper", apptype=FlipperAppType.EXTERNAL, entry_point="bpm_tapper_app", diff --git a/applications/external/bpmtapper/bpm.c b/applications/external/bpmtapper/bpm.c index 5720ac4d2f..397b1a3b58 100644 --- a/applications/external/bpmtapper/bpm.c +++ b/applications/external/bpmtapper/bpm.c @@ -4,7 +4,7 @@ #include #include #include -#include "BPM_Tapper_icons.h" +#include "bpm_tapper_icons.h" typedef enum { EventTypeTick, diff --git a/applications/external/brainfuck/application.fam b/applications/external/brainfuck/application.fam index 716c44a6a9..b4cab7be2f 100644 --- a/applications/external/brainfuck/application.fam +++ b/applications/external/brainfuck/application.fam @@ -1,5 +1,5 @@ App( - appid="Brainfuck", + appid="brainfuck", name="Brainfuck", apptype=FlipperAppType.EXTERNAL, entry_point="brainfuck_app", diff --git a/applications/external/brainfuck/brainfuck_i.h b/applications/external/brainfuck/brainfuck_i.h index 3940cecade..d3d27dcbd9 100644 --- a/applications/external/brainfuck/brainfuck_i.h +++ b/applications/external/brainfuck/brainfuck_i.h @@ -29,7 +29,7 @@ typedef unsigned char byte; #include #include -#include +#include #include #include diff --git a/applications/external/caesarcipher/application.fam b/applications/external/caesarcipher/application.fam index 4f438d2b34..487494640e 100644 --- a/applications/external/caesarcipher/application.fam +++ b/applications/external/caesarcipher/application.fam @@ -1,5 +1,5 @@ App( - appid="Caesar_Cipher", + appid="caesar_cipher", name="Caesar Cipher", apptype=FlipperAppType.EXTERNAL, entry_point="caesar_cipher_app", diff --git a/applications/external/calculator/application.fam b/applications/external/calculator/application.fam index ac78af7a5d..195a621432 100644 --- a/applications/external/calculator/application.fam +++ b/applications/external/calculator/application.fam @@ -1,5 +1,5 @@ App( - appid="Calculator", + appid="calculator", name="Calculator", apptype=FlipperAppType.EXTERNAL, entry_point="calculator_app", diff --git a/applications/external/esp32cam_camera/application.fam b/applications/external/esp32cam_camera/application.fam index 459413817c..a0e0ede4c4 100644 --- a/applications/external/esp32cam_camera/application.fam +++ b/applications/external/esp32cam_camera/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_Camera", + appid="mayhem_camera", name="[MAYHEM] Camera", apptype=FlipperAppType.EXTERNAL, entry_point="camera_app", diff --git a/applications/external/esp32cam_marauder_companion/application.fam b/applications/external/esp32cam_marauder_companion/application.fam index 430d641052..800cb92be3 100644 --- a/applications/external/esp32cam_marauder_companion/application.fam +++ b/applications/external/esp32cam_marauder_companion/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_Marauder", + appid="mayhem_marauder", name="[MAYHEM] Marauder", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_marauder_app", diff --git a/applications/external/esp32cam_morseflasher/application.fam b/applications/external/esp32cam_morseflasher/application.fam index c15207015c..037e63c811 100644 --- a/applications/external/esp32cam_morseflasher/application.fam +++ b/applications/external/esp32cam_morseflasher/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_MorseFlash", + appid="mayhem_morseflash", name="[MAYHEM] Morse Flasher", apptype=FlipperAppType.EXTERNAL, entry_point="uart_terminal_app", diff --git a/applications/external/esp32cam_morseflasher/uart_text_input.c b/applications/external/esp32cam_morseflasher/uart_text_input.c index 4e23362543..5b2b1033dc 100644 --- a/applications/external/esp32cam_morseflasher/uart_text_input.c +++ b/applications/external/esp32cam_morseflasher/uart_text_input.c @@ -1,6 +1,6 @@ #include "uart_text_input.h" #include -#include "MAYHEM_MorseFlash_icons.h" +#include "mayhem_morseflash_icons.h" #include struct UART_TextInput { diff --git a/applications/external/esp32cam_motion_detection/application.fam b/applications/external/esp32cam_motion_detection/application.fam index 9a9b4fcc97..57a5396c5b 100644 --- a/applications/external/esp32cam_motion_detection/application.fam +++ b/applications/external/esp32cam_motion_detection/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_Motion", + appid="mayhem_motion", name="[MAYHEM] Motion detection", apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", diff --git a/applications/external/esp32cam_nannycam/application.fam b/applications/external/esp32cam_nannycam/application.fam index 1c4831d7c8..75f2be7838 100644 --- a/applications/external/esp32cam_nannycam/application.fam +++ b/applications/external/esp32cam_nannycam/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_NannyCam", + appid="mayhem_nannycam", name="[MAYHEM] Nanny Cam", apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", diff --git a/applications/external/esp32cam_qrcode/application.fam b/applications/external/esp32cam_qrcode/application.fam index bd74f26b2f..73c418510c 100644 --- a/applications/external/esp32cam_qrcode/application.fam +++ b/applications/external/esp32cam_qrcode/application.fam @@ -1,5 +1,5 @@ App( - appid="MAYHEM_QRcode", + appid="mayhem_qrcode", name="[MAYHEM] QR Code", apptype=FlipperAppType.EXTERNAL, entry_point="uart_echo_app", diff --git a/applications/external/game_of_life/application.fam b/applications/external/game_of_life/application.fam index 55e244d228..6962bf23e0 100644 --- a/applications/external/game_of_life/application.fam +++ b/applications/external/game_of_life/application.fam @@ -1,5 +1,5 @@ App( - appid="GameOfLife", + appid="gameoflife", name="Game of Life", apptype=FlipperAppType.EXTERNAL, entry_point="game_of_life_app", diff --git a/applications/external/ifttt/application.fam b/applications/external/ifttt/application.fam index 6a3c4986e9..cdb439f26b 100644 --- a/applications/external/ifttt/application.fam +++ b/applications/external/ifttt/application.fam @@ -1,5 +1,5 @@ App( - appid="ESP8266_IFTTT_Virtual_Button", + appid="esp8266_ifttt_virtual_button", name="[ESP8266] IFTTT Virtual Button", apptype=FlipperAppType.EXTERNAL, entry_point="ifttt_virtual_button_app", diff --git a/applications/external/mandelbrot/application.fam b/applications/external/mandelbrot/application.fam index 773835fe55..82cc9871a2 100644 --- a/applications/external/mandelbrot/application.fam +++ b/applications/external/mandelbrot/application.fam @@ -1,5 +1,5 @@ App( - appid="MandelbrotSet", + appid="mandelbrotset", name="Mandelbrot Set", apptype=FlipperAppType.EXTERNAL, entry_point="mandelbrot_app", diff --git a/applications/external/music_beeper/application.fam b/applications/external/music_beeper/application.fam index 39b9babba4..c63a4f7172 100644 --- a/applications/external/music_beeper/application.fam +++ b/applications/external/music_beeper/application.fam @@ -1,5 +1,5 @@ App( - appid="Music_Beeper", + appid="music_beeper", name="Music Beeper", apptype=FlipperAppType.EXTERNAL, entry_point="music_beeper_app", diff --git a/applications/external/music_beeper/music_beeper.c b/applications/external/music_beeper/music_beeper.c index f743e3c98c..f3b4828d34 100644 --- a/applications/external/music_beeper/music_beeper.c +++ b/applications/external/music_beeper/music_beeper.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include diff --git a/applications/external/nrf24scan/application.fam b/applications/external/nrf24scan/application.fam index c346112e2b..1e7bd3efe4 100644 --- a/applications/external/nrf24scan/application.fam +++ b/applications/external/nrf24scan/application.fam @@ -1,5 +1,5 @@ App( - appid="Nrf24_Scanner", + appid="nrf24_scanner", name="[NRF24] Scanner", apptype=FlipperAppType.EXTERNAL, entry_point="nrf24scan_app", diff --git a/applications/external/ocarina/application.fam b/applications/external/ocarina/application.fam index 192cb2f161..8a28d0927b 100644 --- a/applications/external/ocarina/application.fam +++ b/applications/external/ocarina/application.fam @@ -1,5 +1,5 @@ App( - appid="Ocarina", + appid="ocarina", name="Ocarina", apptype=FlipperAppType.EXTERNAL, entry_point="ocarina_app", diff --git a/applications/external/orgasmotron/application.fam b/applications/external/orgasmotron/application.fam index 2fd4563398..29d968fb53 100644 --- a/applications/external/orgasmotron/application.fam +++ b/applications/external/orgasmotron/application.fam @@ -1,5 +1,5 @@ App( - appid="Orgasmotron", + appid="orgasmotron", name="Orgasmotron", apptype=FlipperAppType.EXTERNAL, entry_point="orgasmotron_app", diff --git a/applications/external/paint/application.fam b/applications/external/paint/application.fam index d727ab2d23..e4b7b4fa83 100644 --- a/applications/external/paint/application.fam +++ b/applications/external/paint/application.fam @@ -1,5 +1,5 @@ App( - appid="Paint", + appid="paint", name="Paint", apptype=FlipperAppType.EXTERNAL, entry_point="paint_app", diff --git a/applications/external/sam/application.fam b/applications/external/sam/application.fam index dc0641b3c1..97b557db2e 100644 --- a/applications/external/sam/application.fam +++ b/applications/external/sam/application.fam @@ -1,5 +1,5 @@ App( - appid="SAM", + appid="sam", name="SAM AYBABTU", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app", @@ -14,7 +14,7 @@ App( fap_icon_assets="icons", ) App( - appid="SAM_YES", + appid="sam_yes", name="SAM YES", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app_yes", @@ -29,7 +29,7 @@ App( fap_icon_assets="icons", ) App( - appid="SAM_NO", + appid="sam_no", name="SAM NO", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app_no", @@ -44,7 +44,7 @@ App( fap_icon_assets="icons", ) App( - appid="SAM_WTF", + appid="sam_wtf", name="SAM WTF", apptype=FlipperAppType.EXTERNAL, entry_point="sam_app_wtf", diff --git a/applications/external/subghz_bruteforcer/application.fam b/applications/external/subghz_bruteforcer/application.fam index f756ae51a6..cfd354f481 100644 --- a/applications/external/subghz_bruteforcer/application.fam +++ b/applications/external/subghz_bruteforcer/application.fam @@ -1,5 +1,5 @@ App( - appid="SubGHz_Bruteforcer", + appid="subghz_bruteforcer", name="Sub-GHz Bruteforcer", apptype=FlipperAppType.EXTERNAL, entry_point="subbrute_app", diff --git a/applications/external/subghz_bruteforcer/subbrute_i.h b/applications/external/subghz_bruteforcer/subbrute_i.h index c50a7ed9b2..59afaa231c 100644 --- a/applications/external/subghz_bruteforcer/subbrute_i.h +++ b/applications/external/subghz_bruteforcer/subbrute_i.h @@ -16,7 +16,7 @@ #include #include -#include "SubGHz_Bruteforcer_icons.h" +#include "subghz_bruteforcer_icons.h" #include @@ -77,4 +77,4 @@ struct SubBruteState { void subbrute_show_loading_popup(void* context, bool show); void subbrute_text_input_callback(void* context); -void subbrute_popup_closed_callback(void* context); \ No newline at end of file +void subbrute_popup_closed_callback(void* context); diff --git a/applications/external/subghz_remote/application.fam b/applications/external/subghz_remote/application.fam index 7f12c31f9a..e2229e505f 100644 --- a/applications/external/subghz_remote/application.fam +++ b/applications/external/subghz_remote/application.fam @@ -1,5 +1,5 @@ App( - appid="SubGHz_Remote", + appid="subghz_remote", name="Sub-GHz Remote", apptype=FlipperAppType.EXTERNAL, entry_point="subghz_remote_app", diff --git a/applications/external/tama_p1/application.fam b/applications/external/tama_p1/application.fam index 93ee53aa53..e51281ff71 100644 --- a/applications/external/tama_p1/application.fam +++ b/applications/external/tama_p1/application.fam @@ -1,5 +1,5 @@ App( - appid="TAMA_P1", + appid="tama_p1", name="Tamagotchi", apptype=FlipperAppType.EXTERNAL, entry_point="tama_p1_app", diff --git a/applications/external/tanksgame/application.fam b/applications/external/tanksgame/application.fam index 7489882972..f4fe497230 100644 --- a/applications/external/tanksgame/application.fam +++ b/applications/external/tanksgame/application.fam @@ -1,5 +1,5 @@ App( - appid="Tanks", + appid="tanks", name="Tanks", apptype=FlipperAppType.EXTERNAL, entry_point="tanks_game_app", diff --git a/applications/external/tanksgame/tanks_game.c b/applications/external/tanksgame/tanks_game.c index e8fb988fa4..52ce6b36e5 100644 --- a/applications/external/tanksgame/tanks_game.c +++ b/applications/external/tanksgame/tanks_game.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include "constants.h" diff --git a/applications/external/timelapse/application.fam b/applications/external/timelapse/application.fam index ba5babc7e7..f813b1f8e1 100644 --- a/applications/external/timelapse/application.fam +++ b/applications/external/timelapse/application.fam @@ -1,5 +1,5 @@ App( - appid="GPIO_Timelapse", + appid="gpio_timelapse", name="[GPIO] Timelapse", apptype=FlipperAppType.EXTERNAL, entry_point="zeitraffer_app", diff --git a/applications/external/timelapse/zeitraffer.c b/applications/external/timelapse/zeitraffer.c index 7a300fdcf0..c5430bfdb9 100644 --- a/applications/external/timelapse/zeitraffer.c +++ b/applications/external/timelapse/zeitraffer.c @@ -5,7 +5,7 @@ #include #include #include "gpio_item.h" -#include "GPIO_Timelapse_icons.h" +#include "gpio_timelapse_icons.h" #define CONFIG_FILE_DIRECTORY_PATH "/ext/apps_data/timelapse" #define CONFIG_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/timelapse.conf" diff --git a/applications/external/videopoker/application.fam b/applications/external/videopoker/application.fam index e40953fb4e..31c74422ab 100644 --- a/applications/external/videopoker/application.fam +++ b/applications/external/videopoker/application.fam @@ -1,5 +1,5 @@ App( - appid="VideoPoker", + appid="videopoker", name="Video Poker", apptype=FlipperAppType.EXTERNAL, entry_point="video_poker_app", diff --git a/applications/external/wifi_deauther/application.fam b/applications/external/wifi_deauther/application.fam index 07ceb543b7..5b784bb101 100644 --- a/applications/external/wifi_deauther/application.fam +++ b/applications/external/wifi_deauther/application.fam @@ -1,5 +1,5 @@ App( - appid="ESP8266_Wifi_Deauther_v2", + appid="esp8266_wifi_deauther_v2", name="[ESP8266] Deauther v2", apptype=FlipperAppType.EXTERNAL, entry_point="wifi_deauther_app", From efa8cdaa7e6dcafa04348e95a17b8cf449ef38cf Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 19:58:55 +0100 Subject: [PATCH 079/102] Add back missing nfc icon --- assets/icons/NFC/ArrowC_1_36x36.png | Bin 0 -> 3692 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/icons/NFC/ArrowC_1_36x36.png diff --git a/assets/icons/NFC/ArrowC_1_36x36.png b/assets/icons/NFC/ArrowC_1_36x36.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0c6dd0cb2b7cb6eebb1507fa68a8dbda11b146 GIT binary patch literal 3692 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 literal 0 HcmV?d00001 From cd505b1b7b84131f35a717b8bd185bf404f16da8 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 20:07:43 +0100 Subject: [PATCH 080/102] Why did this get deleted lol --- .../passgen/icons/Pin_back_arrow_10x8.png | Bin 0 -> 3606 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 applications/external/passgen/icons/Pin_back_arrow_10x8.png diff --git a/applications/external/passgen/icons/Pin_back_arrow_10x8.png b/applications/external/passgen/icons/Pin_back_arrow_10x8.png new file mode 100644 index 0000000000000000000000000000000000000000..3bafabd144864b575144c75b592e5eaf53974566 GIT binary patch literal 3606 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 TFC+3}9cE|kU{!4CefEC<&8td2 literal 0 HcmV?d00001 From cca787b96d304c084e68c73f1cf13954f399ec41 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 20:35:45 +0100 Subject: [PATCH 081/102] Dont save setting files on load fail --- applications/services/bt/bt_service/bt.c | 4 +--- applications/services/desktop/desktop.c | 4 +--- applications/services/notification/notification_app.c | 6 +----- applications/services/power/power_service/power.c | 1 - 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 60ab1dd840..b8cb09734d 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -121,9 +121,7 @@ Bt* bt_alloc() { bt->max_packet_size = FURI_HAL_BT_SERIAL_PACKET_SIZE_MAX; bt->profile = BtProfileSerial; // Load settings - if(!bt_settings_load(&bt->bt_settings)) { - bt_settings_save(&bt->bt_settings); - } + bt_settings_load(&bt->bt_settings); // Keys storage bt->keys_storage = bt_keys_storage_alloc(BT_KEYS_STORAGE_PATH); // Alloc queue diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 956a1cbf8f..719110356c 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -457,10 +457,8 @@ int32_t desktop_srv(void* p) { Desktop* desktop = desktop_alloc(); - bool loaded = DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(!loaded) { + if(!DESKTOP_SETTINGS_LOAD(&desktop->settings)) { memset(&desktop->settings, 0, sizeof(desktop->settings)); - DESKTOP_SETTINGS_SAVE(&desktop->settings); } desktop_clock_toggle_view(desktop, desktop->settings.display_clock); diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index b7b7891041..cce407cc4e 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -497,11 +497,7 @@ int32_t notification_srv(void* p) { UNUSED(p); NotificationApp* app = notification_app_alloc(); - if(furi_hal_is_normal_boot()) { - if(!notification_load_settings(app)) { - notification_save_settings(app); - } - } + notification_load_settings(app); notification_vibro_off(); notification_sound_off(); diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 51fcb3895e..a42d700b58 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -509,7 +509,6 @@ int32_t power_srv(void* p) { Power* power = power_alloc(); if(!LOAD_POWER_SETTINGS(&power->shutdown_idle_delay_ms)) { power->shutdown_idle_delay_ms = 0; - SAVE_POWER_SETTINGS(&power->shutdown_idle_delay_ms); } power_auto_shutdown_arm(power); power_update_info(power); From db65b8f09f0036ce40b53a4bdd6b7b627d5e8abc Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 20:36:26 +0100 Subject: [PATCH 082/102] Discard lock flag if desktop settings load fails --- applications/services/desktop/desktop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 719110356c..b10fa53a77 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -459,6 +459,7 @@ int32_t desktop_srv(void* p) { if(!DESKTOP_SETTINGS_LOAD(&desktop->settings)) { memset(&desktop->settings, 0, sizeof(desktop->settings)); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); } desktop_clock_toggle_view(desktop, desktop->settings.display_clock); From b48579e4a7a04f1f37358a562749ac8c604d43a0 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 7 Jun 2023 21:19:31 +0100 Subject: [PATCH 083/102] Add missing anims, fix butthurt values --- .../external/L1_Kaiju_128x64/frame_0.png | Bin 0 -> 1312 bytes .../external/L1_Kaiju_128x64/frame_1.png | Bin 0 -> 1302 bytes .../external/L1_Kaiju_128x64/frame_10.png | Bin 0 -> 1332 bytes .../external/L1_Kaiju_128x64/frame_11.png | Bin 0 -> 1228 bytes .../external/L1_Kaiju_128x64/frame_12.png | Bin 0 -> 1152 bytes .../external/L1_Kaiju_128x64/frame_13.png | Bin 0 -> 1152 bytes .../external/L1_Kaiju_128x64/frame_14.png | Bin 0 -> 1162 bytes .../external/L1_Kaiju_128x64/frame_15.png | Bin 0 -> 1209 bytes .../external/L1_Kaiju_128x64/frame_16.png | Bin 0 -> 1158 bytes .../external/L1_Kaiju_128x64/frame_17.png | Bin 0 -> 1161 bytes .../external/L1_Kaiju_128x64/frame_18.png | Bin 0 -> 828 bytes .../external/L1_Kaiju_128x64/frame_19.png | Bin 0 -> 817 bytes .../external/L1_Kaiju_128x64/frame_2.png | Bin 0 -> 1288 bytes .../external/L1_Kaiju_128x64/frame_20.png | Bin 0 -> 1222 bytes .../external/L1_Kaiju_128x64/frame_21.png | Bin 0 -> 1494 bytes .../external/L1_Kaiju_128x64/frame_22.png | Bin 0 -> 1685 bytes .../external/L1_Kaiju_128x64/frame_23.png | Bin 0 -> 1680 bytes .../external/L1_Kaiju_128x64/frame_24.png | Bin 0 -> 1690 bytes .../external/L1_Kaiju_128x64/frame_25.png | Bin 0 -> 1658 bytes .../external/L1_Kaiju_128x64/frame_26.png | Bin 0 -> 1716 bytes .../external/L1_Kaiju_128x64/frame_27.png | Bin 0 -> 1741 bytes .../external/L1_Kaiju_128x64/frame_28.png | Bin 0 -> 1686 bytes .../external/L1_Kaiju_128x64/frame_29.png | Bin 0 -> 1626 bytes .../external/L1_Kaiju_128x64/frame_3.png | Bin 0 -> 1305 bytes .../external/L1_Kaiju_128x64/frame_30.png | Bin 0 -> 1677 bytes .../external/L1_Kaiju_128x64/frame_31.png | Bin 0 -> 1639 bytes .../external/L1_Kaiju_128x64/frame_32.png | Bin 0 -> 1618 bytes .../external/L1_Kaiju_128x64/frame_33.png | Bin 0 -> 1595 bytes .../external/L1_Kaiju_128x64/frame_34.png | Bin 0 -> 1591 bytes .../external/L1_Kaiju_128x64/frame_35.png | Bin 0 -> 1560 bytes .../external/L1_Kaiju_128x64/frame_36.png | Bin 0 -> 1592 bytes .../external/L1_Kaiju_128x64/frame_37.png | Bin 0 -> 1494 bytes .../external/L1_Kaiju_128x64/frame_38.png | Bin 0 -> 1489 bytes .../external/L1_Kaiju_128x64/frame_39.png | Bin 0 -> 1438 bytes .../external/L1_Kaiju_128x64/frame_4.png | Bin 0 -> 1284 bytes .../external/L1_Kaiju_128x64/frame_40.png | Bin 0 -> 1438 bytes .../external/L1_Kaiju_128x64/frame_41.png | Bin 0 -> 1412 bytes .../external/L1_Kaiju_128x64/frame_42.png | Bin 0 -> 1425 bytes .../external/L1_Kaiju_128x64/frame_43.png | Bin 0 -> 1397 bytes .../external/L1_Kaiju_128x64/frame_44.png | Bin 0 -> 1217 bytes .../external/L1_Kaiju_128x64/frame_45.png | Bin 0 -> 1177 bytes .../external/L1_Kaiju_128x64/frame_46.png | Bin 0 -> 1300 bytes .../external/L1_Kaiju_128x64/frame_47.png | Bin 0 -> 1268 bytes .../external/L1_Kaiju_128x64/frame_5.png | Bin 0 -> 1318 bytes .../external/L1_Kaiju_128x64/frame_6.png | Bin 0 -> 1312 bytes .../external/L1_Kaiju_128x64/frame_7.png | Bin 0 -> 1301 bytes .../external/L1_Kaiju_128x64/frame_8.png | Bin 0 -> 1308 bytes .../external/L1_Kaiju_128x64/frame_9.png | Bin 0 -> 1336 bytes .../dolphin/external/L1_Kaiju_128x64/meta.txt | 50 ++++++++++++++++++ .../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 ++++++++ .../dolphin/external/L2_Dj_128x64/frame_0.png | Bin 0 -> 1640 bytes .../dolphin/external/L2_Dj_128x64/frame_1.png | Bin 0 -> 1687 bytes .../external/L2_Dj_128x64/frame_10.png | Bin 0 -> 1630 bytes .../external/L2_Dj_128x64/frame_11.png | Bin 0 -> 1660 bytes .../external/L2_Dj_128x64/frame_12.png | Bin 0 -> 1637 bytes .../external/L2_Dj_128x64/frame_13.png | Bin 0 -> 1654 bytes .../external/L2_Dj_128x64/frame_14.png | Bin 0 -> 1667 bytes .../external/L2_Dj_128x64/frame_15.png | Bin 0 -> 1344 bytes .../external/L2_Dj_128x64/frame_16.png | Bin 0 -> 1251 bytes .../external/L2_Dj_128x64/frame_17.png | Bin 0 -> 1292 bytes .../external/L2_Dj_128x64/frame_18.png | Bin 0 -> 1498 bytes .../external/L2_Dj_128x64/frame_19.png | Bin 0 -> 1530 bytes .../dolphin/external/L2_Dj_128x64/frame_2.png | Bin 0 -> 1726 bytes .../external/L2_Dj_128x64/frame_20.png | Bin 0 -> 1698 bytes .../external/L2_Dj_128x64/frame_21.png | Bin 0 -> 1665 bytes .../external/L2_Dj_128x64/frame_22.png | Bin 0 -> 1809 bytes .../external/L2_Dj_128x64/frame_23.png | Bin 0 -> 1775 bytes .../external/L2_Dj_128x64/frame_24.png | Bin 0 -> 1758 bytes .../external/L2_Dj_128x64/frame_25.png | Bin 0 -> 1725 bytes .../external/L2_Dj_128x64/frame_26.png | Bin 0 -> 1835 bytes .../external/L2_Dj_128x64/frame_27.png | Bin 0 -> 1759 bytes .../external/L2_Dj_128x64/frame_28.png | Bin 0 -> 1462 bytes .../external/L2_Dj_128x64/frame_29.png | Bin 0 -> 1407 bytes .../dolphin/external/L2_Dj_128x64/frame_3.png | Bin 0 -> 1777 bytes .../external/L2_Dj_128x64/frame_30.png | Bin 0 -> 1408 bytes .../external/L2_Dj_128x64/frame_31.png | Bin 0 -> 1404 bytes .../external/L2_Dj_128x64/frame_32.png | Bin 0 -> 1327 bytes .../external/L2_Dj_128x64/frame_33.png | Bin 0 -> 1306 bytes .../external/L2_Dj_128x64/frame_34.png | Bin 0 -> 1341 bytes .../external/L2_Dj_128x64/frame_35.png | Bin 0 -> 1255 bytes .../external/L2_Dj_128x64/frame_36.png | Bin 0 -> 1059 bytes .../dolphin/external/L2_Dj_128x64/frame_4.png | Bin 0 -> 1727 bytes .../dolphin/external/L2_Dj_128x64/frame_5.png | Bin 0 -> 1641 bytes .../dolphin/external/L2_Dj_128x64/frame_6.png | Bin 0 -> 1635 bytes .../dolphin/external/L2_Dj_128x64/frame_7.png | Bin 0 -> 1588 bytes .../dolphin/external/L2_Dj_128x64/frame_8.png | Bin 0 -> 1608 bytes .../dolphin/external/L2_Dj_128x64/frame_9.png | Bin 0 -> 1610 bytes assets/dolphin/external/L2_Dj_128x64/meta.txt | 14 +++++ assets/dolphin/external/manifest.txt | 37 ++++++++++--- 125 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_10.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_11.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_12.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_13.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_14.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_15.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_16.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_17.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_18.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_19.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_20.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_21.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_22.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_23.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_24.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_25.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_26.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_27.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_28.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_29.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_30.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_31.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_32.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_33.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_34.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_35.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_36.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_37.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_38.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_39.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_4.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_40.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_41.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_42.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_43.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_44.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_45.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_46.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_47.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_5.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_6.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_7.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_8.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/frame_9.png create mode 100644 assets/dolphin/external/L1_Kaiju_128x64/meta.txt 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 create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_0.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_1.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_10.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_11.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_12.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_13.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_14.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_15.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_16.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_17.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_18.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_19.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_2.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_20.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_21.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_22.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_23.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_24.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_25.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_26.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_27.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_28.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_29.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_3.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_30.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_31.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_32.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_33.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_34.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_35.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_36.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_4.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_5.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_6.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_7.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_8.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/frame_9.png create mode 100644 assets/dolphin/external/L2_Dj_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..8b8dc80bce09adfd26988d9021687d77b536961a GIT binary patch literal 1312 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_16.png new file mode 100644 index 0000000000000000000000000000000000000000..fc7b76696c1140ed9f17af4fcccc840e8a07b32b GIT binary patch literal 1158 zcmV;11bO?3P)%JcTp? zv{oW`0;vKh&L>>}6$Q?wBEZL4B7lkl=TZ^i<17#h&}*D43Q5 ztaiR+Bf<%sjqyIQ2q4*b)e?|})&bP-S%W;Qqe4ch;l~r zASch7RY1pQh>!KmS2wa$jm*8CyVB4ljV zTCR;<3)+hu*ZR{iC1a9FJsa$RT05e!oa~=7bDXpYkby`2vk`!s{n-CK`vII?s~s!# zN|+J2rdy&M7ddwA3A|bWSxb;r1l;c)=SMEDk7)(ZK5+D`f==+f_po#3ca9f8L>8X` z*Z+sd!Q-*>DO=i?OuS=rqyR9dpIlLB*7otUlt^+msNcLoI7R@c&U9}>%X*Au3BZmW z_e3n?w^aaIr;lN@AB(NO8e1o#T}~`Ev}3SL0Nf^t7AgD9+T0D*ez=Wwf3ADtp(Qp z+QiNh_NnhiTiZH;vvurO;ns0&-QPcBc9(4i&2C^oo*Moou<%S}@*S*G$aOsX@m3Lp zz7$G5r1re1T|Mv4nCChpTpplt4CibJ)$XRE3q@!S;E4#)4wCi{qKULW8{WCnlp-Yw zv~GxSzhkz&_MU{d#vetXiaL=?z<-8~2@U+sixh^Vu!QxfjU#L0ND~Rf2w+)*v}i+_ zl~uD};{>i%JcCsP(ml8%fRt!p7qqs0IU5)&mIrU58$rgk-8lePl3-*cO3}8x*Iz}( zwWqg($7G`e*B_6AqMhnpR^Con6@Zl>{rlov!utRbjyH9(UR3(+ApDJ>S!b+h+7$oz Y1sh6G!Jt?Yg#Z8m07*qoM6N<$g7uaj0ssI2 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..013e2008a2f51e46a24ea78341d0916c5c7483fb GIT binary patch literal 1161 zcmV;41a|w0P)3~9W2H_#2~le4-xjm4|1^Uy6C#DKO!8|2b2Ie4hi-_3IT`?33fpm z0f-I>c0eit4(cPF0LKc{#<2h|2c!-#!fge1uMf1AEV>uuSP?8Cz_*|kRD{r#lwp|* z__kPJc0ZMbPjq5{5WFO)CVZkpfH15is3?4*Lx9)ggJ=i;4c5^FK73V8h(g;}R5iDFi@o(rRI(>#7}kC4_@Oez;EwKYNVXIjXzI z^dJxkZJyCVXPMtN$khsA70o|U@fg4pTxnIf-I4jc#WVqU2C$mlQr+PREz`6=az6aC z8CJwi0IlRJy{~Dn1kXk*nATg^JS#v5zI_i3Hvyy#c{Xi1lxn8iVr5zqv_E@D&`TZL zInh=C9s}^8Xf_Y7R9g1#197X^D%YC&Xc#I*tZ+M7PqTa1poA|4w&=4skA@A<3WEoA zmM$oF3)&p~^$Mgv8ipLJ0-<^&u($+^)p(*-aBc~kfH%F8N2JgoWmlG1q=%9O z=n4I)0C2XRF}lX$0<6M}#1dr)u!E)}Ve$W?8%(74Jb07K3SZOy$Xe0~5KUFuP4bPdbQPn6_6ou(d}+LPt_TUz34qX& zb}TLZ(a452vsrF|&q1VT6Vbhv2?9B!f(ZZ4f8PUANLI}o5oDH-()_c8ELjRhXlqtr z#hW`?gGgGJMMzqq2-%5*J{^pJrxa=(Cu@tnwf5YSX39O#K3g^jw8EoBmCzGP&njqU z+S5?t^R(}k3<5_$G&G)uR>0vkj27ITNYH}ZZ3{sb!S6(h*0?J6O-jWS?04oJ^-}DV#zBd5c3*afpEIitOQok7Bb%3ZaG6j0v b!2kFKs(*8sivuK+00000NkvXXu0mjf#}Wg} literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..795120e772fed8932017fd12c20a30b2fc3ab5be GIT binary patch literal 828 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 yLdN#b4ZO4o3>$ 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..d23ee492b07b731fde5b3b7f4847ea499e4e23c1 GIT binary patch literal 1685 zcmV;G25R|&qnJ%ymHr^`B z9Ai8qf#>->ea@L#2E$GM7H{bpTGkzVf59mIV4P6?F8nuuCcd-s#~9Bdz_`9^fWlPbTZuX`k-CDV;gzpAY?A z^Z?wNwUIK6%twzBe+?+UoZkA`17Dk&>&ZjULXuOXDW22wJWu6NBq7k_7h!`+unZ%5 z$oxPlUF{sab(TC5{fzNja#;M|*&9gV9bQ%GmD@w-X=^06Y2A2@egu{wTYJ#LfyQ4` zJf&yhU2(&lfyF4RbW3^s8Re-RfcCp|$aGFbah6d;l~3XAQQ~bqkMM_i04fw0u%h(n z;YB@(BwyyhEo9wphMwomx_EVBG8KfnFZ%Hk7Je(K3tZ1E_h%U^A2s@#!Q22hN-8e} zKRf;`x|fy*Sj?oKC4r5VcM;$huZmU2UseH{KEMw5t zMfP$Mk#Ho<$%e6Z72pXt#@Bkixg?9_xkh0PE1{;Y&Av zjVKQwLAvGiJ1Pe^7Bmp_t*|R4vRQ1c0;ElJJV0inUd)%|+vOE99@I)qDKxK|1;hhL zo1j8c!A>Y&lE*q6Eqrv*HK@$`>KQ?D0LqxpoHuC-8vSV?|y_AHYhsxHs^Gr*63G0d5%1 z7`>!SN=PI7ubxx;&jfntB#<{f0F_}k_&&3H_Cl_O_ab1y6NECA=2i~!w(`4(%3i&> zsPlPegtVS%nSWJ4zPXd}SdwE0w6lqx8RBxhGHxacY7mI{jZ^@-*0-ST9VtVX)@u>5 z#xZ(Ljj9LWG9Yo<2&9S!i7MUqZswhAxa;1e7+LX1-7uh4MOKrnB44Qtqyknme=k3% zHgR_0c2bG(0#<`<8AD{B5D(xXd*J9!+Koy=Ym8~ha_9C--$l>xPZ`+hz)8$#BB%*- zLMfkCPTs0!G(bFn6#_cXJ!H$JlSZKNLzl}OSvrs$KoTOdp0|&+ma>&gM0s%+7`VCq?~|`o~Nwv@BqmH5Vwi*U2-j0wLzMyy?mnEREwZPJN)MXP*_UcQU08S zX84w&?lyvomxTOV!Sq%m6+!7C+oY15XahoAqU_qrS-j}Ns@tNsn!5Q8xTipd04ito zRz|84Y5X*_J%FU}j*G7*b%+~`pmDpgrJbMleY+2!HEuZb3Qko4Ram^OMo^XqQZ5eY z{}+%+in8=}RRL%qqe2I1@EUe{xOb;%;k9TZO`hA0 zpk3uz)e|6<&BM49*56$PR1>yrZsBF!{CBGi)7~G@gf?4MaDH9GMCCSKE@!?A@LIbI z89{g5xCl*&x7SavqOyLMtGA4x6#~Z4ZzsCW_6`Tdzk2h~)yS#JCL97Z@c5#A$LB|( z&5wi-I`Z$e`TV<6i7XEtoJxhyTCbua>!M-i03C4UN|wFzkWnGv z1m9|2RPh!(KbsF&3(~F5Djoe!;UrEPP6b>(SPoz*{zeja3y2qAJ9+UnZ3FNWf9p-% fcS9$RcX<5*G3~Up?wLwo00000NkvXXu0mjfNd6$@ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..9d368900c94f1a6dd069d22c2a5f79cc5ce0d497 GIT binary patch literal 1680 zcmV;B251O}g^0MJHLB< z3T^4WCj4FWt?PVoT;n$fXw$y$dyC~q*A)f*J%``>(H`JQbouva{v!LPR(@UA$4m17 zuX!_am6*uP?_N*Rw}hZs*Gl@@eM#UN|6_c602C9ug>X{&qR3c!R{7~w61=6n6K}Ky zdiT*DpmV5ErIYAO{789#Hdfn5RtYul6A}IxV_f%r-<3n5ge5m5eeSqhzOHqs-o`WZ zC828P;LWr6@y2O;*XBeO0OkQWV{}sy3)40ewH}GKHrBE@iT?;660N8ZxMD?UMU}udAv*Q6eN?=+2uEZ}wLkeA)wX)k+WnBB|1lIlGlKIG{OzS8iFGqt=HKem8-v z2*a9N6>enz)v5sYGl3G(itwyzVJCKY9Ewh*(m9{^T);vmagms*RuNs{pDOnxFBnC1 zEXp|Ekp(J5v>H5$&LN_J^gdqrlkz4H2;N>9H=}_jd0J+l#cAPp(Mg&!@RDl}uepeI z9m9U$Gw*wOLIzI}{>oec8KSB@zE=~kZt_(8MnVfA-JDCKL)VR--6LCUhMw)3&?`pTo0lZ5|!V->*Q{cp-g&RmfHW zokS_SS9(5@!XiA3#h0g0$2x(P5)etZI=7P4cl5}b$3e%e(4cY_vq_8svOE2;nlyb40%do_<0BZ2e*GT@~U3SC~SRe;s# z>0(E}0@4S_)YO%A=X(j2O;_C|r)%w%^Zk^vkK;84=H~(E3!w1-ZU<~;w*G}u~^zxDD8UT_XsZqt(e`osW9A1ky(&Rba2tur- zb>p4@w-eYEKs8~DDi?ikJqzHUt=vJficE*nc06>mdn z-S_?VKZ8=#9Y|K475Y!>`%~;xoC^Nw1fmy~1b-rlJB6K(e-Y=}Rsc`%Pi>Ao4HaP` ayv-jxa^1*R%sztv00001vA}yFDaTPfG_w8 zsoK{V;~EKE*Vpvd>y=rS#^fXMdcD5!x904W?Ue2(`G07Xn(z!B!5Y7=>vlu^Cfw=( z+USwNuIu_SZW((1{Lc|EyyqsNPmS#QX&7mo`I0pCD}pso($wQ!ojbaBo*DkpOX4*S z_5j*I-dIFG;*ZepWCN@JIGJ4#p57kF3@Yaw$pBWwdt>ui;ja~z(8_o9?$X<_?|P5r z{>t~?^!Qr)|Ago%86xu|Zr6s{8i2!HRX)1#CLbykbF*3F`eKC}Hq zWcWG0a;#6s2%ho14Pm6gh(8ixk`9tqGw`yLH`j7151#H_&@zA)YPaz<|Be$#IT@Jr z!`j>AYh@r!AEEv3)+ogIQsBO`7e*9p4i<4z}NX8;K+%Dg#uX8?;<7uo2y z)}IEw2Us})GXqHe^652xQcjN+!x1Ww@?{*s(kJ7Hty2KPP_JcX052%JKoz=Fjqn#*zm79F%9 z&6YP0lpbK#Eh&($$$CDsfm>}*gsgL)MAVb4GA#R=>a^iTQzUivupfnEoSxuQ;tdZ$!KQ)1hP+v=E%8!N(MljYKP;Y zpxpyRV>;3!8nd7H?Z&UjY|YO&hTLn^sXkFU)=y8NYzaV=xWfrLWx!gC(9yc-hB;a% z=U3(atQcvbDyT*SyDmQsk#4Bo-CpB^Fj~z)OF#zN_^1PQo#wYXT{#1wi?aDLGv%|?MG~aV!^c;ipyKBx!6lO-#oHGew7p1GN5x#MBz#v zy#|#F$ucWYdVp2xL)KfVT5nLawhBvDFB4@^y#TZ1+LcKt_W%r7&6>;#NEz8B%WCsI zhnezwFOLo*6Qc4{dVnW>F#ZkDLOkV3YtWm7?>rPL0-nZ%qcC zY+3%49)JbSGkXd^qcnSLMXq+O9%jXV=l-Il@#xsc-IDO;hZD=KHGUdtAe#||91sPdU3Fy+T8U!g{nPBRoQV!p9Vf0)< zra_jXx|}de05fB)dLZwZwhNIHSYuo60e0NZtx&yoWypx$99DT{b<1Reb`KyMA^BM+ ztxEnoNfvJ1j?(??0J) zbd4x7SL=~J;p9aS3sy<2g4s~JH-TLP>}tNO+pVYs==WY`%AS`i`Z3b~DW?v5fJkub z3KYSvFF-Qo>I9yene;xjO28O?bXISs^m@r>B?pr~!gGbH6YO#v^Bw>#0TEG6-1UJbyC>a5Z4zE$ktwBuy4<=a3>*fRVbP262LdFRjA*R}(! k4F9CdkSF8hZK34#AGJsPjXYbvdH?_b07*qoM6N<$f=y#C>i_@% literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_25.png new file mode 100644 index 0000000000000000000000000000000000000000..b40333654f83c6ccda04cbb4020621e79e7dfbdb GIT binary patch literal 1658 zcmV-=28H>FP)+!UnaoYS@m7gp+d$;h0t>nNG@)Lx(?w`-+ z8$TXTavz={JsIN$pjqu*!qSw@4Z-78<8yfw%NU+$2-3)3>x<~!T2Q-7L1-tXLq9W@ zUzR|YGOe-J{^effe!Z)@_so9f^Tp$JU3Ae<4xlMNG7m*xj<@Dm>uKR>=gs8!4qx6J zZ@d?7r7nnw-s?g1t`U^uBm**e>f9srcYJVILW;sl2zTCNmwQ+|N?xP;wRn2}X61W| z*Vf3u;widT#*=EawD4)|*W;<>0e0h%%Foc>ZCb7&1s+A^;sN+)ITq;&Ec#0b)foH)RJ_3Q03Aba=Kw^p-Z*FWDIVZInjV12 zeo4abT!9lFKz=@npKgGy9H4>;5Ad6CdOvLsz>^Vv8hKpS#-lwz7Ed;u3~yfI9YHGx zSdhfD#85Ja@BrD(?)ZKZ_@^7+~Z~e@c_GLd{bx- zu<)BI0`5UAXbE>^|LwPdoB@kT>CUE%;3q>$%04o`j*LtQIfpzbCAU<|ls9JbK;)G#9a)2Eo znd*cQ+)*WD=|z;B*?U$;N2`Ec7+GVw#)k?W)6gm;qIyNc=S1hfJ$Eech$zNvJJUJ=(tzu&cijRX}#m;tdIuAy|Ue zzOPg-(Xnz~x(Y~*HZTzao}R%=c>H#Z!h_diS!MT5hHpN}xhy?G#W%~!yknP)UnBwbos+BwB0r3D{2#7xK z^a`@Dvhp3D8f_~E@6h!C7Qb5J?(qc40W>Be^JSApcE;p$OA#GjIWJbdo>BBu!dJKz zj3P!aw_H|qhaRfM-Lm*4InLvnyjwP3~hE}o!#q8c9+IiO@WS_N=u%9r13 zCtr@&XnR7}Txm7FMo`HCs+Ek_2RNDoOkpYnWWrPhk@?f|jqU;739OWWNV(JT5KdG9)#R&dz^IcSRkVdymjSJbV_`A7u85b|8Dl4V z&hT#0wSa1>MG!dz)|eO3*Mv;FBe4XI7RaeK@`5GMQ+*SQpX}Og4?sj;PBqeK_dh5I5qeL#@bkw}W9xJ^4f zkK?$G0Iut2`FWm^Z6EuQhL^6i?z%3QOcH-?9mm5X;}j4{}X|7zt#iqyF}1>KhN{S_*^82@b|3c z42PeivFVF`PO+X#dn0F>(7h%PmHj*hYXDr^aqFXDIZ|_2&$#pKtP?!6wexaz7>@vw z@ps$;R0Q4#ZrzfNOCJ({Oe07`dUc;$lYxFuasKT6zd(DZ46XI3c&cPCzY2d%9!`;- z9Ir(PgtwdF=u7l8y;{#D+V6}Jknj%UNBCoekrYGa#I;^<%C(G1`DzhMgB0OB@=?6A z%3locGQPy$b5kT55lAw5X?da&(v_ZRQOJ^|H3CTJFn%Tic?@BNehrzM&(X;2%k1Is z>bikcu=N=gbXNq(D5pb_T?X-Domom3UU^;-BUnthb(o7Q?-Kut@t=T9?XmV{_b!xc zfCViFqp%YZAcKnHkj}R`-9jk>ENGvjmrg|hOAZN7Fm{YC6LS4#w-2A4jk zUw1_Si?4=na2!iUs-Nt6N?kxiNKWXE2vEUOYX>ZBIbg)aXI_*hfmC2kNV=~Q0iJei zC-T3PI;C~^EUKe&4dB6!2=GJ)xdXRE08R0DOa4*>=!BFi*Vj{Mi2$n@!`J!@_hRnC zwOQkH9mh^g@tdqwM&dDfJuI=Ri4Njol{%QW@F2G{SRjV3}6+N=ypYEY8(!?!7 z=dALp%86;iL+h=DV|?%E0=$Rlbb_6T^2^pY^uka&QM@TJ2~{O#510-AU8!ptT7ev) zvG!*0_As>8M7jVkP#KxrN*!;?5$-UWL^Bluyqls!2`W^zFv=EkT2AbY5MElE;j{8o zi^mrgOnR4u&IlRZQ?24@QHC8ym2O6cE;&%A*)z8MZs0@;=+58WgB6r5QY3S0JEPH} z=b`zgo1}LyQ$!^NOy%rsXJXM?;N-S=bvipn8VlQhJJ^HW!y64=Su`VNoFdCFs_Wl2 zurh(;4nuqJ==@+3kwVaWbGwS&JJ1pVECqT#ugnUfh*a!eTG7^SD#~$_Nlm7f8h{hR zvmaYDsio0wSVvEdN&TbZ3yMRhB+c5`Uw6rx^4=%|TL3ALLO+OZY zuN*BkK=&|(n?rbSpatn{9REz)Sd9RZBTuD_)-E8LTeYe=lL8`2vYY_VB=zlFy6`|s zJJrjC4sKoKmKq?^El7F3ip*a|O9%rP7kH}MjWq9r~O@(|< z2PCa=|2#k!mT|gf2gkA3@RT>FA5T#^=>$>QBR>x?(b{P__pU-!yLsGYiq0ys@`O`B zbq+vpH4XC>V5E@RxjVa*ZCEj)WC3z&2YXIu$ zRH>a6d3O|$=wT?o>-Kg6&w<~+qjPi$YeefA@=Uc&(u9oT>7Ax|&{YIed8^RQq5fis zw+)NJqtwbY&l-hBCTE#Qz}>nic10g`4&be5Bf>^*R8(=C)8I48f2R}N4JitgQowQO zKNwNT-5v#6!=We42(l4!0-QYV6rd4B;@mS_9}zn8d_j|4s#zlRJ(z4^tF+htbP8BR zEL7XT>_MxF&qkkVzPL7Xp=jr}^$$m8(J`3m+#iXWcTP1mUcXLo9I-zeS7l7$ZObyA z-tlh{yAXAfu5@*tr~m&4$Phn{Pob;rkrz8dag}ejF3OCXKZadbvlX;f44+#x5O-Jg z9_)0S3_iMmZo*m*9>F&lo5RkTe}`Vn4qzGnNyCsQW9PJx^7;pT0Jg)RaSLGp0000< KMNUMnLSTZld>|zN literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..0291dfb5830a140e9604c82b66e0fead0a9d8577 GIT binary patch literal 1741 zcmV;;1~U1HP)$@V{Y{3g8ONZp?cAc^=h3_`0oJ ze!%-ZVw~TKw-`Xwl05_A9%?;H*GT-SvNc2ajd<-X!rt{;a=@6p$Wr2wut zjRt*QTC+b(a%sq(UH2WqpTDkks0QGNOKD~3W%z3b5S78I0lMj{g6`fo==viO!1FA6 zph*5w2$y(kQH0;8*w?LSf_%^Ho6Anmfz~_E^Q?Pch`dWec3~6A>V@c}k}Xlt*Sz-7m!}qXbI`O%SU1{kduc90=uq0xGo! z3a^d_Qbg0iR0OC1b?Dz{bQ|pmu$Da|kR1ik+&9IDja_^7`5KRG=cWxTA zpvfwqM}gB30l}|JBWoz_-vsV^(re$#2t^G6(FL528Wn!1u4k{yocU`nv|e3wHj_OJ zy7}oGDz&(i{3y6?sz!517JAQQx_~I8a9&w^x+u)@LwJ3K@5UWni0lHQ{Hh*>NF662 z$=414S^P@@*85t${Hpiqfg{L?fTa5q3xyKh&NNKoQ<+U@_oQf*;Z;#S%3vT7z)Atp z`;y|PLL@ctPKztil+ZQ646=p+sR1M*BI|j9Hm!Jxe|5yopF^~}NJOJJvQEbsZX?*P0`A}WW(N4xLQ zl#0Ks(^Q>6Tl`%1$^FM`aJMz0i7(Fs83)AsD4;$&wkM@bn|`$QE&0y3ypG?y9nTXmH<94 z=%Ea!Uj@3Zp{aWB+5E$hhytpIpBc1d=I&k{6E#+CsN_K!{8S9=RPA8S0iqRcMp$I8 zt;v(+Kb1Zvr+-;!#@tE)Yw6zE9jtJg-a$?ecCzRwe|lt05l#UXQBK?%tYj5pjW#V< zxj1sIIvnDdGQh5J@Ac&ru!(r+wt>}+b`L%qaZd5&&sH3YI>%f8$!~Z@&h4@pc|Fhb_kRsbad#kjbyn#o=l*D%G@J_l>jKb(t*=Hi_y%Q5I63o2oGaufVVp3% j_9=sqPshn=A?5W4au)}*O&X|=00000NkvXXu0mjf^msif literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_28.png new file mode 100644 index 0000000000000000000000000000000000000000..54a889d8151209f012e5b42e1da564793606c3a7 GIT binary patch literal 1686 zcmV;H25I?;P)wUdPS!FH+Q14MrQ7gCtMTXN!pCPM8V$s7-M{BD=lCN>$uj7JR8QwhAmKV@?)%GEc;faQT`gqR=-n+G+cDGI( z3M@-MGFM*6pX5y&y8@1t`@^-gE4%l|e&zGY-}g0|asVxUGEPMFbY)z#CUDFFc4IsF zT8-D%m9jtL0gzFRf$ZHwU*p3%mAFn6Rw&t>dM#|M#(VaS`<>6^&xnsaox8in1dc;N z$jy4`ROoAbNI|WH8G*)=RvoP9c|4<3qD4!SvzMn&MG3p{$W%r75&GWEugX3TBDAde zQ64SY_{_^wn+HI~*vjA0kDkT~GRpTt3mKBG9FyVc9Z`Zs#AC$_BkVW6fN&^d}j{e z!5xD3BD0rD1yt$-NT8dS%xFm(!voCqGI+ArKsk1z)Cru-V#beP!JBjKK&b*|rjOTrCpW^(0e%at{q1Bff6jV?auwjg zDGxAdVa{

A_%cY^nB~nLryOmg z7U49U=22AfNPGVgMYdEvpNqd3VkwD66E>@HMdrqGRj_~tkC#`hpViMCE4~fXHi0L# z3a~0XNUxTuz7?`aZ#gBItR?g;=G@#`sSq$HmNM?n>mcMM`hxI|h z90DXkpg`O+O%QE<>+=aIDWdv$=eL6Mba;5xM&MzVa0q}4W#-VvMm@KJ+O6N$JPosf z6gHwpK&@v%%ij&mVpRn%IzGdiTumSos#3}17`{Inmw}b4D(=oF*WW34*t9ZvH2?nq zUSJ_9%kRj3qlg+%Ch7I|tzgFMDLw@*;o$_3=D;|mx+tOBaJ<0smE{1^!S6`ob|ETv tSDy1v9l$*Uf_Ctym1|E$rmB}&>kp8-&f}bY-Pr&D002ovPDHLkV1goI?@a&z literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_34.png new file mode 100644 index 0000000000000000000000000000000000000000..969b91193d409a832d6d33a097670ca00399d371 GIT binary patch literal 1591 zcmV-72FUq|P)N{wWzs0r^zh-ZRrN*TD7s&hj5Nyx;{jwADBgsG+(0nsZ^C+HrpYADRK4=SlBA&5=9|AUP4f*WGgRceWk! z=kxi_vKwg(_Be<1yk{IK2go?7hh2U`4j|=n5IaV@{lGJl z{GF{;zDK{;*aT0^kBmKr|AevHNzii7(Vf=adgSBzt;RJufQM2}!W*8nXDCC^x$iW- zM?Rh3YTOUg$~NOh$C3<>@88A%5j~d-&_&y)zf+uaNRuPyo?V}MnnqD`8MaTa)XNCeWZ+{pkIz3_WZ zzjg@C&#+l*4j_4DCQ({V<^)#yNz{z5S2#b_No%=hO%7mD zLfiHDbyXI8E3_mGUyI}l9eSD>KvF_7JiSK8sW85U9cVo6awtzS2G4t(z%%A*9kH&d z5Wp=a-Sr^Is^m{%0Bv!t*v$ZRy!S7KMi$z#>#6dQtT=<+PUK+#cz$ONK=mt00xeWM zr(VqiI>gW4&H!V4*EPxjFqYI-=Kz40Wq~!}D=+0HsL7~~*If%IA;JLkS`?8m`mz|G z#&qr;H%=seRfYlX6w=KqnH*pYdx^IS?+_p*G!|;NX5$`Y4CT$pvxw;0p`#@Spb$fV zXF`?`;YD;o$5+F_B0cB`0Up_&@pl|CEn{Aj16UrkB!gPN z-AAz%x^=pl0VuZoMo=ZANY8r{#!e?V#Q>o?K(r$_84Mgsq=k-9cLrU>CO{+)_!SJW zDqdLbON*vCk#(VW*99!HuFe509W_`d$5rX@81z`Wau6!-gyE@d%X2@T0jQC@6XN=L z$KvSs#KKzj{#7|Z#U!|bMTH}E^s(Z$(+`KzQ-H_w$A}+0HSDPNRp>yAZB=r$mP1(% z;7P_{?9M{WX(s@jU;%6Se2fAHupWK5XQ-hn4R?Dk(B&B3@LiGvc+ipbtYn=qL{x@$ z(y{dQu5bF!!>Q1Y*p>yyPM~KeJ8%paX*-e10X)Ypj!|~TX)gR|y8RWTa)9Vr9lzg+ z6Nct;ByZb^_xA+uQUEG`r*@mQeB2@P#t=~Q6)_M`cgO#zF;6(btuW;PD^EOGgRd+< zcOnxCI+6a07?==^?b%9RcOS0bE>^fM1Xw(AmEmJYI296ZcY+&3kIDcMTBUtyl%5AU$shA>3<^T+O(&v|Hf>+}PbGfp7>I!=G??kzLpfEDl7k zS26+JOkIv}^#27|5#7NVMCmtk1kJ?tDCQVr+u>I_->#Ck$j-{{nB6r251Uqg*0TJH zm?$I)+vVk5^@T3Eo_?9I@NKg_#a7_FGjmSGI8j;j;`S;e)vqiE&DR5;S@p!S{sCONsyh002ovPDHLkV1kQQ?b84N literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_35.png new file mode 100644 index 0000000000000000000000000000000000000000..a72cf1823ee1ad46ad72a8b4e1e9156c0dedcc88 GIT binary patch literal 1560 zcmV+z2Iu*SP)#en?wbq{JX}$ONb>MlP_v`U|ytY95QH*}ta|<5%J8)9=F8MDyFcma}&_&l2lAsxy zPJ%m|hKRHL0mEy&;D$zxqk$V*dTu!v#iZ92oYWItz5@r)xSWQMF(6|6+c0*2@BNE@VhDlk+2Ex8^@#%y!baO6~Yg+kN%Or}LYQ3ps!%q@08|Jy~sdgy3_p zG`>eZpWkfU4`^jK<3`8Q3{Rim76Br9P9wmsuzgC7Bv*sw1h+*1>%KK^83&M{O{!A% zEDdN*a8m@}bE4PTxL2eAir%|8;cOYhuokQoP#pm*`Q!c2na}Nt2%t@b-;*YE5}q7< zIs#}&i2&KV67k^i+!Gb7MWfF>um~2vR!4yG8qxn)1eg&OYtmAVL*UsV5oo)zG6Eoa z(f5*m+Z`Zkd}I;mm;;ux_%*}$ zQiRm5ydM++BB50B%3V8v%eeuw+?+B0m1wq=1882kLX>8cIf0peT4<*CGn}96WL>4U zkOLq}Si7FS&SJs0!XjbxUW6;`&=ZaTni87f`8_^PhVhZ;!1M7ghw_lI(ca?(o-t?Z zNOfIG0n%dfT|We&N`9vZU@b0+>IlHcd;g=*$U-B#o+}?=#Ve?GB2NSu?JGF|*Dr+x zBB8o>yP7w`A#Hzq1ZeGlU850TjHO+(Il#aRSzt@}%uA&SS~9Zp^{$1}5QzZ%UKEiu z`a+D)bGr0Q8Yhy!GSh%`3fUc1RyaUwc!@U)?-ZaB8YHx%Y}|ttLwOnbDk8e>&=KJP z9BK;iObAI4UPKqPwfXV0;yCDOf0E;TNda0YnoY_ITTud|r5!&0Ob0&Z?gd}r0INVa z09~`*KR*kQiY3zGM)OeA36MUwoDiaC_UZ^=9s_BJ^x&kSybD-y#6-rth65lC5i)2& zbC6TJcjvjfHD9DW&H;7~jvj3&F{AfAa=e6brxTot0HHcSw4*f{baL7mU2B_BD4}w* z(?Em={D=tPsU2wjH7BqLD7w(qbpb@y)f@onm<{cmP8UjZg4Xm;CTW{W{Hh4xNg3$b z-5Z`Yr>TZ_9exA{h*S(`EMU2Q*4lhG^*sS)qf(aU$29SmFSY-E%50YXv6y>;}$Ijim@cLoEHf6{iba z{Id@9x)bl;30zTNqySpQyVViU^6?ItH>QAP4j>sEMG&09U`pw-hMPr5JZg^tQ8jWrDJ6d5{Be;^_X zuA~%FcbAA)5OtsxQs`})5gFhAZCn;4?_6Q26xF*jf@;U{g6o&Y0W8CxAaSP<)w_!4 z@IK29Ff;sZweMRIF=mF-gdHFw)c;`(8BXJN03_7^?fQGco#r3AcOtGYWHw&_0000< KMNUMnLSTX%)YRDk literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_36.png new file mode 100644 index 0000000000000000000000000000000000000000..9a13e7c67db281d96949e0be75aa7ee4197dfa96 GIT binary patch literal 1592 zcmV-82FLk{P)PbXFRCt{2UE6ZqAPmGE|Nk$$Po#>DgjN?WZn85O5*vg-Vp%v% zdOgqcv;nT`YQ2i~z4WtWM*?r7)#LT8dNZ)I?^*rdNT*hC5oqn{V4ml3B@s`+U-)4r zKx_Y!U_~z`ls_^*)2%`?%=8L8uO89;1H zljGedExV^MK*Z0o1mF&Ir&lC`h0!FTex1$$qvs_Ipsf_H%saG%klu~#&e~40wohV! z4!-CRyQxzE4}RZQC8y}S>$=22NyyOj3_BgG8DP|Z|GAHo#~{rBS|;*m2ut*K+-cr^ z<(3gij1w7PG`_F~KswRhOAH~EQew@(XwS5u1%a2VHGt-u=BJARX6(PC!L3~NS!fBd zk^vCEIIBVoAO&QUW!5`s#hns-50&zE98?FbNP^y{<`3fEhX|yV`p?z`QQ^Tw2zMS4 zIBy3X*Ql%rzO+cSlDGTD0NSdr2oN0vIykNU8({6t)fyn;-J zVgbunFPnLjz++P@C0}pa5HaKhdI~`7oCsvaTkTge0JnE%9T*87>D_2wLYDD0Hgo)umh2FL=@ z035fqGNLO-OM)GN&WwTDK>PAw*PL{GTm(^MokN4ZGdZfUlK~J7Koj)3uJ88%ykdoO z)b5Tz@9584f2DsP$3Usl45yGJIE4i=iTXMXz$?xOf~53U1bat9oz4IpW&bSP8VD*1 zwP$a^=^B7T)c}>?!lT1V0zOHeM93yh^f>q<89*Df=W>Hjbj8F}CAIes1i^6{pfVHD z!SNFZKX+@#7o`6RT@z{hBN>1qG9YQ9`?K8>z|4V=DhMaTR;p%;0dgnE`7r_Lej8r|Ob~ zp5-N*(u7-q_N;28jwTGN@TTKxM~VR`l_GwCw-b;-z1)x06LrFyruYDUIJ!U?(L(Hut+6>gsXQTvh@)x_LNhUDOVo4*)hA$o~o z4Lo`ah$?9`5j;s|F-AqwXTcZsa0@^$Ya++38Hgm6mI%>cNl9=*;3YiIgKq&EMj9X+ zY%jZTdh^~VP#YP)`Fxxfc#8BEARSvqM;UZ7tN5PEgg2+$wn3v{FPb{)C z>Gyr#?Js)oZS3~m8wKfnHg?MY#%I5D2tTLaAH!!)kZ^CJRS6`~@=5KKh` z?GJq5Z=Qh}@-6z2HGmgT0Z0P4uN{L7S7;2#^z1FA=c)27_6a2bR&Xc*_a#W}Cq8C1 zCLGI@D>stQBHwDCGy*^+pu*bgF(_hqoX?GA#fcHXqbExMt-~TQoBfm_R*br;+i4MC zcHA8Ssz@NEi4}&gIDLkbA^@}#-b&+sbO998GBK2yIfyd?B#y_35qei`XY#QvusxS+ zfGiSNW~P(^esT>U!CV92L9?U43Opo;r~%+Onp~Sz18}-=dQKw%9uymHri5g?tX$lh zo&=HL2vHdUGs4KeR+6cr5Y)_g>Hf+Ih{p?w0Jx6W ziCG$`lCOca7O=>k)c{z(kO*K!WfZ6`1icZUHQVm`r^eKb`(*5l0Gb*A`lNU8HjrB=kU$k&)R;d7r$>N~Hjul> z@*q_29Su+3&S+sZGBr4+2AB=@&7nFAr$+#c={JEE5@`EQ&jau396UI^2EY(M3iXhH zQjFT)mHRS@^wiYqm>`oaz@}u#p*yv4WKoqmN3@wP9i~T z^1souZZq-r7eZhGmL>eYKmUS-7pJ`>!UgOTQot+95%8x9E9(Rq#Cc-9^3esr-z~KO z#Og9K!${ zq)T?I2W3Hc<#FpFv+Ln`s&3Gvq)QT_0X2qXe2sF{djpLwxI3^cYH0}+0 zyI3~ErF8)3WDm3v#X9?`alIsv^7-zTFDk-`Uj=K*{1`zHZLNsq17DL_(yTMzJtPRU{3zTMJEJPFAvKYk(DCC8>pqsvg>d>X~f0&$?DIvpM-6N3MWkr`72v=2tCtrvM39zpxr$=J*{& w+%9TB?jEFXW0F?irG_|vx-T(jq07*qoM6N<$f*O#b7XSbN literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_38.png new file mode 100644 index 0000000000000000000000000000000000000000..93da7f4f94b06909289dcbdc89181a3c10a2f39d GIT binary patch literal 1489 zcmV;?1upuDP)1ZUZe~C;v8JmN$l)F2__2&N)} z^#yMgWdi9ag^HljB`=k;8D>#&ZpEYRhCqCyj zCY;NaD>stQBj0PEG6Fy)pu*Pi7!)xg&gbUx;=~9L(UT>B)nS>CRzGEk8KchXc3K3G z&W9twDiUaEVus-}PM2^}1b}wJM{VAZE`VZMC5AGRgE%8V;zW#?q4%nzBp=%X+jF`G zs3L)9WojwlH`f3f+%*86v^omRAVPwS8UW5?$+cBA0H+(Lrx*e7q}Xth5}NUR z5@doS#L5U@(F=a2^t<3d&iR}Hxv(rLP)!C-PPRNLFRvoOkjd#|(eDMnfbXpe;DV#~ z{^Mm;E(=iR3bJuJQ0W05Gn`M=Hrx?k*L!a-tMj#x!+pQX@RY$UMy%4Mwa>)>7lk(h zV9b#EM-5_j@Csmjs?UwKM{Cy;fJEE6KE?5VWj#?fR7q5YLwq0dO6$ z6H*#jCEo&XE#Q$Y)c{z(lnCI($|$h95R68E)_l(+WFaL2oK_z|z3&hK@VO|wtz>KM ze;YW%LT`1wnlN{W09qYY76CK?+2LnbSCP5Wta2n&?3Z}<0R}D$^SBl6$*&bZSOS&4 zEC%6F72S3h&KKoeYgMl~pPJJ$@13zT0$6GQ=#$>T-9T=mKm%29QFHzloE`x_+CXlQ z=Z8?ecQzt?8bF#Xi=CA5pPB|vj{q3rGl3owNP7objbJGtcXSRBoL&Q9 zs1JokNC1`5##_hEVpSV>+X#@Uh<6@|WV3ovb62*3kEsFh#1;e0tgO{rwpF-o1khk5 zfptU3JN7PkTn&&3?0jpTP>}|za5vKadn9ZetBgcd^48pcmkyvcUj)ehO=UGeCT6yl zXXUPl0w)mFbL&2x02#RrJ1To0;H0~O+3?(;Yfbn&fnH(bdJfdw*1L$A?P+wrRnIz5 z*L(fp!7D@IaU)2>aKLzR*T8XiYzwtq>^)ZEH3UTpu84v|mUOL^R5Le-DsV@#PW_p< z0l2C&jB7Py)S1;?1?;Laj+OX90GP|Yw}auUqQ#+b+D#%{3*90G%nG3#ZmkKV`K)8T z*F@xa=DZs|x&Zjza~H5R`N7g^L@Io|U7CeErGS#Z6zbXU=Md=tX4<@rf-`duWL0_- z|8=m(pjgqY)S^Wf?Yivo+FTIcI*K}0@IQB2%9Uw0swFj&&@QXU)U7JMipMS_zkt z?=3oEdGK>2HDg>F%PayY=VY%yF3m_fdG)~ck0fh%%a>(eQwq=&feLZw@OaC-+Wi6v z)QYhJ%RObI34^k^7vU7pnvWn6RJL^*tSKN{t`{(6MPx63AS8n5e*X-t^$$igT4cA) zR`k}|GRks!l{$|@u=|HihV9iW*%M8ru8Iq^2zI3aE3kfPHGt&!9Yx$O%-s7}Aq!tKO#sdDr?yb1A>zyo(u4_+ r5$eB~Lk4Nw1h9nq@2Wo+m^6O?V_bGuj^r&O00000NkvXXu0mjfe?7R4 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_39.png new file mode 100644 index 0000000000000000000000000000000000000000..7510931b44bb100b314d9bc617843bd51a2cf164 GIT binary patch literal 1438 zcmV;P1!4M$P)$~B7-|wG(!YA*5C(dt?@6`<69HDoHRyL1<=sgX#!|F#% z?fe-fru-e}x9aC31oR!D7xe?e-#J7p0nR__L|VYLH(6#nfo6WnPE>hei-ksqb?`NA z46KaW30~tz#PA&%z#}K>0C0d(aHVlOic<1@->FK-VuDfUi*^FexnaGT0WeTT;M#MF z^fCgcge>}}*0&1-K;8D`FavlX?f$-vg%PmvRyo0L3;;J4)bTI_Xq5tmz=ZDGw7QuL zU|})}J2C);DGZre2=El}GlL0hx z^?zgXrQ`q}NK8QWp~`G0z{awMvr#8#b5inX{n|o7Zpb^iq;4bg;5%`FiA9|*c_b$c;x%a0ie*cSpg!#XsZ~Y&0Pw; zks=rXLSh}P?bU>W)P!}&bP9|bM~z1@K;}BQB&@IV!o$VbKupgDk6S(!?WG}`VA0PDQa<^Vk|j3zR%f@Mx%04a$~V*oh# zP9Y%jE+BHJaTsBMsJS)w01elUaX%VR*%`+`YrDSvO8s9?H~ws_WB^SL&^u_Y_46h$ z+Z#ROXJ)_|*qs5sj)9s^)S`au@r~nh0ICC)7@o8fS7CPsfMAorjQHfiNnv338~_5% z0kWPv!Esl4{>-O=n)b_20hrKHXYe+b=k+XP9s`ff0kAu14=k5T)~n|ea2x})Um}lz z-lHDZFCsL}$Rm!-0U9tSusFqtf_HryX~ikYYD4(j!X*Rl`rIK3mdz6FEFsSVMp=LZ zH2xEIJj4zRz{vn#yD&p!7zaCIj?Sa$3t!LJdE9&yvShGc>ysg%A0+Z_K$GZYv%*eK z12z3EPGw!&`(xsw44l2gdhB{bD8*uHnThuYMPkbA>o{SW(HmP}ZL#s6mIIu)mXA*g zm7D+r_Kq70ZLHp5MzJFcz^m`kG7~S&gwZ*G)?rQq_0FRsLO_GZ*!`|vu^V?`ffJpY zeLnzpG{k@%wXph3SWXbx&T?D`z@Xh#eO&MByvCogoBJ@N{n6mW5J0{kz%qGjt7V8R ztiD~`B@|h}eqVrfLD+7YZH(DQz*yp>5J2Vt4cc9W9Uq?p%n(+#lOEM#r8F1T2CL(2 zadBP-lR^rVf~3nMNjNVeLV>jVI74G?y>XoiuhzVT0{f34QfDtkX&Gh2fYfekWB2V~ zU0;v&k>-WK#@2+48DQTJz!?BrfCe*@Gz}BXII?oi)H@*Ov{_Jv01lMEGicZnzIM2) zfU>QYqm;oAPzs(WaJhqap^^hxlTuA0Wf?yTFa*s$#kfO2DK-c3V1z|R4d`W`kqfXk zu5_;wiETbAmV-wTqYj|We;~X@lR{P&YALp_jvawE4nqZa6f(MdX2)a;n_BV)Nq1?V z4*-S4jyN+jZ!1Q3a%Mkkh~RXNerwnWFz>V)eRX0sg4vE^1(YxJ3nNO2JK~&RYz$tx sXXQDt&#(hXhCl7;*eZB(oCv4+1L}9b5t%S*1^@s607*qoM6N<$f(+J@S^xk5 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..f99454b164c598c8b5e6ae11d8d9864b7e3077a6 GIT binary patch literal 1284 zcmV+f1^fDmP)#PQ8w{{HVu407ihiAtVW;!`7}vtu(r>w;lzx{D>EzvbeHm z=LIkV&}3)^cp@f!-vX`xq--vq|D0We&S?z^&=9by974z37NtkLfTnypKk5NcL>g8F zoCaFc&nhP+L_>&nJsV0=jUakRCpSMNRLkvAel!GX$z>#u(n6G0k$4Ds0L`sPF^FzJ z8f#%>?41qEk}V}>EMH0>>F0nr47~bZOWs;6KU;!#5dh(uDk2%I zXH7fh1X=GTDNZW?r19Q9x;ufR(Ayx{N#PV)EuWdIbrY>MwCmQ`ZURKyZ%g~rYIRDD z4sFhCWV3W>ZbW(3xQthZcmSzv(XRKdf$li=!U}^K59cX=RE`4=0qA@HA!7wOq}H#O zGewk7KU>xx^#a?y8+c;r$LoCU9-^VpQoaQ`FIs+X)d!Gz+9mSh}WX z`P+8_NA(m+S<@HrJb{$MWG!D(I{h381zUuG8OhEpcs=_FJmpIkFXai5JuD?a??LT~ zmiS%gMJ=Dpr^KUs@USEV>`<@IfE9~9;%9sRk-mw9sRa0n;-kr|l?z0}0GE>up~DG~ zZHD|Byo_8UfplFfH6y(P4MPaf(&MjVQB9!MIE-9dyH3G-2pL&i=9E@&l literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_40.png new file mode 100644 index 0000000000000000000000000000000000000000..a17a14044a94054ff24eff8c1b51bac31c273251 GIT binary patch literal 1438 zcmV;P1!4M$P)kKQKvOJQ80A_|Ne}^p9Gsmcu&^)qeHk^&j-j0Un{- ztN*$#I*#MFweV$t57ZyB@A~`K6=~aR?eiJADE$>J*S)%}(*M)bh1E(P$omofqbRRj zq<*V)?+l^sfqX8n-5&kX`8lPB^)7;CM8#xe7NH-P#mdExY`&?c|?LK@#*S2{uUqHEZR0r;Hvt;qmfs07V1`aaTluP3fz z02Fp(0KVZ`A8VdY-dURDCRFMp?fTJVS;nklfRUXM?8pEks0<)^jx^Ys3~OxlBt)EG zh6zUcdAphcMz)RYd}9F5;v(sD2L?c3f^Z}8}S zZGt04qG<3=C$M5OItQ4s55fQ(T9}ZOSI6$V7@+AUdUw+P{VW4;Sga%Ug*t%ru>1j# zIe^C=Nsf0yXP_lv{d+UHLWP|W2k<0ovz}kU05dYJ1&AbLy=}^4 z>&LEif>{P2ir>Z}01p8~@Am^*Hp<6HPJpmyX9nQ!&0;2u!cPG_+m3oKIuVrYszfFS zAd26}X5n&xY$7r<7PV-780g=tLWTiQy0sjD10DiKED!~9$4cR4RtBnH-+xtjl2DQZ z^q6Ii-hrNkI~l-}1MvK;+yRK@o#1JUPqaS+gpYwU2CgW;?27+B2at?<;!d8$?hL@O zECfXGWQW;32jEa009l70^|=F;4)!q&u%Zf=-36I4@r*8{?2pa?ybmW;45j*k#v~OJ znZZXe0OA%d0>o*-;|MEqi;v0yRzk`dc+o-|w+nW%#e+8o$eiCjW2El}a6~SZ_`Ukw zh%f-!UT6+bQN-c$D(d}rV+T7l8~A4@|F9|{kz|jY3>g9>**o0^q7h~Amwz1UU7)Oi zO*V&ifq@JGJKY9)#z&kg`fTd&1kQX9pn8XCdUt^pi=%BNP9oWJ`u8j+@EmC*HnBYR zX{UglZ1Lih7KP}2F*OSIdMcMOQz(-3Yxdm%p2K9@O8ka^M%~wSl~PZWkJhon;`7b( z=nxPApZtoW-8Tf(;6xZbI>u!kp(+GaFVd4jz)6%l?)4Lopsq|6GNL02k9_WosJ0R> zAwZjWPq9U#=^1=OKs{dS0NG4mO7603V6h4++#vC!5b*MA;q7%h5%ebm3w{44XXT=J zD`=HAp-%x^uB18HFxH( zh0lT;DS3P4B(DeoNWGD9R_UEXoAcuWuY%;A(M3n!DXhe5$BBa52g?Dpg})<-y9h($07*qoM6N<$f~i@aj{pDw literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_41.png new file mode 100644 index 0000000000000000000000000000000000000000..f763540c917b48c20a54984132b516ab47935de5 GIT binary patch literal 1412 zcmV-~1$+95P)hLzz;{h#>kuQEvwZiy!V{y1I$Kh&bN6xMN7?W4b5{{c@8a0}g5 z{pWeYaU9>RhKB(@P`}H*^XsS6(~j@b{fOKu{T(gUy}Iqv|H5>kwOjD4%Nc(VOvd-gHAhx$N z2sl|o$EqM0I%oD+>@x*`Gzrqga>iFVf5@NN)y)7BxO9_E7m}WF-#U+O2EYc-u1gc_ zF|vvVuM7e$H&@pH5&KFQfI>^H090OC0BF@F?l)^IozJbX74R^?=)bZCp!S&qAQOS(AtV{YS-5}3d3fx~ zAc!&mfd4eM0_7)voizGYf(hfX!% zWV0{@K(-KxjIDASP6PN_7cvY`rCX{2D9Tg7j0L=?T(MkwiOQ(zhx@NePaIlm0E{Se z-~hT4u4Dj94M6L&_5c8g2f^K(53oN2xL*S!2CmqE*%g1j2H=bu4@+0aCkDpu3_#Hu z0M=Iae1UN$~54ZF?M(0Uc1UuPc!LI=5dVlBS&GdK< zmFT(p)$lMtwY{Y^K-VUYM12{ZJ2)R;4?83q=)E_+S#kOXkjrhADS+E!*y%K|8eRtf z?;nR+DVkF{`p>}0n#t}?0Xv-rTE=^V%KL2M-wCw*9sqaVKEseTJ{@lXJ3!0D-nJ8O z06uc?waNm^^H6tt3)smPD_$He&(gsRcvV+U0A?2NdBT;THB7ec#CHn7j*o2R;!k&> z#bJk)M}zU!DZmO$dliObc~1)H$GgI4?Nl!lTtjZ=meV(^~{$LXSNR+k$qXt@c7o&Ry z1G8PmF3@@;+e*(yuS zb+9OzRp?qxQH9wgSF=Zj{GF(Oz?=eR;I@mUEoAG&X#r$OAsKWyA({d@5l#UBz5t0J z!y;MciKc*^ict%;`K3?^k=2lOgth*mh|~sGrho&$_n$^(Fw3jlby{S0ZVYi)y^=j_ z>Us6m=1%=v!BJ2nx#;ek|`=|f$13@63_$6Fk5UBk>stGx zC~Lmx=#g)Y?v){o6v*fD>g|yqwa=3p_B_v!6D%bvj4|T`dfzNoIzO`bqIy0h!;Ba~ zo1ET9&m)b6bV3xf!B-vp+Hq~($8j7lj_)VwF^h)i=YAjkeb!k~8bmo=!%hr9?J*in z29Q>AKc*=`gHWj*Xl^k4fjLvX(LkBY2!Y(lde`8DIpJ z0jxw4Kx2W3 zG3Yr?zuA>5={g##gFu>#r_1Uf=rTw8c=U5KfTo0%0(canR<5Q`H3Mjn(#>qT(B$;< zUOTD`P=Sb&C>p#n2&~+Ut^qRoL2H0W3P3V|N3Sawpv!LUJxJ;CSq9*+SVy`R5&)4D zfUE&LdP(zm4Vr;ggwbP}TA@PE zcvif1|4Ihn^N9=)HB=-8WUAr}q!qRT5eDejudD&MeoIq;_8{F^^!gd+(R)`0L6!kp zvlmANUIHL%ByS4P=H@vR>9pM$fFH{ulSY^C0xX~l!LHx#3}6+JOBf&uuTe7;hH@HB z1N2xGW*7h^TdM&$mN$YTbE1qRrI+j+sO#wUtJ0H%-vG!cVy)7dEli)Ya+Z3R|Ql_d9)aHt&ikU83WPUXD zk8CepBOOy;26?GnTWzD_%+uuYH~^Jer0cc7)okNAhgiv@^on}tpr!yT3bPLu-C9~T z#tcR~7=CVs;dZ3{4mAZ>t=*1@!V%iOqPr(O7fO$vs{amG3eaE`Xfwu()*W2_EGn62 z7r0Zv2z1LO^#iih>FGacm~|SRAGIPsDh0F_3y=(ESY(y~vg~7}O^f;?Iv}w8yMi;w z7y{LXj-Jcx4tTM>vi3Pzi~YB8Gw{AM;6}(81(^a?Ai}26EwtJPsCrPYq!jIyMy^f) zXt|O5qQ$9Z^Wy}2L5j|()6wq~R$_JFM8Wlg)c~5~uPEZK0^-G2r7V8WbO2b6|KvS1 f5}6tY5j6h*`5Py|3n0fx00000NkvXXu0mjfU$&E9 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_43.png new file mode 100644 index 0000000000000000000000000000000000000000..7eadf75185ece2ac3ebd0906c97d054942ac276d GIT binary patch literal 1397 zcmV-*1&aEKP)RCt{2UE6Z&APj_z|NobL9y**7Rp`bF7P77K z&+{y`*8io3hXFoNzDvLJ=iBKi+h?ghGA>GfMoDF_F0FL3a3@}0(1N6#Bf;A;pGeCq1BnCt|cf|Qex{dUNGk_O6FaQE0 zhgit~7QOoKoPH1n(CR4tMpy1=5%Yp44}cIB#H*Fbb=;Fa!^$Gi|Pku08a=&GJuAw7@)~6<*$?W&(Vud zV;!#Tl3hUG7nuWCCWISjhRrl^E0t>#$rUp6JiIX}g1+w#3=kPpd-u_b*K|Y9yC(-| zy54#hpkYN0U|I26|CJ0t$2(fZj1ry@AjK)9@wpYY0v-lv?N{aiRKK?*Fv|m5e=U!Y zoFOW6wG%`cpcH*^WZ<9=B2Hip0g?e|3`H7kcLt!(qKJgiqbZ?zB5D^9eTf;|fdRB6 zav1|~f_fT_Akr6Qv_1^<&$2MX04Ui~4nV;sft<1y`X1FqUJ|)tx$qJl1J$l?zbZU& z_`?7rtO@~i%*@rAfh!rnk^}VfS!;cF0Io426OH!0oAFE8p8@`C0}+E}gn+x^wmCpQ z2$win1XG|-vF?D~8Gr)902;h6cxQ*%JqMs*9l*N<(~gL}N66X8bg-{v0PXN1TCZW| zm^dN}Irg?WKtI^b9$pjN*N?U-)=lxe>2R*cd zxgI74h+YR%!L;4TxsV}%8+)hQKs3D9=t~ZQdPvESbb)~k0Xy9WTKapO%KL8SzY{3M zYpTQ*G`IuxHfXWfTV?{j4IF`^CC~2+?yw8kNf#@o9l%37bb=xWMVx$Y>?=WYnAtKD z%l}ktyJbYv+YZL3O!##npdn*x_k@5Pq+AA}jybbJK(>+Y6auCkfEortU0kwgJ!kO9 z@WL`acPD}=VCE){_T?NQY9y{7o!n9Gu`-F`aJvwI za02dmHb%L}N*y@)XzCw10@sFnCSC?ff@UVrddPz`lH?FxmeN+ARF7QAqwtElb2uzU ziq=d0v=>0;CT%?WOq0d+=jt@tIzIE zW>BkqtsOb@uw?%h8AvSuK}ZCVi9jZhp1R2HfbPUrhNoJJ@88DFzu+iX`qTMD<>k=g>ZopM=qY>6!Qg9g!Rd;WYmMkIM6+eyIw200000NkvXXu0mjf DE2n)B literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_44.png new file mode 100644 index 0000000000000000000000000000000000000000..5241195d32bc160a3f8f6e1f04eda860211538bd GIT binary patch literal 1217 zcmV;y1U~zTP)z1 zai;(_V&x3?A_cG!Q8PSJNGX5=5t%Cfg)zMlGcyrAyx~1eg1#>WU;qzzZ-7T9n9!hl zXFDpc6aWD9I2r{#UiMVfTC)Zip;_t7Ol)Tq??mncI1qW56A{5mk>oyr5i3sejzoaw z?HS~GfSWO!4lE+!SE>CJfP!5=m(axE6$y1N`uhkf23nw|02o$SKL-s6vP`syfv-OJ zk7Rh;DS!t-3$$fP$%UmkKzp15aN@eIUw<<^4N`qDdYwZHwsw7m0MI;Z-JYd?RuOP( zmS%46@u+X;^%Vk`-HxTuL^4y|k8E0cticle*Em2FMh@W-f%I!;BzP#_i1&2@h}dn5 z$o-{Yg_?HlaccLc1C+nE&x@=(V6|<;0Z`r}1rWJsepk>bqJm$FZ52?;q2~dtLF)}1 z6%(4{dt4qtMFpSs0p&ST01|owBt5S!tB@T!sV#5t>3e5`&-;h)e2f6JAX}c-c%*xH z8uA9eaYY2b6wLN1jurr7+5Fy_nW}L|AR_o4o+FFE2Fu_)q1fv|tKPjlsECdhfEN*K z_-IJgyO)RZjd+d}01sX-n9}xXt^fD$pCX2+6lE3Bku?DSU`QPx-NS1Hlye2FOT8$b zBORc@;sg!aR%&wv?hdA=c3!v?w-vwBBwhAC>R%wBk zxak_3-h0D;wbdLkj`}li!LHIO5tO|Z9n^pm|K`Mk9F3HlZeV~xG zcAp@KmBBA1NC3znaVz#>wAansMEC4R!n{KWTF|Wh=i zM;3szZAF*6IM?zXre(1B*(M00000NkvXXu0mjf=Jgy8 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_45.png new file mode 100644 index 0000000000000000000000000000000000000000..2a3ea8e23af713aa1210d6e19693e576aea86d6e GIT binary patch literal 1177 zcmV;K1ZMk*P)Y8+afiT?l%M3TOcp1*+Q}B7m0ov{wHCARx)QRU}^y zDG{JI8m0bO#(Cla_nTl`xsni8G5oidK!B{!uO(MnGP*M|6Y6OK+>4?X*OP!E1R|julPl|e8IX2= z#ChYr8BxSd0M4NC_tK&*MtF8^h0uCCd#nl&j_-emhMNGKK^CJu4`hY$9$2DGPlEnu z4+*H!v7Zwi1=4Z=%J|uZKdmNu>v_bhVylF-)Q^T$2+b%^JDa)&<$Nx&MZa3N%}2wY zdZ(~=hLqe&B2-TvX^!#x6-fVR*xNvnz;Yy5qQ(uzJMTv*ZI=!kE|vAoj_#U7S&srf%c%ij<7o4(pZa-07?L@ z9EqOuqoGu%jGG@FW>@q(xWrD|`5s##1h6D4BP8Rb>&LI--=%vIu zb_C3jq2&7jJB5Fbpwk}%GnLbQU8MUrqErOf#qm*SjUP*Wk17Qp?FEc@S{Kg(XP2gu zYMRJ6{tOAi{~y2uZBcJ_zC}vDVRbw=j|U+Fc$vz9DxV^dTC7MQnlThOk;giZ=UPY- zc!!=D^q~s@s$ulT$U=eAA2dL70Pn7|$fn#JGvj>&+|qQG6l~SRxxdXq8Z)CQz{@~{ r<2}3olzuV5yMa+>WIFU51OMX}ZL*z$)XYxz00000NkvXXu0mjfwJRcX literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_46.png new file mode 100644 index 0000000000000000000000000000000000000000..f4b263b199c8402088ad99c4dc27ab3c38f0bf2e GIT binary patch literal 1300 zcmV+v1?&2WP)W2D-85p<^O-V?-LPnye~A&P*RkfIgD9c7`y0J zKF{+!ki_Tn0RVskQx}Wzr^3i^{3XJ9_^;B~^E~i8&tD>3?2l3c3|tbNgBAh+ToRmu zHUa=#5}bfm0$l8mb^_ceus3c5_;Hlz02)RVI9(r@BUyGXm9{ciLV$0|FFAK((;=kcky4*qM5*a_JDt~((MX(7Ka6%66%u8}l@6alov2Vl>~!z|;Tp^-p3qa)`x?rZcvuW3~z zXo&#xq^s0Fiyg~Ca665%VOiX{4Yoz~r6tN-oB8*1n{0$FCjghdR@jWTqZZc^q2|Ty zI?620r^Zx8f;J&&weu}D@ZxzFOd_@S^YOcg(`G_uqwP_*fzo1WlaJ;k)b8STpCp87 zM^A(dn9y4gpeyui$yFplvx3URtFD7B#FW}#JKk}H2{3nv2ZtA|;~JRTOChE9BU;dIhRXBHMj{N)ui+1(x9s{`bgUh@Nqy9olEMdb(5+Bs;za59=Hi$VW3sb zlF$(~GQi0eM|51Tdqo^g`mL@++HwFbd9v7(9qADQ3rV)UWcHc?>t$KzfA>_m)jyzVvXL5;c2(UuW?}j9T zQEdS92TJhI)+54-R|#Ns`W!w-Tk2xQVk-j4p;lc$x+{3Iyj?~BZc#k2*4wLqL;4tl zG|q~|5r3A60__CICUE$Yg-U)_z*6r6z&~xM`vUxLtzW4hJT@cHu&4^IV_R zVb($d@As&-ls#?dd*)jDZeS%UO23Zsk#YuytdlI|VpdJuQU|EmtzC)Ld>oCRJH{Y6 z-y=6`*e(Kiq)@A!t|@vPTyZQ(k!RZ^&jc!_#2`F8$R-)oEhC=qz$J4gu{|xeA4qX+HE?~YLk;|sG7eg%2SUPIt`A^2)FThKJ zHYwQR+>TPva(!9>Ygn#2CcJj6mqcm~5J^ZpgX3&RNU&=6TbzKU+p4j7NuZ4(904>X zkLu7X`${$_L?ey|RhKZk^c}7OBvp$-l><0|WR)Y{%rQFpPA7r0_%MLapNNAxN~>R~ z20SQ}g7Mk(4Yz#1fYQ4FT!KX5+5NZl%b{Ec$U39bq4ya07ykon_@I`j=(wH$0000< KMNUMnLSTZO1z`dJ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_47.png new file mode 100644 index 0000000000000000000000000000000000000000..1563ff39b27a83826ad84e5eb30d17b8d216a971 GIT binary patch literal 1268 zcmV6ZXSuAvd3*nNy%ItPdd9U0;0d7i&NS!1k1c?I~Od5L9!C@=fQ z>HGTn9vNe|_qR4dfNaU_`=@=oQfdj?Hude28xpzn(sd-eLk1n|Vu zlSx459vx!({^{@DG5R*6Aps_UC5PO#hy`0oq&}nfj*qB-WcCvvA zgnmz-O*(Cz&=4YB_l6RP01~U{|5NGYn5kiB*oaaqWZ}8E0kouMv~ZINssP*; zjY4_`YIw<$tsG1W0i_Ax$!04Uo}xFtR!c$Uf94sb^_wCio3U>8hE$EoZ-B?25BDJ zz0(9hk}!?^5@wWTcTyNV1b$2iq(u}V`syb>Y zQwYlP{FRFG8Nj>u|6pbQ^xl3yla` zwF3#^?*{f0()t)Fmqu)@vf(8*O&UFY(LR@yf@k9^68lCCT?vR0`l8L(=KHK=^&E*t z=P>>E0X&n@uG2oa$?s0;ER2M;9&}B$wT&Wt$Z>FM`ol#@uvI*%0&Px6NSfI_coF(y z2n9W~KoR|rQCmz6i1&}?BQtF+mKpA0RwedaAfL2(SB!T}MxW$!rED_d@I2|W&; zVY#>MuH+Kk_GTbs?9fv1uG;%b?0mM2u}ge$1=;$EP)YOFbE}8|NocMCsz~!X0uIheWdDkG7eriteMd~ z@B6+`faiIjNYuqf{HhZ4zArq_Qzh~%sUJ1+ox}HHQ2jDjKJWu&%Emqd?aKC^s0VL&D!zxo6 zw}Tk7YNxC90|aw`ZpvRR;{=8$#s6Qimt|#OHS0B5vxBk5XgZ%~57Kz=IgE9Td+7c2 z{Vd!BIAQ(TeDC|JuGcz`q_~~#+cK9#j3`Pyz^?v3XA$)J3|33>kbv92bxoS3b8D?4 zXBh#;LUum{iX6Z!VdLho-ZS%7k@b!FDG@42P(}dkGCM=Ev;l9s7muN`xVboaR0cN& zzmLacNl@Ae{F3tVLE?WH)cvl?bv$vMBU@%|?OVZB2^jWJSn=MqNC)k`j@RVHSUW8Z zBP4JUfYzm7LqzeM(mCssWX{Stx;76gDgsjupp{DF`YikH(9Vs@=Q8oC$HA6rL>oip zc-LhjfQ8*r85k?QN|s!dFxf)v!ugU8)VP6Bnt5Jk219OqFMHKNLEF)Xl>dKTFPL;mF$zs>CM#C`Ys`J_!0yM=}g#fLt zUI8shlh$RGA<2`@v1Kb*v&L%oueD%TB3jDPa3VqL`L}BX0oYvdY~kLH;^|x>>Io!e za`Ao^0=7I0$keV_hSny{%q_`WK@LmnmyQR>Su!O=Hq5!JfKhTpG23WV_SY&2?EWne zo5po4WBkqJQQ;6EC0a9AXi=18QD15kV`$F@1~}zP`<40%k$ynQ<6rJSM9Qv!Hn zde#~|_jh;yC*UPPI0Vp47GIBIr9!L2fYtq1gb8mL@Kgp{lE9w^i!y2Jwu;yuGFjHp z=G(#Edru{ol@F~t%zeKd>_o?Qa|SJbIC6*eU+_5oyMb4kXYsk8jk|=Y#nO+2ab+UL cRpxp80Znqgg|Y|Bf&c&j07*qoM6N<$g7rsmoB#j- literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..3f84ad47e3ded623ecc0323b5d64af350fd1e61a GIT binary patch literal 1312 zcmV+*1>gFKP)%Q;16>(ix1Bf|z0Dlld?)z@nbsKnc*=A;H@KC&0nG zFogie3%m!%1N=Cq5#V@%ci?z{1inPb;jt5BMDJ zQUE;}>`FSPg?pkz0BK{d>w4veb0WWO{#m36pl$x!(C4hJWfl>r{3pqmBES=y?~$KT zoHnMvKJsRn5J#`qbh(NI z$Yqb>J{`-6Fp~r@0#J9h0^F5KuaCep@zGeTV(=!79%w|EK>}z9c&ZGHDur((C2z%o zyG$+CkH$)}_?*!s__E7ZrO;j(MC=-u1Dl+&n*&+yhOIK;~}TCjw)OP@N~MG(!^0f}^or zlAVeDsGK1KND|1c!m_lH96HVdf4`E&YjcrYuqSLp?vovd0`#k4^pU@wm4}4YFX-<} z#FEHOU*rGt`)K5pEM3yy%<5`NCvcbKFrzrCaLFUl#*CD-NptXS{u7w;)vy(^qMjx) zFD?0#Hr|@Y?wjA*#M8hPn58)0)znHcj|}T|@8-|+tgj}(Y)G4J#bk{%9XnZm$JPXR zstK-6lr-Z&Z(u{}whG@d{5F(Vz!t|ZC?@VMXT0SX->b>g(n z*%O_#D6IJJ^)Sm1gGLx7Nr=hvP9v)0Xx&eX(exVaI?eheIT}_}3h)qs&Ib@OP)gJ+ z*2;w>S6#_tEK+5#-3LN;Dg9dWe>~e0;yjzrWm**i>WpBkK0s7S5xLdz)ZI%!Sf!^t zv^-4)+Yn%8yhDX0iv-#R@mwG?N6GT7{3C)bLO`S}dJ0|Ho|?SUztGCh$Vp=aTl4{T z+Gq<}`9?9y2S!~^{#=Xss4)2m0p7Op6eADdxh~QHM&X+0)8uGp z5%hgJzCwa90V3|pD-RhGtTGG@uWXHsmM7r%kj5ZN!s||Irqa%&GNL_lZ@uh!y#h~5 z*7AfJ8MG48yY8@e$`XLyoT9S%n#>4BH986&rF%EVV@$J1P;LZu_Y=Wrg4vB)SzUPY zl}N^~{dtW+N*+Cce=)?usFNni;|kdksN}VJj|>Tx^#M>WcvXy#VD>piM1C|e91>vE z+VqY`hlwcA)qqFVQ)`HjU`T*b;-V!UU+VgCcoRVSSkXha-1D@ip;Z8nK^2*ZYNB$j zC4*um!6k28UQd+({{?|8KGS7;vhnnGEUf|#_QO`c9D1yIJ! WF~&y6@>K)?0000&-uL|>!o_*$B0#|vZP;8qkLkA=s$H*W}iFLX*Ifg?b$?O@I~F zAC-@^7VYrs_-~DEMbzUU0DmC_4FtKeGOXm5=-<1L1-#=K>3(uq{CmFktlR(=0?LL* zwtuZdi5z<5Q0sA$#rJ^AufNZ(A(1Y-M;HNkHjI>6V1*saVlw-Yo*{amJpOCBfTnyt zzlEqhM1m%PGb;hKm2#In+WlJR6iFnl&$D^SN3?>A)%7S2C8M=k;R>_lXdz0gNIawn zpd~IB{{x}K=sX^Fq%Ng}PzccCik11cl!~jnVhGeOJZjnAvs=oq*Ou1Q zI7WB9Mx!=}ED5-rXvI?#A)QRVpwrb7vDTz2lGvffAmC-<+UF7(yz0RlBaPGa0R7W2 zX=AO29&w4*&nv(!Pbw!RrSQ+|Bvfh=)${q9T9?uSv@7#5dfL>dnJt3;>_j)qamG1ln|Qoqq&3hd3Psp6tYV5;^}I|6X}PY z=>SHQXN}7kWj6sL*4NVFmF7gssoAi+V20qH@<(|%hzE!?BoMN-xahS>S3qj+cp)>Q zeEzd#{ZTKlWh2NU5xV7!06%cc)kiA2$q2nj5BnSVW&ujyI7 zO3HT!fB6^IrACXaUKy?HW)#>mx6Id+o=FI|c_nOAPhmE^o_z!@%9lJ|WDi>q;8T=e z&=SAPyvUp~M(TxR6X7lT09qAsIw`DJ?2$g(`}}8%d!h(pl8d^ylFovX*W=Tnkz|X~=c8&n?t@wiJ!>rfCA84;EblN*6}6 z%S>y%$bNdNhb)>yfJTI@%ga)PyWLzi0xO?s z#M%Wiz|IeW_m7fKw)A&ia|2g!_vDJSL&07C;|NZy_mhDl>rw^mi00000 LNkvXXu0mjfjhAve literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_8.png new file mode 100644 index 0000000000000000000000000000000000000000..2c8cf3f5d7c34f1eedec4def10bb28095f2e1c4a GIT binary patch literal 1308 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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 zcmV;11$p|3P)N znNNylld*v@KgN*kQA#Or<7M$!Ex?VnxDDXNaT~yk<2Ha7$87*Fj@tlU93cj99LLgY z@9zx0BdlH*>UwzB0gmH(c$Py=SKnap$vhOLAzewSN8~@6#_3DzP9%z^GAyu z^}fncWolm>>KJ^@Gy?Ysz`^SI()rqPGpL4;xrEzB0O_7<_52Er@}+w-^$grN?D5`^ ztj|^vAi~=$G^KgMOccq=EqdUdF+BoMNsz}?l03DLDyC-?+_;s>$5CgL3#CSY)!snC znlotWu7a26PelN1jrINLisy0TdUIA{7v%#ypArD(a)Sc^N+mpkHW6Ea**%|{vy<{M zyduEadT|dDW>(=m7NeE1m7N&mUWItSRnBU)qC$fKF7eO4|?dPmWA8`gM! zopY2@iuU*<_2{UCjFJn0S0*=xnq!rh%8{a;y)}Z~7}8+g8s!1ck)A&&YkSfLE`H|6J)=+!$0>~93<=dm{W&HRaX$&BY09Qej0nE^|@Uie3 zIa*Jj4@ve|2~i9+?=yfi{s`m6^k?898z)DrPjZa*6c7!sg|S92P3ZPW5NF3~A|7?` zA@S@?Y-W#LSR;W)Lh3o^L528U0_1@VhlbFEIc`{%TT_R#}WhJ6r52(^M=>yAR8wDUA6?E_m^2{ER`#9O6F!G zGJn#@AYHSwPQa5ZGRnOYT%jQ4Q9XN%DsAs#^b*u|jFvvPma`R7*Gk}(m9L$HVQEy& zD2!|K(r%ZnAx1ls_GD8^+3&}p8hC_YD-EsJdq)Y1 zp4I^@UkF(Sud{MX%X;M6*>9xAR*LQmA*qN+u)O>7y8$PIQX|;P06e{%qPtS@o?DA% zuV?E3J7lmQk`l*7pAGfd>!S5u7oGsHBoFLm$Pvdj3C&&hcTV}E`@O_)MDI+uIAWo_ z!rq977wu?xHku@ORHoid8LE|vyy;3KCxo!G9mjFN6Az31Yz3Y;7f%8_IGzM}a6Ad{;CK??!SN)(gJTB(*tQLN z*9NWKRjyaT40~(2vi667@?s0$7y5%8Xtu zj--D*9jtO!1dV`RznCHb9&98MNZ+drB01ZWUyQan16I2aM*y!7O^NoMPNH}1^^qoy zLGuMKR&{;@7YW*Z>!Jd4uN#Mw_B4MJyTVghP zev|H->HtkR7C=`^4;JTglCd)c+O;@k|_SKTFYCcl=+^Wfh)gdGK4bCY%axiu-#A`;1;OxxalGB_8 zkMpu3hWadOvIa+0M$6hv&C*6JBXw%!D*oOBuUi#^RvITzo%Ee#6bfphts5h*fMcqgeEr z1FSUhN@%6i6$?teVq6q=nFMMWJ@Kqajwo&j5QqYQa@2_<63Ol*3VMi|5dnSzzch(IIZauq$CtyhwOju}b(yt27A4W2Q%gx7mq>=FPoEGz6#*%(>^=QU1dsMM0f z(Hg$z5a)Zzg~@ak{rvPy1J6q^7|2(@;_hUd{Q@2QU_BXMQdUc=Lk_ zs08qa_sr^B7AmWkUOhPb%-D{kG1OA>Lm|p)X_tMb^Q9R~f49^x z=s7DTr`P($&RAcx{QDyt^uj7mLhm zB#2_Rd}C~TwbN;NQOj`;*&H}VR zk0VDmJnfEFA?#%D_$@n!(nk+SKHF=~mjb;WhH{QrF#z<$Ki2WgTnbHj003UfB5F`m z5$*x>ekfcZhd(uB+BM4YHD8GeV0Bc~peTCG7;Rfe;pu;6Wn&cp;B$1Dk#txo>bF88 zfybFrpg4jQTt6SX0DQ$2xbK#RDXh+4X?7H3l0a%_26nIX)ll0im3!6!uBs5NB%oX0 z7ShnnY0ER)Emm zaN9e*aiblzr9jH9-~d`{heLD{UwR1uXgi&ZYz^ssX}QQqGHEy^0Y+ucEQpc-U!|Qz zh8+^J2=FVZohg{{j)JBHwQ?94lOTft-vQuvLOZ8L^w5Z=+GnWRcK9j(1^}-B@Dl*O z=9^?k_TiHC#SIQ#xo#;>Pikp!Y3VPT2lxa3oc|jpJAXBJ12!%@*b7n~!b5;J0QmC$ z{iWHe1u;7o@SM41+4(>K#ei4IrY0+bSZOx^5LW@|o^(IMuB=_aI$mhRNEM(j-_%fh zJ1YYb0{lp~q5)@C0Sqe{r23J19|Ul)?3=rn&d4S~O$G2Vw{+y4(Z6O_79RdDpjRhn z1Kkd4j-K(-i)XvaoiP+V{C6;|z`f$NDzb>nIp1SLZ)6@29mnzb-@&+IPl@Y=jA%LP zd}_xGNaL@aH171r?CjcEkm3AM&rUj4=E_O!?6mONou8=!BkGL=_iRpi9IU}hf>!5^ zaDFstr2YMr1c)Ny$eA6}0AP!Ry|6fcv@&><02ARUk9EDUI?QrNv|8d!N^1jLKmh=9wuY9J z@aT65tibt#>D$0rGlY^r+8W_97RTfEdOE-5+2Ju@B3x<-pmL~@z_Tn^%g+YQ`VJ!i z_uxl9FiW4_T|l(17d?)<0p6A17);yI5whm^$o7w7#CUos%~LM{7Uyb;WI0kjGlna` zC`8k`9?Vd(?7p8;0iw%{v`kpb_Cl{WwJwndXa!B6tRR|MWOzCP^b9gra6Co^3H}1X WEBEEL?wO_l0000T1`t9(2w}7s+h8gKh>Cy0l75)R03PL! z!4wAYD1RId;6nkWYlPI#bA6OQ7DosH=Xrv=0$|&(T!BaVBXP7)s^$7%ECPVDwnwuA zX3F2e`)7XF>I?wzYyM8kk1@`9p3}aonE~E!bTUAG{~eGoWuEyM1v3Kxd_Wl6rRxfl zBO&+j_-A>)8gY=PdK&L}XMnV!##%28zOn&uOIZ4D(tFmP2T86JvCpCPY_F&1LaZM7 zb8EU)`O-L6qzsfkhhnMsb~v*DFIM(`tw&pwpZAm6tV@g3-<<`dgHWDMecnO&06*zk zNBOO>$vNi^&A^0rND8JN)N}YkS z=WF8!P7Vs35gb&N`_kFQqNLl0jYqLz}gsKCzQND?*r6R zzQ0b`ey5#xtAVA@Rvijm zmC8M+LH}1A##<@$;{8x)D<$s|0BSR)9GaTFrVtgc*13LS03+h=TK<=|12)#ADuKQ5 z-+6%Q11!V zEHwmgCeX_W9tL1=b@_^hkjDvBKdS1Q-k!1Nc$tIk$wge8fIfFd5sd<7rXD z0KNkFS>ZX`gDVVKV7V_iQh+xAuK>OS_^cIZg;;}Ryf2-l#3fkb#gVx5C!(srhOxyhY-T*xeK`V!s zF=}N2Q`e455w#9>)LhR?8A@rtLFykrcc}z=D9ejT!&l0;rFQE()Lt5-c8`3wisrR> zd;~SA9OdU_SuJWU4JCQB&!hU^#sDaK1LXDJV+7p29C{wQ(Hcj$&7hXcKwd-FY~Q9b zm8yN34V=-3XL5to>^`P_y6y(Hjs>)e&t)pfr7?n;3_&ZyKPhI9EFeYgjG%R-Esasy z#_f$#M!?OtXD?t%v<|Ac-MJBa1rkg_VQ&Ti`ql+3w3y!Gk*_pX&v&)fFmAyB6s%rI zox<@bj$(@|hdWwh2m=7^Sl}xCYS&N8xV_F4Xk(QWw_<=AMwNQMw(z4#y?TJl1WK$n zi~)eUEYLc;(B{{pDPA(zy!{po&|=~##Y@yWgAr(I7eMlJGBo~Wkc6ufHaeq_cHBn5 z5(mA^V704Yl{reQlj2^$Xysoi50^H89(+KWlPP{0R%4G~v?0#~i(@SN(=czo-Wshp zyd>m7Y5F~dX$)ZW#I26fnx7j%KF3p~fu4ZWvxCR&MAv4D1@N{a%PRSCC<_oN*p$*z9D>JDgbx5={jIHxr)dE^@NBDB zm`nIF54=EEbNtRY7wwBu=7q~!=9+TOnS1lS7jIM`J@wBd{FV^wxp3tzb7rG}MLfkZ z%{hPlQDy4q*kaeud$(HA2k>UZ7r5Hnj|Fc*s@Jo|TWM@o=Fif{6@-t&(C9I?*0_u% zr34Q=pxH{GS%jksR7>qo_0Gq-R+KMC`F82^d%OeK8nET}(elwy&YN{UBWoqe^u-6@ z3PvDVo1+P{7mYxqx^?}LwUT~&i2`OrP3-K#r_ipmq#Pq_#VHVJ0cL~ywhI49N;5Kt z0v8h2pB8@t^A!bfozCJ8IS47Cqw(4cBWqPF!B!7qZzZ@3+*;SqYQ>10NAz2}u@}-nPdj^1i z>m3`%Qhg;C#=>rIC0JX)1UMyV zW&ODN+JhktO93WfQG!y2tFNio!b<`EIJI+Wtlqm6T}TH+8ax!>Cjfjnv~zkarkN9B zo%grqX_|*m8U3nsF5Us)4FG-v!1wME1rT-kVi|)*NZPoc8lU!#f%f#V*8=>7PvL*S zL4l!v?BLxqM`dKBOt0KWbI{~;ATIuRFNqEhM67j~W$pfQOzp`A-5 z61YT;l;FX5T7mkxM>Ozs9Ng6!&#tM&p0`hPBd2T&)J2v0xpf?+O>G;kyg*OCxGjqU z{4&%z9NOzuoFmnf$gzyA9#?tp;j@fdfFDD>pODdOR}!9OEk4M~1P0>etWl027cuna z4PF%BP8UN)LaNsJzqb{Lidb%E0cI<>_eraCC(v?ka2W-#3f5u;G?~XpUjX;fGX`p& zqypS&8vy820! zE@s!p_mm*C-gm1%?f5;R4utCOtO96GlCFwIfjDqkZ9VUGrWBS_g3E0I-2P2DLnFtx z6h{z#t1rCGrIg~gfL2~Rx;_PWygJhEQH9@X|LH;qS-?0*X;C!`?oprG)1^Zds*DzD zztxo2KDUfTLx=@v4WRX=jMk^h*Unt=zP;c6k@B;J51>c{Mdk#wp_WJ6bx1}j0vEqa zP!WSvRucXmxLbf;0oHG)wQlctC?&vkq&{&O?F*t2{Oq?MK&b%HAz@Vek`*Z50bYFy zt8YSWDH>m9Pt+D*h7!y`n*fsPOKoZdPxZCO+YfNDOrRD{nZ1)S9Nxkop(_y-=@3Ry zkGbBvz2>7?8>+v(Gp*eg;Or@4&Rj=C9-8udb%7 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 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 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 0000000000..122dafe7e1 --- /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: 360 +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/L2_Dj_128x64/frame_0.png b/assets/dolphin/external/L2_Dj_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..95f72f901a5e03e63373957cbb188c9ae4673162 GIT binary patch literal 1640 zcmaJ=eNfY87!O~VNeuA$d_XjMR_@J904APtJopZ9>n2_@i7=U z7bDo5qK$XJUPju&uN5^?pmsbByVGQPN$hY<6bqD1?xCG>Ooqzsc5iND-YzkV`(MUu zy+r^u)WlR3N~EUfj2LD#=ya%2uU9Bh zW1=F3L=`@4y2B;X4uf5Kfw#kBWmel>Bf*-eP~sNUg5V^jLI@Q}A{L4z5lWGWsgxvv zlZ;h5foESS-*~G63rNH1Q==wv5?9fj0#Tuu0wI+Ij*tw6A_QqAX(dfqX^TQC2NLDp z&UvMid?wJI-tEiT03R=>i*taH62R;jKKImvxto-^G}{Q)(b3Vbd+KgLkUuD{F5A;P zl-J?7g@(qAj(+>#&CXml|0Zr_S82ZHZeDvy*Ju;jJ-hsezA0>E#l3uYha5U^Z>Hrj;ykkbVy( zL46TIbF*1n{J{pT?v6xo9<)U43Tx~)`z5sFVb6jaPj47mAKvhutpZ*b7P-a!N8;9@ z3ro9Gg5bIvmscG*Qq}j>>KVNk`=Zg*T_+_c-ONz+{$JhD?ojH!F6!FjbH_(KGvo4T zNzE+3jDH7L?fGc0wxi?flfyyRHyoX}sd35a<>mwOs(`D<6810nBs+(le86u<`ih#^ zy}u>s^$RmR4?>Tb!eblzdgovIYz}*P@xhY*>qgA)p<$=CdWoYxzNapsLOtW%ntDsQ z4(XcR_@lDhoOz~JcYDjDsQpb_D|=EW^|a>Zb?vTdi!SxwV{T1quXIbck(b-2s<>lA z=A4kxkjeloaz{{gXXn+Y0edb!OMh$%zf~Jw|8dQ2`@z*mMF^XA;pRVs=Vx#4^z56% zZH|gqDAqKcY&;qg@kMJ$LtJNE;i=`Vk5=CdHl3_0S3S)2d> literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_1.png b/assets/dolphin/external/L2_Dj_128x64/frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..32e13541d83eb0571414851957a4b8a5647a8c03 GIT binary patch literal 1687 zcmaJ?eNfY87!Qc9!no6Y$hV8dsi0}oG;PyJg_f4lL4hGUL{ZWtg^F!z8l>Pk8QumQ zDw}d_FsYn4r!YOyF~#A;={((380Z-294I1wu@8l*oUoJ+=O3QAB=7q^d4A9Hd%iA3 z332aud-{6<0N|~U)+X|69{)rLCh_l^f)WG2_;I>qZZTuxvI&+3)D&Z+LA`}YrxR&{ z%2|7k4g-Km1?Hq=F4+)^lZ-_~xO7Agi;YJEKv=lLMv$2_2O8;gvsESh^Scv5&`ha> z@5>CZ!4^qpn4@!9dU0-C5}BJxDk)+3A~4K>^9C$5M}Q8?8mk?5sDz_-aenU-i-q8* z3YV!8j*&_>B!H0&OM^-g43RJ_0ToIS2_rCSL@3&{07PI!DuxmMh9n3sL2(2H$1fr8 zk)=#{qBd&WAAeE_GdRwMi^bX5*`jQzh+)&k5~Whib)@T_r%R*Z@y;ddUzlbO^g_Ck6 zW<)UsM36=$gi0k!h>$2Th$c}4mB@{93Z^FPya=z;M9HEQFczgz>m(ALR;`A0k&#k4 ztdmJK7%ZK@>aBK;u#)tIZ!_+Cq;kXv8zBT!Dj-yWVUSTtVUSWPlPKgE zieRKkIF6^DOW#2CXA31sEII2g<_tf z*ws1Db<%{0Z%^0u*=+EKXVXPn`H^Dz+38uZNyN|Hl!VwM9lwr@jC|R#^LGGnFV$<+ zNsi8Y%iOG|VZq^%k=yzsbMj{0e!KlHy009qUmOq!PrVPuRn2SJJo3xW@|-fY$MKoHa(U7ZrpGOSUF)6a6GC_XmA9eF*S(8Q*&k59{m5Ho zCI?X;y>Mp3EnVMIps;RlOskLE!7rHMqds|+fo0m$OMo-w`po-JxooZ@YQqXzd!q%0? znvZAV>mM~<#zP@n$kOXix>JFOdgq6cin{|*g6-FWR<>VdPwhV58U#SrJKtzNhSx=K zYws@qbWTiFU&fY6POl|bYv<42mAj}e{*6~k2algC*?2=T1V4-$xJjrhB#YJ{;~i zc&F1hu6drO=Dm}1hf6ElZPegbA)ayjQnrN7S{M3>Q}deJ=3SM?e)`&0x#!wY>YF`p zxjj*z-{4!_)7V_N`UCJ#ha*uP&|K6Pky~<0Jvn+ws0jz^JvtT!rPeq#G3ITb-xO5W zpL<<%u{X6=9a@_|yQa1C=TKB-80w0=uZh`xFP@&N4%}4meM}Q)7=K%Y@gbv1L?vjBQzuNXySn^yxkc1Db;?AiCQf5mHeq8!Ge0IEMhTM`WmlliKa7{Vd*AoD=lMOq z=j-aSm!wCA$A$v{A~Q3LIpXRSA6;mW_`Vmq!z?b-1yjC|%h`kqnrDID%2`+_(?J)p zIV^3hTz`#C0w8FMJuhF#H)l}{=aAEW9l6Kh60HG9O7Xa8ri2wB3tMD&Cc^_~FTjx9 znhdX0n^CjNz!uvxs(3cHYDpebRl<-~IAtM}OQ6i&7{mDk|g^Dmlj&DU>8h`Y|vJ5j7BZrBk3ih|@hQfM8_Z z3~zS{cFqa;5orroCM3fm+p#blF0=VHu+u%BEHP~g5A9MY<*35p@W(c)?G|#_|7pBc z+nrbGVih^8n=9iPu_m@z0b?TqX@TSOIIc9%3E9P*z`2V#7lh$* z97-_LjNR#%B>Jl~7POJ&?d7aBjprQDXqqYeTPWDnDh*~qEeJ-CS_Ib;1Y#kr1VXCR zO09;#F@mwdPX9T*BWk<%2sl67)RO?D&nD@=^9(EaRhu@>2+8P=klx!>`T5|Hwa+h|e<1xm zv3Omx)O);r=lwJ3L$?yGS0t9m>(yrt*X-ZudD0u3AnnNyrNHI#uYwCZ8*65KTzDY_ zZ%e2|0wXzQyaiUiUR&l7j#mw zvg!ftrL3Troh?(PVbF2O>ZAox3nazUVnCIzF=!|z@-*16BddPefzHDciOxD_Bn7=V zEZe*F%CCRVB;qg13iCFvISD~+KJ_WZw)Kg=L(33QQg$eMXLH|;Pgc#l8G_B5(kO|0 zZ#J;V?hf1f!Y41qQ+uVgvEFV$cS$G7ht<(X*Mi-v=XXDe-}233+v0^??WLggblo0b zXxrou;vea9Uo1+`S+OjoN4>Y6<(E=iRk6M_>K`gq7b<0R=(M z#mF?oOC)*Ut8K^Jlkv*%wXV@%!mhaxbTrugy?{r+CkRq4h+= z%Dd5#uBp@h+ScZ~_UsW1mP@WaUw1!l-2g!Ho!cO*zbM%_$U7#Khw0tcr3MW F(|@~CN2UM( literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_11.png b/assets/dolphin/external/L2_Dj_128x64/frame_11.png new file mode 100644 index 0000000000000000000000000000000000000000..eca4a1296ee3dedf9ebd3b010c7be8763eaaa44d GIT binary patch literal 1660 zcmaJ?eM}Q)7{5{wsDfm1laGv?g=~Cy?e%)Jca>^OTLfY&D~+Nvlk448D)z3l2egQz z&P`+lb&HF*5tVG6ZU%;lafqm>bCdXm&S7L5V{Aj6_<=^iIbm0z&OeNoyL;dFx##yh zzvnAgn{QeiC5jgT01%a%qc`)bkAE}~LjL_%#5NS?f3o1<(va*E4De$9i*@h7#onB$x(skEY^LQ)}@u&y;y8II!t8wdjN8j#z|aD)e9+(|(T zdfH98>`uZsFu3yd|`-ndX_A6&C63R}g zNCl2rQA`ODq!ouyxeSL08ID0Ti6W>>VO3Bt6}0mryh4W~a-|N<&eCSfWCp!f3mbGg zxdJw1$g?n59>nG{ZjN9`I_TTZ`<}+iUy4=fTr|P4E(^=93wA<&G0U;;V%7;Fs00O5 zj09ZYE@pshv5veQDF#%C<$5)DPfr$!W1Y1VI&SiC}zV51%cWKt6VJQ zDM|yK^IRtdMSOb(w$EmRKRlZ*n&C&v#m`RjuGTz$?q=jKu^9MuVq(I7eb27|5aP|% zYb~DMKO62ao8Vc74Sf^-H=EP3ttUd7empeZ(3|R&+hf0Q;(q`5)48`byTd&P(!KSar-7QgDb=rJ z&T`&d`M9wt0jL?BCvFSNU#0tZ>{H)9Anj>VXyYf=wq5Vog}8LpwTUB};}U-})+H7T zyCc2c9;bG~psDNE`jSGAgkRkqo!QyGaG<8BCY%ur<(!Py7+XdCH-GNfn*7VCuWO4S zW?$NYWptGfwe?u|!gQD4GPjN0FE>PQ@LxoifZAKo*1Jp5wymV%+~(7C3= zug)%Nm)_oJr5ZLj?B5mH6a|b_e7;%|e<5CDB7|^Q^al=e`ddFmJQ3vi1m(_9>yz(d z_m21ku{QINt;uwuD_q^`&-l;zS;6s_ZjuPu#^3Jhie;4 zyLPa-gK5c88&ma)<23R0+<~1tYBdc)&5FT<^wL4?>*-}9<3k^IS%z2iSJmAJsg5Z) z=-GC=_CRD(3>tlY{eqT(^ME0G`&$14Z_}mH{MDggW7cQ?+;Du^+dJH8x4jJ)Ua2{> z>~g(N{eyW!S9Mu|rl%ztC@&cbi+K=%9jh01KLkF$DySH{zaTx*GV;{|W z(mqGi4mm_>8_+bWlaD8 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_12.png b/assets/dolphin/external/L2_Dj_128x64/frame_12.png new file mode 100644 index 0000000000000000000000000000000000000000..5f92e47fdd7cdffb93e2cb52b64dd87858071c6b GIT binary patch literal 1637 zcmaJ>dr;GM98U}4iz153Ly=JC&`Fv!P1~fSyxO9@S=M1UVS3s$Noj#LAq}>8J|gGy zrW?~u-E?z$zHcWJ(Xs6We9hwyH@Tfo*ii8?<=n3w_Ddp2oF7Az&gJk~7)NuB}nY1YMnRv%f$ z6f-1UQ`N>~13=U|ucJgLvCTD5tWQOTbyOiAC!ql#J2%9UR0Sh|E~d=uH!AzSX;Fe+ z+NhkbwIMdn#CW`Omh#NJr9}>EX$3{l%G?|D~;}^&f<3u(8^i0oS1JryX=bTvALAFSW}{O2qN2UDfzlDU z3&ZsgNx29J)2IoER1-MFP#B7-buJx^&=EV&!|O~0s?nS9!h-xlwc29N&qpjKlSYSF zw3-4O(L}ITe?TDp6ch37m3$9l^)JL4OguvhEbn01%19^JJ*>b6JS+#Im)P=Yp6esi$rpIx}MbJ1zXtZj*4#!ZO zaw~`N^fT!jYE?-hg~3JG7;qFv^(3Q#^oUvm;W`Y3aEd@640q$Cj>O!gOQVD(ig37d zp6R5BNNUgU_UUX$ho{rU_@$BJrP)#7ubq|VF2+9BVUgB>fq{LUySf2D?y{Qm9igsY zx3_IRgN!ow4h-}jIOpQsRJJK?UC=?6Mvx9J;F@;a+2+2oO1 zzFu|8wr5WPm{rl>cBR_o?VslQ;wJ2+GX8Bz5(}ap-f{vhE4e9VYWf%j&fiQpdyJY` zOU$$%*xg^6bf)SndlFoK{Crc!jXgK~$GZ9)*^TqA3itBjoK&zWy7utr?zXq&2mdHJ zxxBe<)8);DE#u-x^glR%B{nZXFAt4KzbMW(T^ftZpO_NMM+YaZPEL9#-!QKd(N3|?l$+N920Vr zFP%*C{FK(bA_hDPFNyz4eOL>;GC6hA)+a3)7y5K@qDQ2c_;&$X6VUwqHlTK-%;Ou| zfQ)WaC?*2STDRRjQ@UF=ry+4_IhW8oL3Dfqsm|GNR+p#JT(`^_Q)>Mgd}BGEQg`r! zhDCXn)?hKFtqTFdw>`ZNj>NZL+dsWDkQU`!d}o1jBL$*qqDNK!5nhZ?zPej}$m- F{{s^?M>YTe literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_13.png b/assets/dolphin/external/L2_Dj_128x64/frame_13.png new file mode 100644 index 0000000000000000000000000000000000000000..1b1017ce259d7b511bfb2d6c565cbe8350ca929f GIT binary patch literal 1654 zcmaJ?dr;GM98X(TD+i*^sccSTIXtJPP1CeZdK6j-NN*H-(8=R)Gfh$`&?cmT7Iu#6 zx%0&-6BVc898TSx;Hg99oDZhsd~ABCi0_-5!tmxD+t_?;Se62H{^6NR^85Y1`F_6N z&-Zm%ZO>W|8~t`P006Pp3?f@xgW_XW$i#P#V!cgV;)S$4A%}Gdev)SZ3(Yzi(CQ(J zm~4imtIE$aNdO>Q>2~A^dA3X=#d=g^SVtA`a3UH2l2QU3NtH4J=wyoAUX!x_+fz!= zO`DXtIvZ@`Qki0RMm5jmRA)J;>QV})l_|+!Qotx0@Gt@i20Ue6pD|!k4%;<~`>CoRG!Tarm{Q(;wl)TU|0hha1{k3FzQ5T#x)N_U_`5i5%GpJh*5(Y5fmJ~ zl%hwTb{VsY^ihA}$)qe61kR{d`~7~EU#nvIBDDs`@vsd9fkX|+SLGGR0Oa*0L@W@D zkK)~&;AXvG*dpmmSS)Z_YN}Qb zr|GowF<2YHTD?Aj^ioX3w_Ef*iq*dsYfR-CQeb%p%a%nt!CuS?tgo2mKm=8x;A|U7 zxxHb@d*LdL1Who!yON>PdDa6Cr`hO!1qGK*t4Ex$6GAZD0HFp9gPb^xLAX|@G3YTA z!6=t<6i>gDzL8cHC1My{gpCnHFvLJITF3xvv=F985eTDj7(y`@M(Rn_MLM-gwMbDN z?wpr8DIyZvGrWB<8{*-`bTM9Wq ztw&pOz2Czz1pefd5QH{lj9a@_|D7_VI224w&N(ydvp>vR>5|1K zPA)D6LQyN|o{oI!{$+p9>*{yNexKO+bO$#6cz(h~cv^Ew5882L_OS=V(n-Lf-uP9a zRbc!50(0nn-M!Ymi<&N-Q0&rVOg;Hi_d%D=UK^8d?5nsUZV&00oqu@dl6=44_kExD zKJR_sOUjLz^FGOa40A zApvPyvSe9;0XDePCc(98qBlk|m?0vJ6Hrjqy+*E`=d049h_kF15l4j9L&IWuFToFrtvb2zNtrL@h_v z2nvo}63(N?W>=et^f7i0Y}cQ9lj!B~r&l#A69$r5fy zYNKsxs}jd7D5io)s|ANpg&c=SIgUY;6-7|F(xSA%wy>QS;W4=i!L>wcda5>EF4qxS zEv!pRQz&6wf+7`z6=AI2)#^x1Qws?O#M!Xq?%Z%IiyVW>>^H#9CtWE2;BZHH%gY{ax9> zC)HKLoj@ynAyGVdW}pYi-+d>lv*q>s_e8*!d-?!-{^s#Mkx@3S4ghlgou8e}8w!e( zo|e7I+wsUVyr^6^#C3}9o~_>}zP;?9_57YcelI$Alo{-4NMmufIf zueUd?-I3BUNI+E6Kz>!b)aW;L$IX)5-hF!t0+qd*p?LS1!jfsT z?gkSAZId@$l73gx`nIv0f9xsCyL*^Th_Kfh9_(zX<2iyA9m3NwRBY6_*jWKz*%zXtfZu9Xi~;@8j}*ap0E4 r14aC?w7aOMg&+C!?=^uYKr=29SRN~Sr=Tku5B(POx=f-uHE-R2HCSU* literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_15.png b/assets/dolphin/external/L2_Dj_128x64/frame_15.png new file mode 100644 index 0000000000000000000000000000000000000000..9b796498c0c48c2c9125e98dbfea9c0c812345ef GIT binary patch literal 1344 zcmaJ>YfKzf6rKgPB1nVPmPk@M9co&mv-4zjX2KR0cA?vJaiwdam25kYyD))yI5V&d z{%}KUjP(aL#-t@ptfo~HZSg^DLSwYs z@0s(R^PO|gnb#6+trca}We9>)#P&qmq0GUjv9uVz7fMIsP(0yAJKgf-Jl&v>fno4Ks|yKzp5oW-Ol(vQ8D#^M^_8p1m2;$+-v20hxIAsg%;YD+3Zy^5e>4ZG3$tOyDo8(r(j?6gBn@wzqD6`oX%=0* zFc{HR(_(w1Wi=k$g|HsiwM2r*WHNya6EN*=f)WJ5^Fh-%w7{J~!@?YQmWe;VsrJIO%{ z5beM*`)viDNxHTGhV^@JAkPr85udd+cra35#8moqV7ReJ2!me&s-}txC-5@M2XRS} z1)OCl0hcI&$AQAqEXB#3N~#4v>*0ASNDJXeOG{I@g`%R7aF~oXH!~a=tz(*ak||(g zhT}?x0t&G;7<&~PeIQnBwt?iDcG5Ka3Z0PXFntSWt`@PAkGGP9+w4`#|5U23UWM4^GX_9#jE#~Z>3eC z2Bu*Z*oZvM(?JO^c#xzRoaa~?=M{m(Sw786oW!OjnZXE1k?=a_zD_ErV0(JYwb_81 zwdn!|94Q;l&SPbN`~>H2Lt9@`cUJ=oZ-7!G~sv4Z*RGdpLt(;QFk`(M1HN3K3S3x z?h*foSF4IX?k2lNh@xByx$Xb`^^t|eft<`8dnruc+pu@~*vV4j;ATZxtVWa@+h?~b zAKm-(&eCFaY&_K3@Yn9CrTJH8o$=WevIB~D-@hQ>q4&yD>PQtJ6~on$xmA#|9Td*{sg zzVn@P&v~ypxqrvDC$|9rc9cu`8d*E!$qXjQ``X|_g)C1*g{kO(XGJX(V30LE9aCio zHE<0hbMEK`JO)7Gq+OqirYaMf;W;ddeb~0+6Epy0>9&uIX&g~HZrE;${`1RoG-aD9 z`nAytU-5IeX_w{$d|-aEZp=>`(4^C^P-AV45I8tORNI+xL#>^n*LXFukIfuSt+_>$k#Zo)oQUViS>d8CqM{e4pC%?2NTY@5o$ATIMQRt9rDs^R}-Z0a4>=X}i7aOlkj200UJ zqz5MTdvKuGk*LvL4Qz5SXeRF&vkrEnay~_qKdfn+nxQ~dm(?VL3>`AEBtQlUP-U2{M}m zxqGNOQ7@2nb#?X3Pam(4-PLkFTW|mT$CYn}&QseH)w`?5=P&=b=aZ!$sL6>UOx}E? ze`{Z3VDQ4?q0HHi+qZJ!SMYAf&9*X!GiMjm)XTRaej`)8)PMSg+Ye_-nOl!u1k1Ru zZ-4*Z>EFQa{^vT4Q^$6_4{FQC<*#3o|8x3-qRjV+yf zhlH=c+dVen?|J(B1@P>hcfzX&hlV?U{WG}u`Ri{dmbV-{HVi&{Jo7>NE_-Fyjpr|2 zTL4D}sArCE_upE&xcB$dO&9!q{oi4KUt%P4Zs1<$BuIdp=XWj~{U~dZTtT@onO_<| Ga^gRXW|=zx literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_17.png b/assets/dolphin/external/L2_Dj_128x64/frame_17.png new file mode 100644 index 0000000000000000000000000000000000000000..80863f0b6935ebcdc353deb69a6b5cf4d0c4d4ad GIT binary patch literal 1292 zcmaJ>Z)n_P7|*p`cWu3O-I%%|8ZwZ%HJ9W~E|~=*oE27&jfM8X;EgN@(wi zDFRHO&Nw4+p4zXX5s%dHq>@&r(Imqp;?XFbh{af*PK4M&fo7Z7lEZ|nc-Xltt%@l=YD+WXAJXoq~g(Bb#dwD4_l7W{^N3W<Y-MP88j#+;%e)*wV`r+Q!^6&d*!PZ}nesb;Ey{|`4O@H^E zwHM}3|8eWz{nxu@dbl(Br3>c^cWWQC9SPidt0z+1Om{ACD`0t~x literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_18.png b/assets/dolphin/external/L2_Dj_128x64/frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..b4527bc83306f43a5ee138258cee037f073fc276 GIT binary patch literal 1498 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 BgtY(w literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_20.png b/assets/dolphin/external/L2_Dj_128x64/frame_20.png new file mode 100644 index 0000000000000000000000000000000000000000..f63904f29b2f44112bcec74fe738a623b2d9f1c8 GIT binary patch literal 1698 zcmaJ?dr;GM91joWAtK1;K-?K3r>LY&)6k}cN=r+z&LYE3!P|s1O&^0cr46(KL#|9- zn};{2r`s5cn~F}y&BIgY1|lfp&f()w<_XGl^9eqVbHYiN0(Ji3Etll?`+f8Me7~RX z>r#`Eo;EXhZZH4P$O!qoHCl07u%@S#pxQdIFA(RpyXv>l2gnYFjEWxI?eeOyVxm#ChUsYy+_Q2 zzzG#5PXbMn%2H>5a@t0LA`SwR2*Lw}A`Xe52&P4Kls+Cr5tPqGQ1*s-sF;U|Q4E~A zAl9Qzrx#}`lvDoLlLRs{j8)9#Ivfs;gU_LD1};w|5_xQ(D9mcW_6-(>aKaY*0|#Jld{u=Hj=H0et{Rv=I{RiTNol=L6e1M%EG7=5{Ui6(V28& zQXs;$7%qefQY(TnK2HP_JP{64B!*%o<-AG$O6ysoE zoSGm_7LR1Hr%IDSD=3?(h|(!-v>BX8v)J?o3i<@T0M#N|7{x_G7!%?+tQF~SSj11@ z2?aQY;-ns$!s}j3-(;(@5;hF0myH-laa2f9d{~I^_%JTOP#7mg2#n!+oDdM0p3w3k zE=!T?>73U($tz;p)3bdw8|>lLbWs*|q-^Z$Sax10Vdrjo#!5{pySm-(@B12V0Dy0U zN+HuY|LDjLvUDJU^W5&shfb(Mc97N&&EKt1yG}dtkoa=q$2;L=4PA!F!QPzKvZ8Hc zrBR_SpW&_dKk+HQveD;pf6U9hM8~c31N5$es%9uKE;mrxxX~JhxoUm-$0K8BC?WaH z)pfaJbr(-ss~u~ul_U3BOmT+&y~w&!V_^I6`_WRVaZ{jlJg8}-czauB{Gzz#x^P27 z-8va<4hU=7bTs*MS{WV`8VVDQp}l98`YS@nfG-OL&Byx+D}LQPXWHXE@ag7Rzl&am zjXvFe?^MsK`m>8Gm9x_;G*tV-871A4S+Z}}3dkFW3huX8G+p?L7tyOpdlXa~o}Z}s z=9&A#7fU{#QPocYj=_0PvxevOJdGbZV%_&}=gI3ML62R&)y0{;Ys@30f9EwGuwS}+ zMRos@uG*sU)`~L>qQuXs9DPxGg_4ZT_jGno4_4gL?1T*uNv>&o=k{Rr+GRuF`2GEoX$eX;jmbqa)9i>YV9q z(A{mL>RW$~Uca<&%sUtm1R#hl@4Frf3s6%T<53TGiW?9wj})A>}Aon4&N`}L>w58Jsjn05N)j;9{8cm aKN6?|A`4=UbUyd|HB_nTio+?nu73e(FMSmN literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_21.png b/assets/dolphin/external/L2_Dj_128x64/frame_21.png new file mode 100644 index 0000000000000000000000000000000000000000..076448fa940b42f0740c62feeb1943e00616371f GIT binary patch literal 1665 zcmaJ?c~BE)6kotbz=O_+tyqsGqP55-dnMVd1PBBKAs8W5v>j!$*+2-%#?4{^s6(mX zF)gOmv6Sji4=T0}W(4VBhoYk3RmXx>@MzFt$I&S`)3H>ebOXfthtr+i{l4$*`@Q#j z*X%Z3_L3>#Gs6J@n39pM$r08H;YkV&5#9r#JG8saj+#EE!4y`F8~(u_y3OkiUJ#Q8D|y>LltXSy2Q%Z_zaCoEPRm|%|?=z zD+vQmC?JY9C?Om}ln{j|35cO_6i4I+xe+!7?7RxE(WD>*f@so`)oBQls!^-q)RYuV z4yVenWCF$l*bJM4r))G6@NE`+k7HB*7fYsa48^mYo@I*zouDgZdDc};o2?Golrq|4H$3hT(oNJDS^EdU7Kl%Y}U zU40#^dbtj8a`W?%LyH%`6}ly&*VFx1gE{omDr;oyu{FcK{O{^Fb-JF$&zg8K7dr4+ z?~w=CS(C?*)wptKuE=0Xn&zI_b}E1Bg&0r5*LNmPmX~}PUNL1y=Sb7>y<1o8Y4jdw zZwI{t+h-?EjY?Yk!SuPF)76JR{zTM0i402I&`O?c{Az0W(ETd!jF?BcS1P;S-@NUT zw|O-ud+50|P~8crXXo}~y|vc>Ji+6%sFxQQYDJe*KRO238~!=9sJwO0a&JHNr24_H zf9hdKeX(DBrn+rw{q7)DO`oOcc{H+PZDZq=?CH@WXlH---V0|tkIZM~(+^a(INuw( zT4OqFQk|cX*MerlNwcD^H)tO04-2liX_40fK_NG-Md{Os%+j6hzpSY8w60E!o94c) zQ;GI%=^Nl6$i7s(j zdVXBY@C|y|hNy>;k>!u(HFk)Epp~_O|TUWV$e-xag2+53mE$H?f-@US&5NnTHw%hKG{ubHTrC$+!GWa?jx>IuR=61uGuuCUn-P(;VNI@mp(>qsi0_UI(@nS&LHE==?~46>Y3P-=bBIav+ZV(4UU$iMgUUA8-f& literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_22.png b/assets/dolphin/external/L2_Dj_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..8651f12f8bd91d684d5943af135697e9e7d700d0 GIT binary patch literal 1809 zcmaJ?dr;GM9FMfpg1~U&alT`4t}1DhG_+|2ZJ`AVR-x9T&KGH#N)OxAHcDYbD|!NR zDpSun>vN~>oKBs}eBcH?PQ|I9PCZbZcy@=6gQ@rcL6#~w|M1Ku`Tc(1d_Ujs=li;p zWll;94;>f^06@5Ig2uqDUhYu|dEDDCT&3rh0j$=Jr~4z zbXlxQgC@1dA9qTEa#_}?lt}XP^Tqj6G2@yeK@|!`zy^lFoCfSJaImBYcDUnuEof*r z<+3?h8{+^27D+QRpG|@|wr|6*JN5c^h8^ynWN~Secu1!N6(bV6JrLU)Z8vM6|EKXm zZMU(&NlOg0o0;#TxSCkwdcj=&KJF->$gxpQcG-5HL;Q7>>%!G7DnqweumoMw5aPD2Apct5Z=_t5K^FZAyw% zhG-L{$pj+p#p)bxmUK{buWuXYy9bMW6st^e(Im^bj0`ikw-YjR8J2P9GENY~#W*O^ zla$R7kc21&&+GSfnTT)$&9ek5!rR@V0tO-&XW=6~~MkwSkE++`stgsNULYjce zWdx29lojg1Ti#3G+g9ZyTo}$?HcA2`FgZy}VL5_IVM2yuFhMC07$>X*DI;+!X_i6~ zj-n*cIq!8+uZU~U!1mp2aEEu(MLW2Wa&fcMYI2R_<}NsMqEXAO-QC?g>bEp+YX!u9OJ&N0#opWU_y|KR&zITY9Ip_V0eMJ9@lr5#9y+=*fn)byNzk7lC0BorAn|VQRv$u&DmcOnOTTQ7J>G zhekI#=Dnzt+{!u@RNbtPSw2{-EV#in0n@Srpi{@2x@pMC8^Ydx{vsjItU&pdSwt-PK6 zS#91uzkh93W838TKX{SGFAjy2XD+JPiNxPi?ZGqDUD3RhoxbRnlB(asvjpeDc{lUj zv5%xHy?x|)qk^-W_*<2p^{;2$-gaQ!6ID$GPfH)q{0bT(hl$$bfOqq}+0Tj_59@Bl zjU9S4v>`9m{LA*Tk~ZfPzb;+?y6=^15A7P(rRI$glsD#HWVeqmsb4sALi%jlynp4V za<~K7N`<9QE9(ZzJC96RKl(Ud6*}uwwpg@c)ssK#D^c&&(jToVK{cst%vzNZeD3QV zg(0gJjm|w`l%o{tsyvoy!0K literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_23.png b/assets/dolphin/external/L2_Dj_128x64/frame_23.png new file mode 100644 index 0000000000000000000000000000000000000000..d2d8e7e5144697f5932a8e920d673c29de730372 GIT binary patch literal 1775 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_27.png b/assets/dolphin/external/L2_Dj_128x64/frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..39ddf46ab3bd195b3e75567a3f91021720c9e1c8 GIT binary patch literal 1759 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_3.png b/assets/dolphin/external/L2_Dj_128x64/frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..40d1314c95fb07190f995925e69f77df558f8cbf GIT binary patch literal 1777 zcmaJ?dsNeA6ptX!!4ySyz*i{vnx;+D7Scc`l(rV7h)@;h)TU`F9ki)QfP#v}Q*{jR zdFIq1zQHL+Jvtd4buwS`wZTzgQykzFA7ew9=wLFtRH*Y0k2y)c@4LCbdw=(JQk0TB zEzm#A9{_;BM71K7TkYHv=j+M6+kDq)xFwWTrn703k6 zxlG0afae;MHl0n^Bw++)=Ho6MzRhgq&;SrS)n>(sOp*ol z%6R>x(lsey0!5Rcln+A$3=6?%DW8B5Sgc13q;U#}z=%iyBis!M5lkq?5HUD#@i>pP z!HA_QR0ICFlZ=V0wEt3n9Z)(UTHIID)~Q+w`w!m zJS!{%gg+iqwJ|0#kB#5G5 zr9>o0VUZh~Xkl2~LXd9XCeHT&7I`NYOQ1=drD!chWw|>cC4*uqCWEqqh?p-1Cu(rQ zWN}F*xvJD3w1T8fIix{FQ)cj0nlaN`C>SN8C`1qIAq161L*i%@h4fMb3Q0v0VRRHK zMo_}Y8^9ahNMC=eauO~KtD6mmA}A7#lOiY@7K$J=N{m1#A%!6^YDDoUTx`VkBA$Sw zC~$So8=d49aqa2azMc*4@OrvP3pY|UH#>C0@?dW6yi=02N^W&Jo%?R>_!R&=IuaG} zTHEbE<_%hVAND=xbap>JwCSSv#`WJY*yHNSr8x~ruy?@aZU4qjty|%2&41}Ps)ZTS z&~SME%!2JJ+GanU&~aKNubvfj*2q3x(4}Lv!`8HnpS+}FOw~qo3sp?4yxSN6+UJI6 z72g=TXIoO>V`^=^v)M|&)ZeWeyY$Ph_Jp9F+a8cbS9pEB^t2=Ifm>A4AL0V{EP1mIw>30iOq)uLK>okRiy>lBR_MbYy7I(x7p2uiCOf!87(} zk5z|#q>DSg_b(_iyk=9Yj@Nvp&^OL;0t-6rr`t2MQ@y$I`QhH~oe^hz07hD{rf}=> z9+CXV+XsOP-|`fDw)Y&gb_S5;p(o|;L)Y>I~$)hhJ)JV4SL__$2QpOs=CTkLiU2}Zy^Ck6b=tZFpw6s>+jai0+s<# z^N0?}R1^f9de$R(`qlBW62E&tLqLxVj2K8B>;-$hl`ZNR@@)IWI8|WPqcW(p?quk`u<+*0)$>P* ziZ*rVhj^E4+BFC zJF#+Jeazt5`P9_F!lmPGhd8q@gs07T@cjKlF_)j!%eOkN?RIpXE*gHe_u|cez*Vto z|AtCQL*c}l9bcn2gFoJKY{Vd6AO4iJeTzH-fQAKrUcxWfcGpiRQJJhbB-gF{58GOv ANdN!< literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_30.png b/assets/dolphin/external/L2_Dj_128x64/frame_30.png new file mode 100644 index 0000000000000000000000000000000000000000..27c297e8d59c983d0a06ce9afafdc39064216039 GIT binary patch literal 1408 zcmaJ>YfKzf6rP0z)~)4H)TFJ3af+4#v-4zkX37ft;Mp#$+cgr4hIuVaV0YHpp}TC` zWUFb~{;1#&joKz9n$}lR5lz%I^00|sYcrp@Gh{}AEc8zbMHO( zeCK@U+;irLx1naed6OA}p!IdNZXcKv;H@;JgYPeQznpE;XP>c}<2Kk}Ak> zU65~;eKId~z4(n>4ngUM6n~T6*iKh9X;17Tj_~ z)ReHUs3ACMkr&iX-HrgZ%V7k=9?z=bP-G=pAZ=KT4`YM{$AZCRY)je^-6#J~V@>Ud zzbhd3k*Wl5o2sjkHZ=^Bw1tL?JiMrcl9Fx7 zTUrjI}Ii=#9XV0bG}2Y7)(FhCJYR?a<@loEmR zOfFY@18i2iOAdjS(m?M#)c2JN`YzME(_aPV#l^*wmrq=QAjAGTx5FR%@%PJrWWS7? zGR7|49sFy*wFU^`;UMVO|*cWakyoStc6*)cm!&`@H_nlwp zF*0}a)EyHKOdH1AzQu0*-9MH0ld-SA$%!u=fd-^IjZyKO4L24ZYPkN0@rcQlQp|;`e;wT z!GC)6s?uWge(8EM@qK>%lf7TuY&N5b(0pO#_Qxp0+f&D1r{rP@#gJW*+n|L}axNu4OzVvwe9JU;vjA4Jq zh#}s1JneAzVjwa1dMUhMYAG5>&wUl@9Nq!_S~Bo(?&b%_kdQPJ~{u%69q#* e7k@BOlQ2NG;zy>fj4vzsPpqqIaG!Rzy!0Pj5bA>f literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_31.png b/assets/dolphin/external/L2_Dj_128x64/frame_31.png new file mode 100644 index 0000000000000000000000000000000000000000..f2aefbfae2f1ed51e8e87c69f47d42b6d57b6a7b GIT binary patch literal 1404 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 UNHl%vIsb2!jCaRAjU1c&4=x3@iU0rr literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_34.png b/assets/dolphin/external/L2_Dj_128x64/frame_34.png new file mode 100644 index 0000000000000000000000000000000000000000..81f133ac5592569c6963b813e7eee54087a5195b GIT binary patch literal 1341 zcmaJ>YfKzf6dr^uO>wc>#DuoN>0qKnXXm-QGZVJmC+%vNlI_M4k(7DdWk;AtX9jn` zqy_t<`k(ow@g( zd%knNbM85FsIg(2ckPC?2!eQ%+v90C_rN#2rWAfJujxy{sme(-IlGLklNT+3L=-~; zXi^i~KpKcj*WPbI9fFh|R5MLZQ)-7G8=6ln==faCglGh*+v=L4+zuR60&S`u!e+<5 zz))2QVY~e)GG)d_+=< zkCu`Z(((Fbe{dJVa*ksP1d-3@eR;-bSZxHw^L)VuP2*4lx4U#lbaCCTDO!jFTeeiw zQ4Jj}SQI6r(+Oda?NS(;nM$n~*6rnF!L$*sXcCld| zWhfpODW1cD%+f3skOB&+6z!~rrM|($R-x}@Y~+DhA!Y&5F|3SXbQCM0F=sf2oij|7W_>LBbV`&}y&$PA+|p9eabT&h z0HxkCG;}e|g1QQYte*+c5-H&{#|LpX$Z@#DD;&-)_nFO2{78XwZa_kaP&;`s z^86 zSUq@pTkhoJga3XP9cY=_GmpMM7uzXC{=`}2Ir8(V-PEWZzWVV$rB99_?|#t3jSb&D zG1W6sO5ahlsJpK9YyZ!`hdtM;-btp$kBDg4~+HYXme{t=gri$nls2pT3LVj*o_N?A6hce5Fd%Q GzV;v5Zqt+i literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_35.png b/assets/dolphin/external/L2_Dj_128x64/frame_35.png new file mode 100644 index 0000000000000000000000000000000000000000..c828207d33ed6ec94a71c904597dd24b4805b3b3 GIT binary patch literal 1255 zcmaJ=U1%It6dt>whW_M5Y@;ZQ(+X8PGk1P=cV?2!-_E94%%;m`!T1t-XXftiG?|~y zOuCblYMP4L2dhGbRV)#;STGjEKd=xj6j5KaNeZtO$oFf;eg zne%<;JLjHrB0scm`<92d5CpM3JD?WuI*p&6));Tnc@?~m%q&H& zx&&h>YE9HgE>ET%50NqhXdM8KOv;Q7cpz%Lfl7Nx9`FJSczn|wuW+Kmi)7=Xa7522 zDFwB^5f6W*s7esH3d>fjRi-L1j#p+mS(YOXo~N+~?N8bP3~Afn)nrhSuY0B&n2t?G zj8JnXf)s_-uBBnQx!gLj?Kg^r%f^P#WjO}0mKEi;>g@*w^uLS^z5U{(i`WA4oe59J zXHweL1mpVMJ5c0^)hPQt6CVs5R~>!ALUxc?JlW9@lWEyg^ zL?d0~MJ}Nw3}7^QHo^-6P(`37`uq9-$7R&sUXV$rg#^gNg+2)gO>EZo18D208Qa9M z8`$_gu}a!QFmSx07njEsKIvB=dj zE9M3gN^v2t}wNUBbI7S+%zqE-u$Dv>gZA(l_taKlmb6d2eFRBM&489_+hz z;oRS%y1e+=hxU!5->~+!w`-g+9qTS^J^3%!Rd4_0{H@X1+h;c|-u(Kt>8>Yhm#FZN zPIPSkw4VL>^Sw*Z`U@RseQS$A5P8p$J+jU16A`K z2Y(-ZyuRG}QEjL#{A=d>(F2!fj?A69zV+hL^Eb*X?~Zl9v-{2p^RoC@aI~eJIOg8x U{4;a%BF>k{W`@*reFtX#193vGHUIzs literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_36.png b/assets/dolphin/external/L2_Dj_128x64/frame_36.png new file mode 100644 index 0000000000000000000000000000000000000000..fc923b40236dee851d9c3b1f60a64d0050b67350 GIT binary patch literal 1059 zcmaJ=&2G~`5O!4wRh2k$fYZvA_>ow9y>^nd)zBt!N+Z=JN+S_*Vr{P*)A|Q{OWdAN zC4@Nf25{mE55NI9@&pJZj@*#?2w|PXrH863+dDI!Z~o^^fA`__)my8QBwg=q+XHdG z6z}HBviSa4dFzPlChv~;UJ~#L&6w2k6PJNrMEBW%QUCb)ceXA`%df-Xh>x5d%S$4K zmO4rir2;KU>l;N%y#vO9%l5;#DW84*B7@Lx%8zRfa?%bPhubF^+dJ7EdM5{->B}1r zzng(Hff}|n!@>qw99ekG{Jv{)cg?c0N2#nL1#3a+G^0qFPSQg-NPJVmwRod^ZGh zzFjO9E0u}ed3UFHBKGcDe`nYg_jEe_@bmp|L4ls#8Wz9)ep^}++$orTinljQvOH(E Yze-?JTKaM0*{hQ8b$9KL?Wd>z0Hr%cDgXcg literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_4.png b/assets/dolphin/external/L2_Dj_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..d372ff643b40c4cf27bc810fb25a3ed239535b5d GIT binary patch literal 1727 zcmaJ?c~BE)6kiBRhRdT$MX|0y8B{jeY(lcTLx4aMi4uws6;RO4W&<_xrxL@AuyCU9;nM`l^5xU6V3nUzLjT25#P7;MKG0|tsFK)WH`XvOUk&Y)eK**nBM4mhYn zr%548t5Y5Cmd0AZwNjFRMp;RV z-c0LFM$loA(3mo435Q`j6o$d9QoS*3v<@eWNgK~jn0b6I%rh7qu?=cl=~(K28t>G$ zsx!?LFP5^JGAtxh6P@1(n91M!9XS*kHu%RDJyRGWU1lOP43v>p$|M}-3s3WwtkP(u2_s33_|`MNhq0pfV(|zIMbIXT+GI)}>4cb66K%4ln#>>~m4iCG2GgPtCJ5q- zMW_%#NgZbxuYD_hL#@h4m@v#EY;Y7o5ivmtATi7rK&VKFKq!g9kPy|OgoqI82#tWl zV<_?*o%2>Fjfj}`bZpYwXXb8nOq5#AtOElB`~N6w0|4h! zN|{t`Z#k9V9^C+Y6g^v|cS*1A&?cSPF?M`c%kkols=C1J<@3kDFJ4_`ClV*XJO8+! zox!f^_vyi+_Sly-^|^R=-`s(FaNBrO*MH{L9GK+s-Q$JN>Smw6rTXpA*nNQ;8eA2X z9w*z$n~!S!{wE~ffz64j7pBc;6$59x;t87FbTPD|9L4h&xE59jLtLzJo4L-T0dTU< z-<`VZ2eW{r2_bi@XY}TG^pqRQI@~Ebj>uyfq2m5tBmo(HY z__3&Mpdz+)c+p zEi2yNr+jBFH==i9Nqyr1<@Ur!KF8FpE8{#%XU_1cJ&<0os!v`}=~>dus{scZn=&U{ z-G6S>QO@NJe`?}a#Y#JW)$b02JK8(8*vgiEOAD?=`ezsHE4nxPNQ}QUVaB8l;+-=KlMOI!zDSddDuouQvmIZjsBsC!Fjr4Os1%)+;r1jmjZSE;h9VFzIpQee$VfD z-sjnnVVoB_ar#6607CWY+AMjkke}q>0Qub?ywxBt(ULA#$`&nB3G3tmjafADpx(yj z^I1G=E-gCCCjmgfIw2=l$~7!tIMJqHy*>)J%^{-!AZf1K!E(!a2{iHff?W;WKY9WJ z1+yAjlxRQ@s5xSGJs^&CuLAV3M1W32eN?f^J)(-Nm@o&l8F$N_Vi~z?j zNRH?<)3RUk^6&70eZw@b?6|CjMn zZ&yyKgI8wpF0t6j$!B7T@qy*~y*iNBQC7pe?-b;NVGFe)S8U_$l3uHZf~8TbmoX+p|2A%tfC~ktfB+N2n7MYV_-SK z?zO~uPiZu1E$mEO*I zp_6UZqZfQKqt4>>;xjM z^8PHOwjt_zU~pS+L{S8m^zfUe#+G2q@4#fs@xV*_1Uz{|?YxsM->)fCLNVHs=M&a@PPedfEmY;l0M93#=g}bGU*!Tk%Yz zPkH-Z==ck+4_@og4?2$*o4Xe&4sOhnxVNG!>Tb1EDx=nS3GrlSf`5CEc~4!{hr^kX zU^x9pdxWr&udCkP&l&a|>i7FAB~toiO5v_W?wT5O>-PFzvFL)vnEJACzn->bHMzSj zJMlhE*tOFsD_d=Rxv8Y=*ur$n-U-=@&)0Jgn;G`AM@#g1GnzEFTS_m*9y{e9IDJQE z@wu*B$H>_quhq7MZB4Rd85gd9FC=i+uDUq#>mTz&_I2%Eqe?%tEpE!Y2T!&>uoI&hrEhO+t@MJ8-10H(1?aH75N0`nbEpTRxhfH&ZWW7d2gq>8$F!Z2WfVx#EJR z1=R<-kakt;o3lyd{!Kf|Gp6pn_T}wl=5XSpye9pUwJh&n9kBgWwI{X%Xd4dJ?Hpg* QB=;Vm*BQ0VDS4~^1HhwTkpKVy literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_6.png b/assets/dolphin/external/L2_Dj_128x64/frame_6.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1e84a11eeb499fcd47e7709e166c714229afe5 GIT binary patch literal 1635 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 a;?v;Bx|Au6L*e|`zuj&tqdJRfTmA#Jq%n&C literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L2_Dj_128x64/frame_9.png b/assets/dolphin/external/L2_Dj_128x64/frame_9.png new file mode 100644 index 0000000000000000000000000000000000000000..05de5d5c698a86baccd74cfe33d1109ee566f793 GIT binary patch literal 1610 zcmaJ?eNfY87*FY-RYd$4;Mce;$Tm0ACTVDsj7keF*o^{*I5_7`lQe}4+JrQW!qls% zb4>51c<1~=xedSADd@J-`89URhTeJNoJ=VSH#ZzR*%XIt?mU(Pb^hU*OY*+&ljrw5 zzvt^xo0pRnGjY~L003ef3vKzz8c-gKR;|2$)qd<$mYK4>NM0nk9l8c<#6f1aibVx_%_lOD_08-QZBF&a^GRSbnyw?oD#SE^5Usx@1i>h-9*y>W2bU|qgz*Ik_81Qg14f;K0ULWN*L!)*nWgimj zA#hYhE;U1Aq>7w*V1^)ZAgM!O7C}(ZMCw=sLvRLjaqjsbhF}Igf+;tQVibx~7!HnK zkm6Btxv6|x=D0uQWQIy)S)}y(%F0Szr9mf1#d?$^$&d{UgB1q%nM#HWRYfs3fT-PY{$azh)(BY!(QKbvXr#x{j{h@b%@^M3B@+5?UVDl|7ko` z+gDH}a{7GECsatRQWJMl7_8*)*^WYr3L7d{;+4YCWj2AW@Niz)VKYO@7oCfDQLK?9 z7@RP{G|P}MZa_(xMo9wZSRBJqBV%+SuCSe_;W3k)w51!xRbht|!tr)~bp`2}2CCp$Lq?Of+YJO$cg$2_ufd1WO_?PPhr$NaJprF+h5SqCV6) zPjpgPq_k&f`*=2#!{h1Vyvj&P%ItLP`YBGCyQsYE0=u$~jEwBPzUKx2L}WQ^)&hUm zo$YT+XA!Oa>wkOX;<~QBn7V_(e%mts)!T`xh`H12)j!U;=6Bz#Oan$@fu38(fL6=y zNHIRq(5=b)VDjF(NkH}=L-&SmJLV1qs({niGM?YH=EceC#=$8`Q-hj7{gn3o&jo?E z(zd2eh*B{t|4ynq`_6-?nxLh{r!^MdY zC$;gLk^YF851H$lZ!L#-^{JECRQ0OaSb|!JnRBA?=;}?wvpR!l3lKP7f3iO!5`2)@ z_=UCtys5c+DeGLeba8gAs{MdZb@N$M@Qcm}vksJ!osZl6;O; zqZVUKS5pJ1I`6?&2&DzA+N}en>UA zv$^-BzOD#!)VsT4T$(-3&eWm3`4IgLP`WPP Date: Wed, 7 Jun 2023 21:21:55 +0100 Subject: [PATCH 084/102] Typo --- assets/dolphin/external/manifest.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/manifest.txt index d3f026b731..24dd8ab9df 100644 --- a/assets/dolphin/external/manifest.txt +++ b/assets/dolphin/external/manifest.txt @@ -102,7 +102,7 @@ Weight: 4 Name: L2_Wake_up_128x64 Min butthurt: 0 Max butthurt: 14 -Min level: 11 +Min level: 12 Max level: 30 Weight: 3 From 9af3c22a6a3acef2652ced06b3646a928cad9f9e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:39:52 +0300 Subject: [PATCH 085/102] Fix ProtoView issue #503 --- applications/external/protoview/signal_file.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/external/protoview/signal_file.c b/applications/external/protoview/signal_file.c index c60a6a181b..886573a063 100644 --- a/applications/external/protoview/signal_file.c +++ b/applications/external/protoview/signal_file.c @@ -48,8 +48,9 @@ bool save_signal(ProtoViewApp* app, const char* filename) { for(int j = 0; regs[j]; j += 2) { furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]); } - size_t len = furi_string_size(file_content); - furi_string_set_char(custom, len - 1, '\n'); + //size_t len = furi_string_size(file_content); + //furi_string_set_char(custom, len - 1, '\n'); + furi_string_cat(custom, "\n"); furi_string_cat(file_content, custom); furi_string_free(custom); } From 87f70655a2817acc99552d01915827aaa0f8931b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 7 Jun 2023 23:54:01 +0300 Subject: [PATCH 086/102] Remove broken modulation that was causing buffer Overrun Fixes issue #506 --- applications/main/subghz/subghz.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index d30bd79f22..97e2e8c34b 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -211,7 +211,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { flipper_format_free(temp_fm_preset3); - // # HND - FM presets + // # HND - FM preset FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc(); flipper_format_write_string_cstr( temp_fm_preset4, @@ -221,16 +221,6 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz_setting_load_custom_preset(setting, (const char*)"HND_1", 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(setting, (const char*)"HND_2", temp_fm_preset5); - - flipper_format_free(temp_fm_preset5); } // custom presets loading - end From af2ecbc3ed35a1e6b52af7a5b07decd03a2a5186 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 00:26:10 +0300 Subject: [PATCH 087/102] SCD30 Unitemp --- applications/external/unitemp/Sensors.c | 3 +- applications/external/unitemp/Sensors.h | 5 + .../external/unitemp/assets/co2_11x14.png | Bin 0 -> 176 bytes applications/external/unitemp/sensors/SCD30.c | 438 ++++++++++++++++++ applications/external/unitemp/sensors/SCD30.h | 59 +++ .../external/unitemp/views/General_view.c | 38 ++ 6 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 applications/external/unitemp/assets/co2_11x14.png create mode 100644 applications/external/unitemp/sensors/SCD30.c create mode 100644 applications/external/unitemp/sensors/SCD30.h diff --git a/applications/external/unitemp/Sensors.c b/applications/external/unitemp/Sensors.c index 666438bfae..ae90ce4d58 100644 --- a/applications/external/unitemp/Sensors.c +++ b/applications/external/unitemp/Sensors.c @@ -78,7 +78,8 @@ const Interface SPI = { static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT21, &DHT22, &Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10, &SHT30, &GXHT30, &LM75, &HDC1080, &BMP180, - &BMP280, &BME280, &BME680, &MAX31855, &MAX6675}; + &BMP280, &BME280, &BME680, &MAX31855, &MAX6675, + &SCD30}; const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) { if(index > SENSOR_TYPES_COUNT) return NULL; diff --git a/applications/external/unitemp/Sensors.h b/applications/external/unitemp/Sensors.h index d2b7c07af8..25b9cb49e9 100644 --- a/applications/external/unitemp/Sensors.h +++ b/applications/external/unitemp/Sensors.h @@ -24,6 +24,7 @@ #define UT_TEMPERATURE 0b00000001 #define UT_HUMIDITY 0b00000010 #define UT_PRESSURE 0b00000100 +#define UT_CO2 0b00001000 //Статусы опроса датчика typedef enum { @@ -31,6 +32,7 @@ typedef enum { UT_DATA_TYPE_TEMP_HUM = UT_TEMPERATURE | UT_HUMIDITY, UT_DATA_TYPE_TEMP_PRESS = UT_TEMPERATURE | UT_PRESSURE, UT_DATA_TYPE_TEMP_HUM_PRESS = UT_TEMPERATURE | UT_HUMIDITY | UT_PRESSURE, + UT_DATA_TYPE_TEMP_HUM_CO2 = UT_TEMPERATURE | UT_HUMIDITY | UT_CO2, } SensorDataType; //Типы возвращаемых данных @@ -121,6 +123,8 @@ typedef struct Sensor { float hum; //Атмосферное давление float pressure; + // Концентрация CO2 + float co2; //Тип датчика const SensorType* type; //Статус последнего опроса датчика @@ -329,4 +333,5 @@ const GPIO* #include "./sensors/HDC1080.h" #include "./sensors/MAX31855.h" #include "./sensors/MAX6675.h" +#include "./sensors/SCD30.h" #endif diff --git a/applications/external/unitemp/assets/co2_11x14.png b/applications/external/unitemp/assets/co2_11x14.png new file mode 100644 index 0000000000000000000000000000000000000000..2a2b5e068d6153d1f30dfc93acd3b0279ee8d551 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2?f#ZI0f96(URk}M!fC3NGbie=qQ>#|?bzTh2Q%vH}y3IC$ZRvemx8YG!hReRKhg7T% zs+8bg=d#Wzp$PyIY(cC5 literal 0 HcmV?d00001 diff --git a/applications/external/unitemp/sensors/SCD30.c b/applications/external/unitemp/sensors/SCD30.c new file mode 100644 index 0000000000..b5f15b50d6 --- /dev/null +++ b/applications/external/unitemp/sensors/SCD30.c @@ -0,0 +1,438 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +// Some information may be seen on https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library + +#include "SCD30.h" +#include "../interfaces/I2CSensor.h" +//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h> + +inline static uint16_t load16(uint8_t* b) { + uint16_t x; + memcpy(&x, b, 2); + return x; +} + +inline static uint32_t load32(uint8_t* b) { + uint32_t x; + memcpy(&x, b, 4); + return x; +} + +inline static void store16(uint8_t* b, uint16_t i) { + memcpy(b, &i, 2); +} + +inline static void store32(uint8_t* b, uint32_t i) { + memcpy(b, &i, 4); +} + +#if BYTE_ORDER == BIG_ENDIAN +#define htobe16(x) (x) +#define htobe32(x) (x) +#define htole16(x) __builtin_bswap16(x) +#define htole32(x) __builtin_bswap32(x) +#define be16toh(x) (x) +#define be32toh(x) (x) +#define le16toh(x) __builtin_bswap16(x) +#define le32toh(x) __builtin_bswap32(x) +#elif BYTE_ORDER == LITTLE_ENDIAN +#define htobe16(x) __builtin_bswap16(x) +#define htobe32(x) __builtin_bswap32(x) +#define htole16(x) (x) +#define htole32(x) (x) +#define be16toh(x) __builtin_bswap16(x) +#define be32toh(x) __builtin_bswap32(x) +#define le16toh(x) (x) +#define le32toh(x) (x) +#else +#error "What kind of system is this?" +#endif + +#define load16_le(b) (le16toh(load16(b))) +#define load32_le(b) (le32toh(load32(b))) +#define store16_le(b, i) (store16(b, htole16(i))) +#define store32_le(b, i) (store32(b, htole32(i))) + +#define load16_be(b) (be16toh(load16(b))) +#define load32_be(b) (be32toh(load32(b))) +#define store16_be(b, i) (store16(b, htobe16(i))) +#define store32_be(b, i) (store32(b, htobe32(i))) + +typedef union { + uint16_t array16[2]; + uint8_t array8[4]; + float value; +} ByteToFl; + +bool unitemp_SCD30_alloc(Sensor* sensor, char* args); +bool unitemp_SCD30_init(Sensor* sensor); +bool unitemp_SCD30_deinit(Sensor* sensor); +UnitempStatus unitemp_SCD30_update(Sensor* sensor); +bool unitemp_SCD30_free(Sensor* sensor); + +const SensorType SCD30 = { + .typename = "SCD30", + .interface = &I2C, + .datatype = UT_DATA_TYPE_TEMP_HUM_CO2, + .pollingInterval = 2000, + .allocator = unitemp_SCD30_alloc, + .mem_releaser = unitemp_SCD30_free, + .initializer = unitemp_SCD30_init, + .deinitializer = unitemp_SCD30_deinit, + .updater = unitemp_SCD30_update}; + +#define SCD30_ID 0x61 + +#define COMMAND_CONTINUOUS_MEASUREMENT 0x0010 +#define COMMAND_SET_MEASUREMENT_INTERVAL 0x4600 +#define COMMAND_GET_DATA_READY 0x0202 +#define COMMAND_READ_MEASUREMENT 0x0300 +#define COMMAND_AUTOMATIC_SELF_CALIBRATION 0x5306 +#define COMMAND_SET_FORCED_RECALIBRATION_FACTOR 0x5204 +#define COMMAND_SET_TEMPERATURE_OFFSET 0x5403 +#define COMMAND_SET_ALTITUDE_COMPENSATION 0x5102 +#define COMMAND_RESET 0xD304 // Soft reset +#define COMMAND_STOP_MEAS 0x0104 +#define COMMAND_READ_FW_VER 0xD100 + +static bool dataAvailable(Sensor* sensor) __attribute__((unused)); +static bool readMeasurement(Sensor* sensor) __attribute__((unused)); +static void reset(Sensor* sensor) __attribute__((unused)); + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) __attribute__((unused)); +static bool getAutoSelfCalibration(Sensor* sensor) __attribute__((unused)); + +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) __attribute__((unused)); + +static bool setForcedRecalibrationFactor(Sensor* sensor, uint16_t concentration) + __attribute__((unused)); +static uint16_t getAltitudeCompensation(Sensor* sensor) __attribute__((unused)); +static bool setAltitudeCompensation(Sensor* sensor, uint16_t altitude) __attribute__((unused)); +static bool setAmbientPressure(Sensor* sensor, uint16_t pressure_mbar) __attribute__((unused)); + +static float getTemperatureOffset(Sensor* sensor) __attribute__((unused)); +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused)); + +static bool beginMeasuringWithSettings(Sensor* sensor, uint16_t pressureOffset) + __attribute__((unused)); +static bool beginMeasuring(Sensor* sensor) __attribute__((unused)); +static bool stopMeasurement(Sensor* sensor) __attribute__((unused)); + +static bool setMeasurementInterval(Sensor* sensor, uint16_t interval) __attribute__((unused)); +static uint16_t getMeasurementInterval(Sensor* sensor) __attribute__((unused)); + +bool unitemp_SCD30_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + i2c_sensor->minI2CAdr = SCD30_ID << 1; + i2c_sensor->maxI2CAdr = SCD30_ID << 1; + return true; +} + +bool unitemp_SCD30_free(Sensor* sensor) { + //Нечего высвобождать, так как ничего не было выделено + UNUSED(sensor); + return true; +} + +bool unitemp_SCD30_init(Sensor* sensor) { + if(beginMeasuring(sensor) == true) { // Start continuous measurements + setMeasurementInterval(sensor, SCD30.pollingInterval / 1000); + setAutoSelfCalibration(sensor, true); + setAmbientPressure(sensor, 0); + } else + return false; + + return true; +} + +bool unitemp_SCD30_deinit(Sensor* sensor) { + return stopMeasurement(sensor); +} + +UnitempStatus unitemp_SCD30_update(Sensor* sensor) { + readMeasurement(sensor); + return UT_SENSORSTATUS_OK; +} + +static uint8_t computeCRC8(uint8_t* message, uint8_t len) { + uint8_t crc = 0xFF; // Init with 0xFF + for(uint8_t x = 0; x < len; x++) { + crc ^= message[x]; // XOR-in the next input byte + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; // No output reflection +} + +// Sends a command along with arguments and CRC +static bool sendCommandWithCRC(Sensor* sensor, uint16_t command, uint16_t arguments) { + static const uint8_t cmdSize = 5; + + uint8_t bytes[cmdSize]; + uint8_t* pointer = bytes; + store16_be(pointer, command); + pointer += 2; + uint8_t* argPos = pointer; + store16_be(pointer, arguments); + pointer += 2; + *pointer = computeCRC8(argPos, pointer - argPos); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +// Sends just a command, no arguments, no CRC +static bool sendCommand(Sensor* sensor, uint16_t command) { + static const uint8_t cmdSize = 2; + + uint8_t bytes[cmdSize]; + store16_be(bytes, command); + + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes); +} + +static uint16_t readRegister(Sensor* sensor, uint16_t registerAddress) { + static const uint8_t regSize = 2; + + if(!sendCommand(sensor, registerAddress)) return 0; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[regSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, regSize, bytes)) return 0; + + return load16_be(bytes); +} + +static bool loadWord(uint8_t* buff, uint16_t* val) { + uint16_t tmp = load16_be(buff); + uint8_t expectedCRC = computeCRC8(buff, 2); + if(buff[2] != expectedCRC) return false; + *val = tmp; + return true; +} + +static bool getSettingValue(Sensor* sensor, uint16_t registerAddress, uint16_t* val) { + static const uint8_t respSize = 3; + + if(!sendCommand(sensor, registerAddress)) return false; // Sensor did not ACK + + furi_delay_ms(3); + + uint8_t bytes[respSize]; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) return false; + + return loadWord(bytes, val); +} + +static bool loadFloat(uint8_t* buff, float* val) { + // ByteToFl tmp; + size_t cntr = 0; + uint8_t floatBuff[4]; + for(size_t i = 0; i < 2; i++) { + floatBuff[cntr++] = buff[0]; + floatBuff[cntr++] = buff[1]; + uint8_t expectedCRC = computeCRC8(buff, 2); + if(buff[2] != expectedCRC) return false; + buff += 3; + } + uint32_t tmpVal = load32_be(floatBuff); + *val = *(float*)&tmpVal; + return true; +} + +// Get 18 bytes from SCD30 +// Updates global variables with floats +// Returns true if success +static bool readMeasurement(Sensor* sensor) { + // Verify we have data from the sensor + if(!dataAvailable(sensor)) { + return false; + } + + if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) { + FURI_LOG_E(APP_NAME, "Sensor did not ACK"); + return false; // Sensor did not ACK + } + + float tempCO2 = 0; + float tempHumidity = 0; + float tempTemperature = 0; + + furi_delay_ms(3); + + static const uint8_t respSize = 18; + uint8_t buff[respSize]; + uint8_t* bytes = buff; + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) { + FURI_LOG_E(APP_NAME, "Error while read measures"); + return false; + } + + bool error = false; + if(loadFloat(bytes, &tempCO2)) { + sensor->co2 = tempCO2; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing CO2"); + error = true; + } + + bytes += 6; + if(loadFloat(bytes, &tempTemperature)) { + sensor->temp = tempTemperature; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing temp"); + error = true; + } + + bytes += 6; + if(loadFloat(bytes, &tempHumidity)) { + sensor->hum = tempHumidity; + } else { + FURI_LOG_E(APP_NAME, "Error while parsing humidity"); + error = true; + } + + return !error; +} + +static void reset(Sensor* sensor) { + sendCommand(sensor, COMMAND_RESET); +} + +static bool setAutoSelfCalibration(Sensor* sensor, bool enable) { + return sendCommandWithCRC( + sensor, COMMAND_AUTOMATIC_SELF_CALIBRATION, enable); // Activate continuous ASC +} + +// Get the current ASC setting +static bool getAutoSelfCalibration(Sensor* sensor) { + return 1 == readRegister(sensor, COMMAND_AUTOMATIC_SELF_CALIBRATION); +} + +static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) { + return getSettingValue(sensor, COMMAND_READ_FW_VER, val); +} + +// Set the forced recalibration factor. See 1.3.7. +// The reference CO2 concentration has to be within the range 400 ppm ≤ cref(CO2) ≤ 2000 ppm. +static bool setForcedRecalibrationFactor(Sensor* sensor, uint16_t concentration) { + if(concentration < 400 || concentration > 2000) { + return false; // Error check. + } + return sendCommandWithCRC(sensor, COMMAND_SET_FORCED_RECALIBRATION_FACTOR, concentration); +} + +// Get the temperature offset. See 1.3.8. +static float getTemperatureOffset(Sensor* sensor) { + union { + int16_t signed16; + uint16_t unsigned16; + } signedUnsigned; // Avoid any ambiguity casting int16_t to uint16_t + signedUnsigned.unsigned16 = readRegister(sensor, COMMAND_SET_TEMPERATURE_OFFSET); + + return ((float)signedUnsigned.signed16) / 100.0; +} + +static bool setTemperatureOffset(Sensor* sensor, float tempOffset) { + // Temp offset is only positive. See: https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library/issues/27#issuecomment-971986826 + //"The SCD30 offset temperature is obtained by subtracting the reference temperature from the SCD30 output temperature" + // https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/9.5_CO2/Sensirion_CO2_Sensors_SCD30_Low_Power_Mode.pdf + + if(tempOffset < 0.0) return false; + + uint16_t value = tempOffset * 100; + + return sendCommandWithCRC(sensor, COMMAND_SET_TEMPERATURE_OFFSET, value); +} + +// Get the altitude compenstation. See 1.3.9. +static uint16_t getAltitudeCompensation(Sensor* sensor) { + return readRegister(sensor, COMMAND_SET_ALTITUDE_COMPENSATION); +} + +// Set the altitude compenstation. See 1.3.9. +static bool setAltitudeCompensation(Sensor* sensor, uint16_t altitude) { + return sendCommandWithCRC(sensor, COMMAND_SET_ALTITUDE_COMPENSATION, altitude); +} + +// Set the pressure compenstation. This is passed during measurement startup. +// mbar can be 700 to 1200 +static bool setAmbientPressure(Sensor* sensor, uint16_t pressure_mbar) { + if(pressure_mbar != 0 || pressure_mbar < 700 || pressure_mbar > 1200) { + return false; + } + return sendCommandWithCRC(sensor, COMMAND_CONTINUOUS_MEASUREMENT, pressure_mbar); +} + +// Begins continuous measurements +// Continuous measurement status is saved in non-volatile memory. When the sensor +// is powered down while continuous measurement mode is active SCD30 will measure +// continuously after repowering without sending the measurement command. +// Returns true if successful +static bool beginMeasuringWithSettings(Sensor* sensor, uint16_t pressureOffset) { + return sendCommandWithCRC(sensor, COMMAND_CONTINUOUS_MEASUREMENT, pressureOffset); +} + +// Overload - no pressureOffset +static bool beginMeasuring(Sensor* sensor) { + return beginMeasuringWithSettings(sensor, 0); +} + +// Stop continuous measurement +static bool stopMeasurement(Sensor* sensor) { + return sendCommand(sensor, COMMAND_STOP_MEAS); +} + +// Sets interval between measurements +// 2 seconds to 1800 seconds (30 minutes) +static bool setMeasurementInterval(Sensor* sensor, uint16_t interval) { + if(interval < 2 || interval > 1800) return false; + if(!sendCommandWithCRC(sensor, COMMAND_SET_MEASUREMENT_INTERVAL, interval)) return false; + uint16_t verInterval = readRegister(sensor, COMMAND_SET_MEASUREMENT_INTERVAL); + if(verInterval != interval) { + FURI_LOG_E(APP_NAME, "Measure interval wrong! Val: %02x", verInterval); + return false; + } + return true; +} + +// Gets interval between measurements +// 2 seconds to 1800 seconds (30 minutes) +static uint16_t getMeasurementInterval(Sensor* sensor) { + uint16_t interval = 0; + getSettingValue(sensor, COMMAND_SET_MEASUREMENT_INTERVAL, &interval); + return interval; +} + +// Returns true when data is available +static bool dataAvailable(Sensor* sensor) { + return 1 == readRegister(sensor, COMMAND_GET_DATA_READY); +} \ No newline at end of file diff --git a/applications/external/unitemp/sensors/SCD30.h b/applications/external/unitemp/sensors/SCD30.h new file mode 100644 index 0000000000..1ebfbb4119 --- /dev/null +++ b/applications/external/unitemp/sensors/SCD30.h @@ -0,0 +1,59 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by divinebird (https://github.com/divinebird) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_SCD30 +#define UNITEMP_SCD30 + +#include "../unitemp.h" +#include "../Sensors.h" + +extern const SensorType SCD30; +/** + * @brief Выделение памяти и установка начальных значений датчика SCD30 + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_SCD30_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика SCD30 + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_SCD30_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD30_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * @param sensor Указатель на датчик + * @return Статус опроса датчика + */ +UnitempStatus unitemp_SCD30_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * @param sensor Указатель на датчик + */ +bool unitemp_SCD30_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c index 2c8d389bfa..dd045d3362 100644 --- a/applications/external/unitemp/views/General_view.c +++ b/applications/external/unitemp/views/General_view.c @@ -159,7 +159,34 @@ static void _draw_pressure(Canvas* canvas, Sensor* sensor) { canvas_draw_str(canvas, x + 52, y + 13, "kPa"); } else if(app->settings.pressure_unit == UT_PRESSURE_HPA) { canvas_draw_str(canvas, x + 67, y + 13, "hPa"); + +static void _draw_co2(Canvas* canvas, Sensor* sensor, Color color) { + const uint8_t x = 29, y = 39; + //Рисование рамки + canvas_draw_rframe(canvas, x, y, 75, 20, 3); + if(color == ColorBlack) { + canvas_draw_rbox(canvas, x, y, 75, 19, 3); + canvas_invert_color(canvas); + } else { + canvas_draw_rframe(canvas, x, y, 75, 19, 3); + } + + //Рисование иконки + canvas_draw_icon(canvas, x + 3, y + 3, &I_co2_11x14); + + int16_t concentration_int = sensor->co2; + // int8_t concentration_dec = (int16_t)(sensor->co2 * 10) % 10; + + //Целая часть + if(concentration_int > 9999) { + snprintf(app->buff, BUFF_SIZE, "MAX "); + canvas_set_font(canvas, FontPrimary); + } else { + snprintf(app->buff, BUFF_SIZE, "%d", concentration_int); + canvas_set_font(canvas, FontBigNumbers); } + + canvas_draw_str_aligned(canvas, x + 70, y + 10, AlignRight, AlignCenter, app->buff); } static void _draw_singleSensor(Canvas* canvas, Sensor* sensor, const uint8_t pos[2], Color color) { @@ -320,6 +347,17 @@ static void _draw_carousel_values(Canvas* canvas) { canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]); _draw_pressure(canvas, unitemp_sensor_getActive(generalview_sensor_index)); break; + case UT_DATA_TYPE_TEMP_HUM_CO2: + _draw_temperature( + canvas, + unitemp_sensor_getActive(generalview_sensor_index), + temp_positions[2][0], + temp_positions[2][1], + ColorWhite); + _draw_humidity( + canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]); + _draw_co2(canvas, unitemp_sensor_getActive(generalview_sensor_index), ColorWhite); + break; } } From 47734f24599edb39c9bd4ceec9196d00802bbcc2 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 00:26:50 +0300 Subject: [PATCH 088/102] Fix --- applications/external/unitemp/views/General_view.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/applications/external/unitemp/views/General_view.c b/applications/external/unitemp/views/General_view.c index dd045d3362..22d724935f 100644 --- a/applications/external/unitemp/views/General_view.c +++ b/applications/external/unitemp/views/General_view.c @@ -159,6 +159,8 @@ static void _draw_pressure(Canvas* canvas, Sensor* sensor) { canvas_draw_str(canvas, x + 52, y + 13, "kPa"); } else if(app->settings.pressure_unit == UT_PRESSURE_HPA) { canvas_draw_str(canvas, x + 67, y + 13, "hPa"); + } +} static void _draw_co2(Canvas* canvas, Sensor* sensor, Color color) { const uint8_t x = 29, y = 39; From ffda6ad32102b27e493cf0d49e434997f11938a7 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:03:07 +0300 Subject: [PATCH 089/102] Fix? I have no way to check if sensor still works --- applications/external/unitemp/sensors/SCD30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/unitemp/sensors/SCD30.c b/applications/external/unitemp/sensors/SCD30.c index b5f15b50d6..627130da75 100644 --- a/applications/external/unitemp/sensors/SCD30.c +++ b/applications/external/unitemp/sensors/SCD30.c @@ -263,7 +263,7 @@ static bool loadFloat(uint8_t* buff, float* val) { buff += 3; } uint32_t tmpVal = load32_be(floatBuff); - *val = *(float*)&tmpVal; + memcpy(val, &tmpVal, sizeof(float)); return true; } From 016249c982ac4d8ac68427e607eab68f232a246a Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:05:38 +0300 Subject: [PATCH 090/102] bump ver --- applications/external/unitemp/unitemp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/external/unitemp/unitemp.h b/applications/external/unitemp/unitemp.h index 4d184b2c37..69cd8cf4f2 100644 --- a/applications/external/unitemp/unitemp.h +++ b/applications/external/unitemp/unitemp.h @@ -40,7 +40,7 @@ //Имя приложения #define APP_NAME "Unitemp" //Версия приложения -#define UNITEMP_APP_VER "1.2" +#define UNITEMP_APP_VER "1.3" //Путь хранения файлов плагина #define APP_PATH_FOLDER "/ext/unitemp" //Имя файла с настройками From c5f062be9a0715ef34016e4fabf186df681e681c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:30:29 +0300 Subject: [PATCH 091/102] Rename and remove old apps --- ReadMe.md | 4 +- applications/external/flipfrid/LICENSE.md | 8 - applications/external/flipfrid/README.md | 36 - .../external/flipfrid/application.fam | 12 - applications/external/flipfrid/flipfrid.c | 276 -------- applications/external/flipfrid/flipfrid.h | 94 --- .../scene/flipfrid_scene_entrypoint.c | 201 ------ .../scene/flipfrid_scene_entrypoint.h | 8 - .../scene/flipfrid_scene_load_custom_uids.c | 85 --- .../scene/flipfrid_scene_load_custom_uids.h | 9 - .../flipfrid/scene/flipfrid_scene_load_file.c | 193 ----- .../flipfrid/scene/flipfrid_scene_load_file.h | 9 - .../scene/flipfrid_scene_run_attack.c | 666 ------------------ .../scene/flipfrid_scene_run_attack.h | 8 - .../scene/flipfrid_scene_select_field.c | 160 ----- .../scene/flipfrid_scene_select_field.h | 9 - applications/external/ibtn_fuzzer/LICENSE.md | 8 - .../external/ibtn_fuzzer/application.fam | 12 - .../external/ibtn_fuzzer/ibtnfuzzer.c | 280 -------- .../external/ibtn_fuzzer/ibtnfuzzer.h | 94 --- .../ibtn_fuzzer/images/ibutt_10px.png | Bin 304 -> 0 bytes .../scene/ibtnfuzzer_scene_entrypoint.c | 201 ------ .../scene/ibtnfuzzer_scene_entrypoint.h | 8 - .../scene/ibtnfuzzer_scene_load_custom_uids.c | 86 --- .../scene/ibtnfuzzer_scene_load_custom_uids.h | 9 - .../scene/ibtnfuzzer_scene_load_file.c | 293 -------- .../scene/ibtnfuzzer_scene_load_file.h | 9 - .../scene/ibtnfuzzer_scene_run_attack.c | 501 ------------- .../scene/ibtnfuzzer_scene_run_attack.h | 8 - .../scene/ibtnfuzzer_scene_select_field.c | 160 ----- .../scene/ibtnfuzzer_scene_select_field.h | 9 - .../application.fam | 16 +- .../{pacs_fuzzer => multi_fuzzer}/fuzzer.c | 0 .../{pacs_fuzzer => multi_fuzzer}/fuzzer_i.h | 0 .../helpers/fuzzer_custom_event.h | 0 .../helpers/fuzzer_types.h | 0 .../icons}/125_10px.png | Bin .../icons/ButtonLeft_4x7.png | Bin .../icons/ButtonRight_4x7.png | Bin .../icons/Ok_btn_9x9.png | Bin .../icons/Pin_arrow_up_7x9.png | Bin .../icons/Pin_back_arrow_10x8.png | Bin .../icons}/ibutt_10px.png | Bin .../icons}/rfid_10px.png | Bin .../lib/worker/fake_worker.c | 0 .../lib/worker/fake_worker.h | 0 .../lib/worker/protocol.c | 0 .../lib/worker/protocol.h | 0 .../lib/worker/protocol_i.h | 0 .../scenes/fuzzer_scene.c | 0 .../scenes/fuzzer_scene.h | 0 .../scenes/fuzzer_scene_attack.c | 0 .../scenes/fuzzer_scene_config.h | 0 .../scenes/fuzzer_scene_field_editor.c | 0 .../scenes/fuzzer_scene_main.c | 0 .../{pacs_fuzzer => multi_fuzzer}/todo.md | 0 .../views/attack.c | 0 .../views/attack.h | 0 .../views/field_editor.c | 0 .../views/field_editor.h | 0 .../views/main_menu.c | 0 .../views/main_menu.h | 0 .../external/pacs_fuzzer/icons/125_10px.png | Bin 308 -> 0 bytes .../external/pacs_fuzzer/icons/ibutt_10px.png | Bin 304 -> 0 bytes .../external/pacs_fuzzer/icons/rfid_10px.png | Bin 2389 -> 0 bytes 65 files changed, 10 insertions(+), 3462 deletions(-) delete mode 100644 applications/external/flipfrid/LICENSE.md delete mode 100644 applications/external/flipfrid/README.md delete mode 100644 applications/external/flipfrid/application.fam delete mode 100644 applications/external/flipfrid/flipfrid.c delete mode 100644 applications/external/flipfrid/flipfrid.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_load_file.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_load_file.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_run_attack.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_run_attack.h delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_select_field.c delete mode 100644 applications/external/flipfrid/scene/flipfrid_scene_select_field.h delete mode 100644 applications/external/ibtn_fuzzer/LICENSE.md delete mode 100644 applications/external/ibtn_fuzzer/application.fam delete mode 100644 applications/external/ibtn_fuzzer/ibtnfuzzer.c delete mode 100644 applications/external/ibtn_fuzzer/ibtnfuzzer.h delete mode 100644 applications/external/ibtn_fuzzer/images/ibutt_10px.png delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c delete mode 100644 applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h rename applications/external/{pacs_fuzzer => multi_fuzzer}/application.fam (81%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/fuzzer.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/fuzzer_i.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/helpers/fuzzer_custom_event.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/helpers/fuzzer_types.h (100%) rename applications/external/{flipfrid/images => multi_fuzzer/icons}/125_10px.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/ButtonLeft_4x7.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/ButtonRight_4x7.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/Ok_btn_9x9.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/Pin_arrow_up_7x9.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/icons/Pin_back_arrow_10x8.png (100%) rename applications/external/{ibtn_fuzzer => multi_fuzzer/icons}/ibutt_10px.png (100%) rename applications/external/{flipfrid => multi_fuzzer/icons}/rfid_10px.png (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/fake_worker.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/fake_worker.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/protocol.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/protocol.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/lib/worker/protocol_i.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene_attack.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene_config.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene_field_editor.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/scenes/fuzzer_scene_main.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/todo.md (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/attack.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/attack.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/field_editor.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/field_editor.h (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/main_menu.c (100%) rename applications/external/{pacs_fuzzer => multi_fuzzer}/views/main_menu.h (100%) delete mode 100644 applications/external/pacs_fuzzer/icons/125_10px.png delete mode 100644 applications/external/pacs_fuzzer/icons/ibutt_10px.png delete mode 100644 applications/external/pacs_fuzzer/icons/rfid_10px.png diff --git a/ReadMe.md b/ReadMe.md index 23128cac03..f9e759e0bf 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -134,7 +134,8 @@ You can support us by using links or addresses below: ### Community apps included: -- **RFID Fuzzer** [(by Ganapati & @xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/54) & New protocols by @mvanzanten +- **RFID Fuzzer** [(by @gid9798)](https://github.com/DarkFlippers/unleashed-firmware/pull/507) (original by Ganapati & xMasterX) +- **iButton Fuzzer** [(by @gid9798)](https://github.com/DarkFlippers/unleashed-firmware/pull/507) (original by xMasterX) - **Sub-GHz bruteforcer** [(by @derskythe & xMasterX)](https://github.com/derskythe/flipperzero-subbrute) [(original by Ganapati & xMasterX)](https://github.com/DarkFlippers/unleashed-firmware/pull/57) - **Sub-GHz playlist** [(by darmiel)](https://github.com/DarkFlippers/unleashed-firmware/pull/62) - ESP8266 Deauther plugin [(by SequoiaSan)](https://github.com/SequoiaSan/FlipperZero-Wifi-ESP8266-Deauther-Module) @@ -157,7 +158,6 @@ You can support us by using links or addresses below: - Morse Code [(by wh00hw)](https://github.com/wh00hw/MorseCodeFAP) - **Unitemp - Temperature sensors reader** (DHT11/22, DS18B20, BMP280, HTU21x and more) [(by quen0n)](https://github.com/quen0n/unitemp-flipperzero) - BH1750 - Lightmeter [(by oleksiikutuzov)](https://github.com/oleksiikutuzov/flipperzero-lightmeter) -- **iButton Fuzzer** [(by xMasterX)](https://github.com/xMasterX/ibutton-fuzzer) - HEX Viewer [(by QtRoS)](https://github.com/QtRoS/flipper-zero-hex-viewer) - POCSAG Pager [(by xMasterX & Shmuma)](https://github.com/xMasterX/flipper-pager) - Text Viewer [(by kowalski7cc & kyhwana)](https://github.com/kowalski7cc/flipper-zero-text-viewer/tree/refactor-text-app) diff --git a/applications/external/flipfrid/LICENSE.md b/applications/external/flipfrid/LICENSE.md deleted file mode 100644 index a856581c9f..0000000000 --- a/applications/external/flipfrid/LICENSE.md +++ /dev/null @@ -1,8 +0,0 @@ -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * @G4N4P4T1 wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. - * ---------------------------------------------------------------------------- - */ \ No newline at end of file diff --git a/applications/external/flipfrid/README.md b/applications/external/flipfrid/README.md deleted file mode 100644 index 83d849454e..0000000000 --- a/applications/external/flipfrid/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Flipfrid - -Basic LFRFID Fuzzer. - -## Why - -Flipfrid is a simple Rfid fuzzer using lfrfid protocols (125khz). -Objective is to provide a simple to use fuzzer to test readers by emulating various cards. - -- EM4100 cards use a 1 byte customer id and 4 bytes card id. -- HIDProx cards use a 2 byte customer id and 3 byte card id. - -## How - -1) Select the Protocol with the left and right arrows -2) Select the Mode with the up and down arrows -3) Set TD (Time delay) between ID switch if you need, or left it as is -4) Click Start - -### Info - -There are 4 Protocols: -- EM4100 -- HIDProx -- PAC/Stanley -- H10301 - -There are 4 modes: -- Default Values: Try factory/default keys and emulate one after the other. -- BF customer id: An iteration from 0X00 to 0XFF on the first byte. -- Load Dump file: Load an existing dump (.rfid) generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF; -- Uids list: Iterate over an input text file (one uid per line) and emulate one after the other. - - -TODO : -- Add second byte test to `BF customer id` diff --git a/applications/external/flipfrid/application.fam b/applications/external/flipfrid/application.fam deleted file mode 100644 index 1ddd6ba50b..0000000000 --- a/applications/external/flipfrid/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="rfid_fuzzer", - name="RFID Fuzzer", - apptype=FlipperAppType.EXTERNAL, - entry_point="flipfrid_start", - requires=["gui", "storage", "dialogs", "input", "notification"], - stack_size=2 * 1024, - order=15, - fap_icon="rfid_10px.png", - fap_category="Tools", - fap_icon_assets="images", -) diff --git a/applications/external/flipfrid/flipfrid.c b/applications/external/flipfrid/flipfrid.c deleted file mode 100644 index 1cf7be8652..0000000000 --- a/applications/external/flipfrid/flipfrid.c +++ /dev/null @@ -1,276 +0,0 @@ -#include "flipfrid.h" - -#include "scene/flipfrid_scene_entrypoint.h" -#include "scene/flipfrid_scene_load_file.h" -#include "scene/flipfrid_scene_select_field.h" -#include "scene/flipfrid_scene_run_attack.h" -#include "scene/flipfrid_scene_load_custom_uids.h" - -#define RFIDFUZZER_APP_FOLDER "/ext/rfidfuzzer" - -static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - FlipFridState* flipfrid_state = ctx; - furi_mutex_acquire(flipfrid_state->mutex, FuriWaitForever); - - // Draw correct Canvas - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_draw(canvas, flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_draw(canvas, flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_draw(canvas, flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_draw(canvas, flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_draw(canvas, flipfrid_state); - break; - } - - furi_mutex_release(flipfrid_state->mutex); -} - -void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - FlipFridEvent event = { - .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; - furi_message_queue_put(event_queue, &event, 25); -} - -static void flipfrid_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - FlipFridEvent event = { - .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; - furi_message_queue_put(event_queue, &event, 25); -} - -FlipFridState* flipfrid_alloc() { - FlipFridState* flipfrid = malloc(sizeof(FlipFridState)); - flipfrid->notification_msg = furi_string_alloc(); - flipfrid->attack_name = furi_string_alloc(); - flipfrid->proto_name = furi_string_alloc(); - flipfrid->data_str = furi_string_alloc(); - - flipfrid->main_menu_items[0] = furi_string_alloc_set("Default Values"); - flipfrid->main_menu_items[1] = furi_string_alloc_set("BF Customer ID"); - flipfrid->main_menu_items[2] = furi_string_alloc_set("Load File"); - flipfrid->main_menu_items[3] = furi_string_alloc_set("Load UIDs from file"); - - flipfrid->main_menu_proto_items[0] = furi_string_alloc_set("EM4100"); - flipfrid->main_menu_proto_items[1] = furi_string_alloc_set("HIDProx"); - flipfrid->main_menu_proto_items[2] = furi_string_alloc_set("PAC/Stanley"); - flipfrid->main_menu_proto_items[3] = furi_string_alloc_set("H10301"); - - flipfrid->previous_scene = NoneScene; - flipfrid->current_scene = SceneEntryPoint; - flipfrid->is_running = true; - flipfrid->is_attacking = false; - flipfrid->key_index = 0; - flipfrid->menu_index = 0; - flipfrid->menu_proto_index = 0; - - flipfrid->attack = FlipFridAttackDefaultValues; - flipfrid->notify = furi_record_open(RECORD_NOTIFICATION); - - flipfrid->data[0] = 0x00; - flipfrid->data[1] = 0x00; - flipfrid->data[2] = 0x00; - flipfrid->data[3] = 0x00; - flipfrid->data[4] = 0x00; - flipfrid->data[5] = 0x00; - - flipfrid->payload[0] = 0x00; - flipfrid->payload[1] = 0x00; - flipfrid->payload[2] = 0x00; - flipfrid->payload[3] = 0x00; - flipfrid->payload[4] = 0x00; - flipfrid->payload[5] = 0x00; - - //Dialog - flipfrid->dialogs = furi_record_open(RECORD_DIALOGS); - - return flipfrid; -} - -void flipfrid_free(FlipFridState* flipfrid) { - //Dialog - furi_record_close(RECORD_DIALOGS); - notification_message(flipfrid->notify, &sequence_blink_stop); - - // Strings - furi_string_free(flipfrid->notification_msg); - furi_string_free(flipfrid->attack_name); - furi_string_free(flipfrid->proto_name); - furi_string_free(flipfrid->data_str); - - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(flipfrid->main_menu_items[i]); - } - - for(uint32_t i = 0; i < 4; i++) { - furi_string_free(flipfrid->main_menu_proto_items[i]); - } - - // The rest - free(flipfrid); -} - -// ENTRYPOINT -int32_t flipfrid_start(void* p) { - UNUSED(p); - // Input - FURI_LOG_I(TAG, "Initializing input"); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent)); - FlipFridState* flipfrid_state = flipfrid_alloc(); - - flipfrid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!flipfrid_state->mutex) { - FURI_LOG_E(TAG, "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_NOTIFICATION); - flipfrid_free(flipfrid_state); - return 255; - } - - Storage* storage = furi_record_open(RECORD_STORAGE); - if(!storage_simply_mkdir(storage, RFIDFUZZER_APP_FOLDER)) { - FURI_LOG_E(TAG, "Could not create folder %s", RFIDFUZZER_APP_FOLDER); - } - furi_record_close(RECORD_STORAGE); - - // Configure view port - FURI_LOG_I(TAG, "Initializing viewport"); - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, flipfrid_draw_callback, flipfrid_state); - view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue); - - // Configure timer - FURI_LOG_I(TAG, "Initializing timer"); - FuriTimer* timer = - furi_timer_alloc(flipfrid_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second - - // Register view port in GUI - FURI_LOG_I(TAG, "Initializing gui"); - Gui* gui = (Gui*)furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - // Init values - FlipFridEvent event; - while(flipfrid_state->is_running) { - // Get next event - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); - if(event_status == FuriStatusOk) { - if(event.evt_type == EventTypeKey) { - //Handle event key - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_event(event, flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_event(event, flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_event(event, flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_event(event, flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_event(event, flipfrid_state); - break; - } - - } else if(event.evt_type == EventTypeTick) { - //Handle event tick - if(flipfrid_state->current_scene != flipfrid_state->previous_scene) { - // Trigger Exit Scene - switch(flipfrid_state->previous_scene) { - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_exit(flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_exit(flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_exit(flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_exit(flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_exit(flipfrid_state); - break; - case NoneScene: - break; - } - - // Trigger Entry Scene - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_enter(flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_enter(flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_enter(flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_enter(flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_enter(flipfrid_state); - break; - } - flipfrid_state->previous_scene = flipfrid_state->current_scene; - } - - // Trigger Tick Scene - switch(flipfrid_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - flipfrid_scene_entrypoint_on_tick(flipfrid_state); - break; - case SceneSelectFile: - flipfrid_scene_load_file_on_tick(flipfrid_state); - break; - case SceneSelectField: - flipfrid_scene_select_field_on_tick(flipfrid_state); - break; - case SceneAttack: - flipfrid_scene_run_attack_on_tick(flipfrid_state); - break; - case SceneLoadCustomUids: - flipfrid_scene_load_custom_uids_on_tick(flipfrid_state); - break; - } - view_port_update(view_port); - } - } - } - - // Cleanup - furi_timer_stop(timer); - furi_timer_free(timer); - - FURI_LOG_I(TAG, "Cleaning up"); - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_mutex_free(flipfrid_state->mutex); - flipfrid_free(flipfrid_state); - - return 0; -} \ No newline at end of file diff --git a/applications/external/flipfrid/flipfrid.h b/applications/external/flipfrid/flipfrid.h deleted file mode 100644 index b95f9f75f1..0000000000 --- a/applications/external/flipfrid/flipfrid.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#define TAG "FlipFrid" - -typedef enum { - FlipFridAttackDefaultValues, - FlipFridAttackBfCustomerId, - FlipFridAttackLoadFile, - FlipFridAttackLoadFileCustomUids, -} FlipFridAttacks; - -typedef enum { - EM4100, - HIDProx, - PAC, - H10301, -} FlipFridProtos; - -typedef enum { - NoneScene, - SceneEntryPoint, - SceneSelectFile, - SceneSelectField, - SceneAttack, - SceneLoadCustomUids, -} FlipFridScene; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType evt_type; - InputKey key; - InputType input_type; -} FlipFridEvent; - -// STRUCTS -typedef struct { - FuriMutex* mutex; - bool is_running; - bool is_attacking; - FlipFridScene current_scene; - FlipFridScene previous_scene; - NotificationApp* notify; - u_int8_t menu_index; - u_int8_t menu_proto_index; - - FuriString* data_str; - uint8_t data[6]; - uint8_t payload[6]; - uint8_t attack_step; - FlipFridAttacks attack; - FlipFridProtos proto; - FuriString* attack_name; - FuriString* proto_name; - FuriString* main_menu_items[4]; - FuriString* main_menu_proto_items[4]; - - DialogsApp* dialogs; - FuriString* notification_msg; - uint8_t key_index; - LFRFIDWorker* worker; - ProtocolDict* dict; - ProtocolId protocol; - bool workr_rund; - bool attack_stop_called; - - uint8_t time_between_cards; - - // Used for custom dictionnary - Stream* uids_stream; -} FlipFridState; \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c deleted file mode 100644 index f4b39aa66b..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.c +++ /dev/null @@ -1,201 +0,0 @@ -#include "flipfrid_scene_entrypoint.h" - -void flipfrid_scene_entrypoint_menu_callback( - FlipFridState* context, - uint32_t index, - uint32_t proto_index) { - switch(index) { - case FlipFridAttackDefaultValues: - context->attack = FlipFridAttackDefaultValues; - context->current_scene = SceneAttack; - furi_string_set(context->attack_name, "Default Values"); - break; - case FlipFridAttackBfCustomerId: - context->attack = FlipFridAttackBfCustomerId; - context->current_scene = SceneAttack; - furi_string_set(context->attack_name, "Bad Customer ID"); - break; - case FlipFridAttackLoadFile: - context->attack = FlipFridAttackLoadFile; - context->current_scene = SceneSelectFile; - furi_string_set(context->attack_name, "Load File"); - break; - case FlipFridAttackLoadFileCustomUids: - context->attack = FlipFridAttackLoadFileCustomUids; - context->current_scene = SceneLoadCustomUids; - furi_string_set(context->attack_name, "Load Custom UIDs"); - break; - default: - break; - } - - switch(proto_index) { - case EM4100: - context->proto = EM4100; - furi_string_set(context->proto_name, "EM4100"); - break; - case HIDProx: - context->proto = HIDProx; - furi_string_set(context->proto_name, "HIDProx"); - break; - case PAC: - context->proto = PAC; - furi_string_set(context->proto_name, "PAC/Stanley"); - break; - case H10301: - context->proto = H10301; - furi_string_set(context->proto_name, "H10301"); - break; - default: - break; - } -} - -void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) { - // Clear the previous payload - context->payload[0] = 0x00; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - context->payload[5] = 0x00; - - context->menu_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_items[i] = furi_string_alloc(); - }*/ - - context->menu_proto_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_proto_items[i] = furi_string_alloc(); - }*/ -} - -void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - if(context->menu_index < FlipFridAttackLoadFileCustomUids) { - context->menu_index++; - } - break; - case InputKeyUp: - if(context->menu_index > FlipFridAttackDefaultValues) { - context->menu_index--; - } - break; - case InputKeyLeft: - if(context->menu_proto_index > EM4100) { - context->menu_proto_index--; - } else if(context->menu_proto_index == EM4100) { - context->menu_proto_index = H10301; - } - break; - case InputKeyRight: - if(context->menu_proto_index < H10301) { - context->menu_proto_index++; - } else if(context->menu_proto_index == H10301) { - context->menu_proto_index = EM4100; - } - break; - case InputKeyOk: - flipfrid_scene_entrypoint_menu_callback( - context, context->menu_index, context->menu_proto_index); - break; - case InputKeyBack: - context->is_running = false; - break; - default: - break; - } - } - } -} - -void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(context->main_menu_items != NULL) { - if(context->main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > FlipFridAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 24, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index])); - - if(context->menu_index < FlipFridAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 48, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index + 1])); - } - - if(context->menu_proto_index > EM4100) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 4, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_proto_items[context->menu_proto_index])); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - - if(context->menu_proto_index < H10301) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index + 1])); - } - } - } -} \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h b/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h deleted file mode 100644 index 29ca5bdfa2..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_entrypoint.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_entrypoint_on_enter(FlipFridState* context); -void flipfrid_scene_entrypoint_on_exit(FlipFridState* context); -void flipfrid_scene_entrypoint_on_tick(FlipFridState* context); -void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context); \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c deleted file mode 100644 index 32157556b8..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "flipfrid_scene_load_custom_uids.h" -#include "flipfrid_scene_run_attack.h" -#include "flipfrid_scene_entrypoint.h" - -#define LFRFID_UIDS_EXTENSION ".txt" -#define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer" - -bool flipfrid_load_uids(FlipFridState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - context->uids_stream = buffered_file_stream_alloc(storage); - result = - buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); - // Close if loading fails - if(!result) { - buffered_file_stream_close(context->uids_stream); - return false; - } - return result; -} - -bool flipfrid_load_custom_uids_from_file(FlipFridState* context) { - // Input events and views are managed by file_select - FuriString* uid_path; - uid_path = furi_string_alloc(); - furi_string_set(uid_path, RFIDFUZZER_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, LFRFID_UIDS_EXTENSION, &I_125_10px); - browser_options.base_path = RFIDFUZZER_APP_PATH_FOLDER; - browser_options.hide_ext = false; - - bool res = dialog_file_browser_show(context->dialogs, uid_path, uid_path, &browser_options); - - if(res) { - res = flipfrid_load_uids(context, furi_string_get_cstr(uid_path)); - } - - furi_string_free(uid_path); - - return res; -} - -void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context) { - if(flipfrid_load_custom_uids_from_file(context)) { - // Force context loading - flipfrid_scene_run_attack_on_enter(context); - context->current_scene = SceneAttack; - } else { - flipfrid_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context) { - UNUSED(context); - UNUSED(canvas); -} diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h b/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h deleted file mode 100644 index a8ed982b68..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_custom_uids.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context); -bool flipfrid_load_custom_uids_from_file(FlipFridState* context); \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_file.c b/applications/external/flipfrid/scene/flipfrid_scene_load_file.c deleted file mode 100644 index 5f3f1a31b2..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_file.c +++ /dev/null @@ -1,193 +0,0 @@ -#include "flipfrid_scene_load_file.h" -#include "flipfrid_scene_entrypoint.h" - -#define LFRFID_APP_EXTENSION ".rfid" -#define LFRFID_APP_PATH_FOLDER "/ext/lfrfid" - -bool flipfrid_load(FlipFridState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - FuriString* temp_str; - temp_str = furi_string_alloc(); - do { - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Error open file"); - break; - } - - // FileType - if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Filetype"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Filetypes"); - break; - } else { - FURI_LOG_I(TAG, "Filetype: %s", furi_string_get_cstr(temp_str)); - } - - // Key type - if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key type"); - break; - } else { - FURI_LOG_I(TAG, "Key type: %s", furi_string_get_cstr(temp_str)); - - if(context->proto == EM4100) { - if(strcmp(furi_string_get_cstr(temp_str), "EM4100") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == PAC) { - if(strcmp(furi_string_get_cstr(temp_str), "PAC/Stanley") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == H10301) { - if(strcmp(furi_string_get_cstr(temp_str), "H10301") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else { - if(strcmp(furi_string_get_cstr(temp_str), "HIDProx") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } - } - - // Data - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(context->proto == EM4100) { - if(furi_string_size(context->data_str) != 14) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == PAC) { - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == H10301) { - if(furi_string_size(context->data_str) != 8) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else { - if(furi_string_size(context->data_str) != 17) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } - - // String to uint8_t - for(uint8_t i = 0; i < 6; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - - result = true; - } while(0); - furi_string_free(temp_str); - flipper_format_free(fff_data_file); - if(result) { - FURI_LOG_I(TAG, "Loaded successfully"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Source loaded."); - } - return result; -} - -void flipfrid_scene_load_file_on_enter(FlipFridState* context) { - if(flipfrid_load_protocol_from_file(context)) { - context->current_scene = SceneSelectField; - } else { - flipfrid_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void flipfrid_scene_load_file_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_file_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context) { - UNUSED(context); - UNUSED(canvas); -} - -bool flipfrid_load_protocol_from_file(FlipFridState* context) { - FuriString* user_file_path; - user_file_path = furi_string_alloc(); - furi_string_set(user_file_path, LFRFID_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, LFRFID_APP_EXTENSION, &I_125_10px); - browser_options.base_path = LFRFID_APP_PATH_FOLDER; - - // Input events and views are managed by file_select - bool res = dialog_file_browser_show( - context->dialogs, user_file_path, user_file_path, &browser_options); - - if(res) { - res = flipfrid_load(context, furi_string_get_cstr(user_file_path)); - } - - furi_string_free(user_file_path); - - return res; -} \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_load_file.h b/applications/external/flipfrid/scene/flipfrid_scene_load_file.h deleted file mode 100644 index ca82daab4b..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_load_file.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_load_file_on_enter(FlipFridState* context); -void flipfrid_scene_load_file_on_exit(FlipFridState* context); -void flipfrid_scene_load_file_on_tick(FlipFridState* context); -void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context); -bool flipfrid_load_protocol_from_file(FlipFridState* context); \ No newline at end of file diff --git a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c deleted file mode 100644 index 5f40313ba4..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.c +++ /dev/null @@ -1,666 +0,0 @@ -#include "flipfrid_scene_run_attack.h" -#include - -uint8_t counter = 0; - -uint8_t id_list[17][5] = { - {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha - {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha - {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha - {0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -uint8_t id_list_hid[14][6] = { - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}, // Incremental UID - {0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12}, // Decremental UID - {0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -uint8_t id_list_pac[17][4] = { - {0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d}, // From arha - {0x34, 0x00, 0x29, 0x3d}, // From arha - {0x04, 0xdf, 0x00, 0x00}, // From arha - {0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -uint8_t id_list_h[14][3] = { - {0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56}, // Incremental UID - {0x56, 0x34, 0x12}, // Decremental UID - {0xCA, 0xCA, 0xCA}, // From arha -}; - -void flipfrid_scene_run_attack_on_enter(FlipFridState* context) { - context->time_between_cards = 10; - context->attack_step = 0; - context->attack_stop_called = false; - context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); - context->worker = lfrfid_worker_alloc(context->dict); - if(context->proto == HIDProx) { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "HIDProx"); - } else if(context->proto == PAC) { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "PAC/Stanley"); - } else if(context->proto == H10301) { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "H10301"); - } else { - context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100"); - } -} - -void flipfrid_scene_run_attack_on_exit(FlipFridState* context) { - if(context->workr_rund) { - lfrfid_worker_stop(context->worker); - lfrfid_worker_stop_thread(context->worker); - context->workr_rund = false; - } - lfrfid_worker_free(context->worker); - protocol_dict_free(context->dict); - notification_message(context->notify, &sequence_blink_stop); -} - -void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { - if(context->is_attacking) { - if(1 == counter) { - protocol_dict_set_data(context->dict, context->protocol, context->payload, 6); - lfrfid_worker_free(context->worker); - context->worker = lfrfid_worker_alloc(context->dict); - lfrfid_worker_start_thread(context->worker); - lfrfid_worker_emulate_start(context->worker, context->protocol); - context->workr_rund = true; - } else if(0 == counter) { - if(context->workr_rund) { - lfrfid_worker_stop(context->worker); - lfrfid_worker_stop_thread(context->worker); - context->workr_rund = false; - furi_delay_ms(200); - } - switch(context->attack) { - case FlipFridAttackDefaultValues: - if(context->proto == EM4100) { - context->payload[0] = id_list[context->attack_step][0]; - context->payload[1] = id_list[context->attack_step][1]; - context->payload[2] = id_list[context->attack_step][2]; - context->payload[3] = id_list[context->attack_step][3]; - context->payload[4] = id_list[context->attack_step][4]; - - if(context->attack_step == 16) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == PAC) { - context->payload[0] = id_list_pac[context->attack_step][0]; - context->payload[1] = id_list_pac[context->attack_step][1]; - context->payload[2] = id_list_pac[context->attack_step][2]; - context->payload[3] = id_list_pac[context->attack_step][3]; - - if(context->attack_step == 16) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == H10301) { - context->payload[0] = id_list_h[context->attack_step][0]; - context->payload[1] = id_list_h[context->attack_step][1]; - context->payload[2] = id_list_h[context->attack_step][2]; - - if(context->attack_step == 13) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = id_list_hid[context->attack_step][0]; - context->payload[1] = id_list_hid[context->attack_step][1]; - context->payload[2] = id_list_hid[context->attack_step][2]; - context->payload[3] = id_list_hid[context->attack_step][3]; - context->payload[4] = id_list_hid[context->attack_step][4]; - context->payload[5] = id_list_hid[context->attack_step][5]; - - if(context->attack_step == 13) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - - } else { - context->attack_step++; - } - break; - } - - case FlipFridAttackBfCustomerId: - if(context->proto == EM4100) { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == PAC) { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == H10301) { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = context->attack_step; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - context->payload[5] = 0x00; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } - - case FlipFridAttackLoadFile: - if(context->proto == EM4100) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - context->payload[4] = context->data[4]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else if(context->proto == PAC) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else if(context->proto == H10301) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - context->payload[4] = context->data[4]; - context->payload[5] = context->data[5]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } - - case FlipFridAttackLoadFileCustomUids: - if(context->proto == EM4100) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 11) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 11) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 5; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else if(context->proto == PAC) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 9) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 9) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 4; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else if(context->proto == H10301) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 7) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 7) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 3; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 13) break; - break; - } - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(end_of_list) break; - if(furi_string_size(context->data_str) != 13) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 6; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } - } - } - if(counter > context->time_between_cards) { - counter = 0; - } else { - counter++; - } - } -} - -void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - break; - case InputKeyUp: - break; - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 5) { - context->time_between_cards--; - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 70) { - context->time_between_cards++; - } - } - break; - case InputKeyOk: - counter = 0; - if(!context->is_attacking) { - notification_message(context->notify, &sequence_blink_start_blue); - context->is_attacking = true; - } else { - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } - break; - case InputKeyBack: - context->is_attacking = false; - counter = 0; - - notification_message(context->notify, &sequence_blink_stop); - if(context->attack_stop_called) { - context->attack_stop_called = false; - context->attack_step = 0; - if(context->attack == FlipFridAttackLoadFileCustomUids) { - furi_string_reset(context->data_str); - stream_rewind(context->uids_stream); - buffered_file_stream_close(context->uids_stream); - } - - furi_string_reset(context->notification_msg); - context->current_scene = SceneEntryPoint; - } - - context->attack_stop_called = true; - break; - default: - break; - } - } - if(event.input_type == InputTypeLong) { - switch(event.key) { - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 0) { - if((context->time_between_cards - 10) > 5) { - context->time_between_cards -= 10; - } - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 70) { - context->time_between_cards += 10; - } - } - break; - default: - break; - } - } - } -} - -void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(context->attack_name)); - - char uid[18]; - char speed[16]; - if(context->proto == HIDProx) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3], - context->payload[4], - context->payload[5]); - } else if(context->proto == PAC) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3]); - } else if(context->proto == H10301) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2]); - } else { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3], - context->payload[4]); - } - - canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, uid); - - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned( - canvas, 64, 26, AlignCenter, AlignTop, furi_string_get_cstr(context->proto_name)); - - snprintf(speed, sizeof(speed), "Time delay: %d", context->time_between_cards); - - //canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Speed:"); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, speed); - //char start_stop_msg[20]; - if(context->is_attacking) { - elements_button_center(canvas, "Stop"); - //snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop "); - } else { - elements_button_center(canvas, "Start"); - elements_button_left(canvas, "TD -"); - elements_button_right(canvas, "+ TD"); - } - //canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg); -} diff --git a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h b/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h deleted file mode 100644 index ae56d35e7e..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_run_attack.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_run_attack_on_enter(FlipFridState* context); -void flipfrid_scene_run_attack_on_exit(FlipFridState* context); -void flipfrid_scene_run_attack_on_tick(FlipFridState* context); -void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context); diff --git a/applications/external/flipfrid/scene/flipfrid_scene_select_field.c b/applications/external/flipfrid/scene/flipfrid_scene_select_field.c deleted file mode 100644 index ccb49e910a..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_select_field.c +++ /dev/null @@ -1,160 +0,0 @@ -#include "flipfrid_scene_select_field.h" - -void flipfrid_center_displayed_key(FlipFridState* context, uint8_t index) { - char key_cstr[18]; - uint8_t key_len = 18; - uint8_t str_index = (index * 3); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - int key_index = 0; - - if(context->proto == EM4100) { - key_len = 16; - } - if(context->proto == PAC) { - key_len = 13; - } - if(context->proto == H10301) { - key_len = 10; - } - - for(uint8_t i = 0; i < data_len; i++) { - if(context->data[i] < 9) { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "0%X ", context->data[i]); - } else { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "%X ", context->data[i]); - } - } - - char display_menu[17] = { - 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; - - if(index > 1) { - display_menu[0] = key_cstr[str_index - 6]; - display_menu[1] = key_cstr[str_index - 5]; - } else { - display_menu[0] = ' '; - display_menu[1] = ' '; - } - - if(index > 0) { - display_menu[3] = key_cstr[str_index - 3]; - display_menu[4] = key_cstr[str_index - 2]; - } else { - display_menu[3] = ' '; - display_menu[4] = ' '; - } - - display_menu[7] = key_cstr[str_index]; - display_menu[8] = key_cstr[str_index + 1]; - - if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { - display_menu[11] = key_cstr[str_index + 3]; - display_menu[12] = key_cstr[str_index + 4]; - } else { - display_menu[11] = ' '; - display_menu[12] = ' '; - } - - if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { - display_menu[14] = key_cstr[str_index + 6]; - display_menu[15] = key_cstr[str_index + 7]; - } else { - display_menu[14] = ' '; - display_menu[15] = ' '; - } - - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, display_menu); -} - -void flipfrid_scene_select_field_on_enter(FlipFridState* context) { - furi_string_reset(context->notification_msg); -} - -void flipfrid_scene_select_field_on_exit(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_select_field_on_tick(FlipFridState* context) { - UNUSED(context); -} - -void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - const char* key_cstr = furi_string_get_cstr(context->data_str); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - - // don't look, it's ugly but I'm a python dev so... - uint8_t nb_bytes = 0; - for(uint8_t i = 0; i < strlen(key_cstr); i++) { - if(' ' == key_cstr[i]) { - nb_bytes++; - } - } - - switch(event.key) { - case InputKeyDown: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] - 1); - } - } - break; - case InputKeyUp: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] + 1); - } - } - break; - case InputKeyLeft: - if(context->key_index > 0) { - context->key_index = context->key_index - 1; - } - break; - case InputKeyRight: - if(context->key_index < nb_bytes) { - context->key_index = context->key_index + 1; - } - break; - case InputKeyOk: - furi_string_reset(context->notification_msg); - context->current_scene = SceneAttack; - break; - case InputKeyBack: - context->key_index = 0; - furi_string_reset(context->notification_msg); - context->current_scene = SceneSelectFile; - break; - default: - break; - } - FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); - } - } -} - -void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 12, 5, AlignLeft, AlignTop, "Left and right: select byte"); - canvas_draw_str_aligned(canvas, 12, 15, AlignLeft, AlignTop, "Up and down: adjust byte"); - - char msg_index[18]; - canvas_set_font(canvas, FontPrimary); - snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); - - flipfrid_center_displayed_key(context, context->key_index); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignTop, furi_string_get_cstr(context->notification_msg)); -} diff --git a/applications/external/flipfrid/scene/flipfrid_scene_select_field.h b/applications/external/flipfrid/scene/flipfrid_scene_select_field.h deleted file mode 100644 index 5533e321cd..0000000000 --- a/applications/external/flipfrid/scene/flipfrid_scene_select_field.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../flipfrid.h" - -void flipfrid_scene_select_field_on_enter(FlipFridState* context); -void flipfrid_scene_select_field_on_exit(FlipFridState* context); -void flipfrid_scene_select_field_on_tick(FlipFridState* context); -void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context); -void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context); -void center_displayed_key(FlipFridState* context, uint8_t index); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/LICENSE.md b/applications/external/ibtn_fuzzer/LICENSE.md deleted file mode 100644 index ba3b844562..0000000000 --- a/applications/external/ibtn_fuzzer/LICENSE.md +++ /dev/null @@ -1,8 +0,0 @@ -/* - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * @xMasterX and @G4N4P4T1(made original version) wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. - * ---------------------------------------------------------------------------- - */ \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/application.fam b/applications/external/ibtn_fuzzer/application.fam deleted file mode 100644 index 87c02a9136..0000000000 --- a/applications/external/ibtn_fuzzer/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="ibtn_fuzzer", - name="iButton Fuzzer", - apptype=FlipperAppType.EXTERNAL, - entry_point="ibtnfuzzer_start", - requires=["gui", "storage", "dialogs", "input", "notification"], - stack_size=1 * 1024, - order=15, - fap_icon="ibutt_10px.png", - fap_category="Tools", - fap_icon_assets="images", -) diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.c b/applications/external/ibtn_fuzzer/ibtnfuzzer.c deleted file mode 100644 index 825a555607..0000000000 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.c +++ /dev/null @@ -1,280 +0,0 @@ -#include "ibtnfuzzer.h" - -#include "scene/ibtnfuzzer_scene_entrypoint.h" -#include "scene/ibtnfuzzer_scene_load_file.h" -#include "scene/ibtnfuzzer_scene_select_field.h" -#include "scene/ibtnfuzzer_scene_run_attack.h" -#include "scene/ibtnfuzzer_scene_load_custom_uids.h" - -#define IBTNFUZZER_APP_FOLDER "/ext/ibtnfuzzer" - -static void ibtnfuzzer_draw_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - iBtnFuzzerState* ibtnfuzzer_state = ctx; - furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever); - - // Draw correct Canvas - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_draw(canvas, ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_draw(canvas, ibtnfuzzer_state); - break; - } - - furi_mutex_release(ibtnfuzzer_state->mutex); -} - -void ibtnfuzzer_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - iBtnFuzzerEvent event = { - .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; - furi_message_queue_put(event_queue, &event, 25); -} - -static void ibtnfuzzer_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - iBtnFuzzerEvent event = { - .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; - furi_message_queue_put(event_queue, &event, 25); -} - -iBtnFuzzerState* ibtnfuzzer_alloc() { - iBtnFuzzerState* ibtnfuzzer = malloc(sizeof(iBtnFuzzerState)); - ibtnfuzzer->notification_msg = furi_string_alloc(); - ibtnfuzzer->attack_name = furi_string_alloc(); - ibtnfuzzer->proto_name = furi_string_alloc(); - ibtnfuzzer->data_str = furi_string_alloc(); - - ibtnfuzzer->main_menu_items[0] = furi_string_alloc_set("Default Values"); - ibtnfuzzer->main_menu_items[1] = furi_string_alloc_set("Load File"); - ibtnfuzzer->main_menu_items[2] = furi_string_alloc_set("Load UIDs from file"); - - ibtnfuzzer->main_menu_proto_items[0] = furi_string_alloc_set("DS1990"); - ibtnfuzzer->main_menu_proto_items[1] = furi_string_alloc_set("Metakom"); - ibtnfuzzer->main_menu_proto_items[2] = furi_string_alloc_set("Cyfral"); - - ibtnfuzzer->previous_scene = NoneScene; - ibtnfuzzer->current_scene = SceneEntryPoint; - ibtnfuzzer->is_running = true; - ibtnfuzzer->is_attacking = false; - ibtnfuzzer->key_index = 0; - ibtnfuzzer->menu_index = 0; - ibtnfuzzer->menu_proto_index = 0; - - ibtnfuzzer->attack = iBtnFuzzerAttackDefaultValues; - ibtnfuzzer->notify = furi_record_open(RECORD_NOTIFICATION); - - ibtnfuzzer->data[0] = 0x00; - ibtnfuzzer->data[1] = 0x00; - ibtnfuzzer->data[2] = 0x00; - ibtnfuzzer->data[3] = 0x00; - ibtnfuzzer->data[4] = 0x00; - ibtnfuzzer->data[5] = 0x00; - ibtnfuzzer->data[6] = 0x00; - ibtnfuzzer->data[7] = 0x00; - - ibtnfuzzer->payload[0] = 0x00; - ibtnfuzzer->payload[1] = 0x00; - ibtnfuzzer->payload[2] = 0x00; - ibtnfuzzer->payload[3] = 0x00; - ibtnfuzzer->payload[4] = 0x00; - ibtnfuzzer->payload[5] = 0x00; - ibtnfuzzer->payload[6] = 0x00; - ibtnfuzzer->payload[7] = 0x00; - - //Dialog - ibtnfuzzer->dialogs = furi_record_open(RECORD_DIALOGS); - - return ibtnfuzzer; -} - -void ibtnfuzzer_free(iBtnFuzzerState* ibtnfuzzer) { - //Dialog - furi_record_close(RECORD_DIALOGS); - notification_message(ibtnfuzzer->notify, &sequence_blink_stop); - - // Strings - furi_string_free(ibtnfuzzer->notification_msg); - furi_string_free(ibtnfuzzer->attack_name); - furi_string_free(ibtnfuzzer->proto_name); - furi_string_free(ibtnfuzzer->data_str); - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(ibtnfuzzer->main_menu_items[i]); - } - - for(uint32_t i = 0; i < 3; i++) { - furi_string_free(ibtnfuzzer->main_menu_proto_items[i]); - } - - // The rest - free(ibtnfuzzer); -} - -// ENTRYPOINT -int32_t ibtnfuzzer_start(void* p) { - UNUSED(p); - // Input - FURI_LOG_I(TAG, "Initializing input"); - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(iBtnFuzzerEvent)); - iBtnFuzzerState* ibtnfuzzer_state = ibtnfuzzer_alloc(); - - ibtnfuzzer_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!ibtnfuzzer_state->mutex) { - FURI_LOG_E(TAG, "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_NOTIFICATION); - ibtnfuzzer_free(ibtnfuzzer_state); - return 255; - } - - Storage* storage = furi_record_open(RECORD_STORAGE); - if(!storage_simply_mkdir(storage, IBTNFUZZER_APP_FOLDER)) { - FURI_LOG_E(TAG, "Could not create folder %s", IBTNFUZZER_APP_FOLDER); - } - furi_record_close(RECORD_STORAGE); - - // Configure view port - FURI_LOG_I(TAG, "Initializing viewport"); - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, ibtnfuzzer_draw_callback, ibtnfuzzer_state); - view_port_input_callback_set(view_port, ibtnfuzzer_input_callback, event_queue); - - // Configure timer - FURI_LOG_I(TAG, "Initializing timer"); - FuriTimer* timer = - furi_timer_alloc(ibtnfuzzer_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second - - // Register view port in GUI - FURI_LOG_I(TAG, "Initializing gui"); - Gui* gui = (Gui*)furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - // Init values - iBtnFuzzerEvent event; - while(ibtnfuzzer_state->is_running) { - // Get next event - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); - //furi_mutex_acquire(ibtnfuzzer_state->mutex, FuriWaitForever); - if(event_status == FuriStatusOk) { - if(event.evt_type == EventTypeKey) { - //Handle event key - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_event(event, ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_event(event, ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_event(event, ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_event(event, ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_event(event, ibtnfuzzer_state); - break; - } - - } else if(event.evt_type == EventTypeTick) { - //Handle event tick - if(ibtnfuzzer_state->current_scene != ibtnfuzzer_state->previous_scene) { - // Trigger Exit Scene - switch(ibtnfuzzer_state->previous_scene) { - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_exit(ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_exit(ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_exit(ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_exit(ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_exit(ibtnfuzzer_state); - break; - case NoneScene: - break; - } - - // Trigger Entry Scene - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_enter(ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_enter(ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_enter(ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_enter(ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_enter(ibtnfuzzer_state); - break; - } - ibtnfuzzer_state->previous_scene = ibtnfuzzer_state->current_scene; - } - - // Trigger Tick Scene - switch(ibtnfuzzer_state->current_scene) { - case NoneScene: - case SceneEntryPoint: - ibtnfuzzer_scene_entrypoint_on_tick(ibtnfuzzer_state); - break; - case SceneSelectFile: - ibtnfuzzer_scene_load_file_on_tick(ibtnfuzzer_state); - break; - case SceneSelectField: - ibtnfuzzer_scene_select_field_on_tick(ibtnfuzzer_state); - break; - case SceneAttack: - ibtnfuzzer_scene_run_attack_on_tick(ibtnfuzzer_state); - break; - case SceneLoadCustomUids: - ibtnfuzzer_scene_load_custom_uids_on_tick(ibtnfuzzer_state); - break; - } - view_port_update(view_port); - } - } - //furi_mutex_release(ibtnfuzzer_state->mutex); - } - - // Cleanup - furi_timer_stop(timer); - furi_timer_free(timer); - - FURI_LOG_I(TAG, "Cleaning up"); - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - furi_message_queue_free(event_queue); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_mutex_free(ibtnfuzzer_state->mutex); - ibtnfuzzer_free(ibtnfuzzer_state); - - return 0; -} \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/ibtnfuzzer.h b/applications/external/ibtn_fuzzer/ibtnfuzzer.h deleted file mode 100644 index 7a9e2b537a..0000000000 --- a/applications/external/ibtn_fuzzer/ibtnfuzzer.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#define TAG "iBtnFuzzer" - -typedef enum { - iBtnFuzzerAttackDefaultValues, - iBtnFuzzerAttackLoadFile, - iBtnFuzzerAttackLoadFileCustomUids, -} iBtnFuzzerAttacks; - -typedef enum { - DS1990, - Metakom, - Cyfral, -} iBtnFuzzerProtos; - -typedef enum { - NoneScene, - SceneEntryPoint, - SceneSelectFile, - SceneSelectField, - SceneAttack, - SceneLoadCustomUids, -} iBtnFuzzerScene; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType evt_type; - InputKey key; - InputType input_type; -} iBtnFuzzerEvent; - -// STRUCTS -typedef struct { - FuriMutex* mutex; - bool is_running; - bool is_attacking; - iBtnFuzzerScene current_scene; - iBtnFuzzerScene previous_scene; - NotificationApp* notify; - u_int8_t menu_index; - u_int8_t menu_proto_index; - - FuriString* data_str; - uint8_t data[8]; - uint8_t payload[8]; - uint8_t attack_step; - iBtnFuzzerAttacks attack; - iBtnFuzzerProtos proto; - FuriString* attack_name; - FuriString* proto_name; - FuriString* main_menu_items[3]; - FuriString* main_menu_proto_items[3]; - - DialogsApp* dialogs; - FuriString* notification_msg; - uint8_t key_index; - iButtonWorker* worker; - iButtonKey* key; - iButtonProtocolId keytype; - iButtonProtocols* protocols; - bool workr_rund; - bool enter_rerun; - bool attack_stop_called; - - uint8_t time_between_cards; - - // Used for custom dictionnary - Stream* uids_stream; -} iBtnFuzzerState; \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/images/ibutt_10px.png b/applications/external/ibtn_fuzzer/images/ibutt_10px.png deleted file mode 100644 index 2fdaf123a657c00c9c84632ca3c151674e451ae1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)bW_|craZ$KesPZ!4!j_b)kjvx52z42i= z^Wk##wtdVzTiGS{99;2>!TC2M!yZeXz}?LkD}l;YOI#yLQW8s2t&)pUffR$0fsvuE zfvK*cNr<75m9c@9v4ysQft7*5`ikN&C>nC}Q!>*kp&E>VdO{3LtqcsU49p-Jly36? Qy~)7f>FVdQ&MBb@0C$~I0{{R3 diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c deleted file mode 100644 index 1dd239c3bb..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.c +++ /dev/null @@ -1,201 +0,0 @@ -#include "ibtnfuzzer_scene_entrypoint.h" - -void ibtnfuzzer_scene_entrypoint_menu_callback( - iBtnFuzzerState* context, - uint32_t index, - uint32_t proto_index) { - switch(index) { - case iBtnFuzzerAttackDefaultValues: - context->attack = iBtnFuzzerAttackDefaultValues; - context->current_scene = SceneAttack; - furi_string_set(context->attack_name, "Default Values"); - break; - case iBtnFuzzerAttackLoadFile: - context->attack = iBtnFuzzerAttackLoadFile; - context->current_scene = SceneSelectFile; - furi_string_set(context->attack_name, "Load File"); - break; - case iBtnFuzzerAttackLoadFileCustomUids: - context->attack = iBtnFuzzerAttackLoadFileCustomUids; - context->current_scene = SceneLoadCustomUids; - furi_string_set(context->attack_name, "Load Custom UIDs"); - break; - default: - break; - } - - switch(proto_index) { - case DS1990: - context->proto = DS1990; - furi_string_set(context->proto_name, "DS1990"); - break; - case Metakom: - context->proto = Metakom; - furi_string_set(context->proto_name, "Metakom"); - break; - case Cyfral: - context->proto = Cyfral; - furi_string_set(context->proto_name, "Cyfral"); - break; - default: - break; - } -} - -void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context) { - // Clear the previous payload - context->payload[0] = 0x00; - context->payload[1] = 0x00; - context->payload[2] = 0x00; - context->payload[3] = 0x00; - context->payload[4] = 0x00; - context->payload[5] = 0x00; - context->payload[6] = 0x00; - context->payload[7] = 0x00; - - context->menu_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_items[i] = furi_string_alloc(); - }*/ - - context->menu_proto_index = 0; - /*for(uint32_t i = 0; i < 4; i++) { - menu_proto_items[i] = furi_string_alloc(); - }*/ -} - -void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context) { - context->enter_rerun = false; -} - -void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_entrypoint_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { - context->menu_index++; - } - break; - case InputKeyUp: - if(context->menu_index > iBtnFuzzerAttackDefaultValues) { - context->menu_index--; - } - break; - case InputKeyLeft: - if(context->menu_proto_index > DS1990) { - context->menu_proto_index--; - } else if(context->menu_proto_index == DS1990) { - context->menu_proto_index = Cyfral; - } - break; - case InputKeyRight: - if(context->menu_proto_index < Cyfral) { - context->menu_proto_index++; - } else if(context->menu_proto_index == Cyfral) { - context->menu_proto_index = DS1990; - } - break; - case InputKeyOk: - ibtnfuzzer_scene_entrypoint_menu_callback( - context, context->menu_index, context->menu_proto_index); - break; - case InputKeyBack: - context->is_running = false; - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - if(!context->enter_rerun) { - ibtnfuzzer_scene_entrypoint_on_enter(context); - context->enter_rerun = true; - } - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(context->main_menu_items != NULL) { - if(context->main_menu_items[context->menu_index] != NULL) { - if(context->menu_index > iBtnFuzzerAttackDefaultValues) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 24, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, - 64, - 36, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index])); - - if(context->menu_index < iBtnFuzzerAttackLoadFileCustomUids) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - 48, - AlignCenter, - AlignTop, - furi_string_get_cstr(context->main_menu_items[context->menu_index + 1])); - } - - if(context->menu_proto_index > DS1990) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index - 1])); - } - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 27, 4, AlignCenter, AlignTop, "<"); - - canvas_set_font(canvas, FontPrimary); - if(context->main_menu_proto_items[context->menu_proto_index] != NULL) { - canvas_draw_str_aligned( - canvas, - 64, - 4, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index])); - } - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 101, 4, AlignCenter, AlignTop, ">"); - - if(context->menu_proto_index < Cyfral) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - 64, - -12, - AlignCenter, - AlignTop, - furi_string_get_cstr( - context->main_menu_proto_items[context->menu_proto_index + 1])); - } - } - } -} \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h deleted file mode 100644 index b77aec369f..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_entrypoint.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_entrypoint_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_entrypoint_on_draw(Canvas* canvas, iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c deleted file mode 100644 index 07199ab463..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "ibtnfuzzer_scene_load_custom_uids.h" -#include "ibtnfuzzer_scene_run_attack.h" -#include "ibtnfuzzer_scene_entrypoint.h" - -#define IBTNFUZZER_UIDS_EXTENSION ".txt" -#define IBTNFUZZER_APP_PATH_FOLDER "/ext/ibtnfuzzer" - -bool ibtnfuzzer_load_uids(iBtnFuzzerState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - context->uids_stream = buffered_file_stream_alloc(storage); - result = - buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); - // Close if loading fails - if(!result) { - buffered_file_stream_close(context->uids_stream); - return false; - } - return result; -} - -bool ibtnfuzzer_load_custom_uids_from_file(iBtnFuzzerState* context) { - // Input events and views are managed by file_select - FuriString* uid_path; - uid_path = furi_string_alloc(); - furi_string_set(uid_path, IBTNFUZZER_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, IBTNFUZZER_UIDS_EXTENSION, &I_ibutt_10px); - browser_options.base_path = IBTNFUZZER_APP_PATH_FOLDER; - browser_options.hide_ext = false; - - bool res = dialog_file_browser_show(context->dialogs, uid_path, uid_path, &browser_options); - - if(res) { - res = ibtnfuzzer_load_uids(context, furi_string_get_cstr(uid_path)); - } - - furi_string_free(uid_path); - - return res; -} - -void ibtnfuzzer_scene_load_custom_uids_on_enter(iBtnFuzzerState* context) { - if(ibtnfuzzer_load_custom_uids_from_file(context)) { - // Force context loading - ibtnfuzzer_scene_run_attack_on_enter(context); - context->current_scene = SceneAttack; - } else { - ibtnfuzzer_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void ibtnfuzzer_scene_load_custom_uids_on_exit(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_custom_uids_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_custom_uids_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_load_custom_uids_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - UNUSED(context); - UNUSED(canvas); -} diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h deleted file mode 100644 index bb51c70794..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_custom_uids.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_load_custom_uids_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_custom_uids_on_draw(Canvas* canvas, iBtnFuzzerState* context); -bool ibtnfuzzer_load_custom_uids_from_file(iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c deleted file mode 100644 index 92f79a424c..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.c +++ /dev/null @@ -1,293 +0,0 @@ -#include "ibtnfuzzer_scene_load_file.h" -#include "ibtnfuzzer_scene_entrypoint.h" - -#define IBUTTON_FUZZER_APP_EXTENSION ".ibtn" -#define IBUTTON_FUZZER_APP_PATH_FOLDER "/ext/ibutton" - -bool ibtnfuzzer_load(iBtnFuzzerState* context, const char* file_path) { - bool result = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - FuriString* temp_str; - temp_str = furi_string_alloc(); - bool key_v2 = false; - do { - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_E(TAG, "Error open file %s", file_path); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Error open file"); - break; - } - - // FileType - if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Filetype"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Filetypes"); - break; - } else { - FURI_LOG_I(TAG, "Filetype: %s", furi_string_get_cstr(temp_str)); - } - - // Key type - if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Key type, checking for typ2.."); - - if(!flipper_format_rewind(fff_data_file)) { - FURI_LOG_E(TAG, "Failed to rewind file"); - break; - } - if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) { - furi_string_reset(context->notification_msg); - furi_string_set( - context->notification_msg, "Missing or incorrect Protocol or Key type"); - break; - } - FURI_LOG_I(TAG, "Key type V2: %s", furi_string_get_cstr(temp_str)); - key_v2 = true; - - if(context->proto == DS1990) { - if(strcmp(furi_string_get_cstr(temp_str), "DS1990") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == Cyfral) { - if(strcmp(furi_string_get_cstr(temp_str), "Cyfral") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else { - if(strcmp(furi_string_get_cstr(temp_str), "Metakom") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } - } else { - FURI_LOG_I(TAG, "Key type: %s", furi_string_get_cstr(temp_str)); - - if(context->proto == DS1990) { - if(strcmp(furi_string_get_cstr(temp_str), "Dallas") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else if(context->proto == Cyfral) { - if(strcmp(furi_string_get_cstr(temp_str), "Cyfral") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } else { - if(strcmp(furi_string_get_cstr(temp_str), "Metakom") != 0) { - FURI_LOG_E(TAG, "Unsupported Key type"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Unsupported Key type"); - break; - } - } - } - if(!key_v2) { - // Data - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Key"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(context->proto == DS1990) { - if(furi_string_size(context->data_str) != 23) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else if(context->proto == Cyfral) { - if(furi_string_size(context->data_str) != 5) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } else { - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - } - - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - } else { - // Data - if(context->proto == DS1990) { - if(!flipper_format_read_string(fff_data_file, "Rom Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Rom Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Rom Data"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(furi_string_size(context->data_str) != 23) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - } else if(context->proto == Cyfral) { - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Data"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(furi_string_size(context->data_str) != 5) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - } else { - if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { - FURI_LOG_E(TAG, "Missing or incorrect Data"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Missing or incorrect Data"); - break; - } else { - FURI_LOG_I(TAG, "Key: %s", furi_string_get_cstr(context->data_str)); - - if(furi_string_size(context->data_str) != 11) { - FURI_LOG_E(TAG, "Incorrect Key length"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Incorrect Key length"); - break; - } - - // String to uint8_t - for(uint8_t i = 0; i < 8; i++) { - char temp_str2[3]; - temp_str2[0] = furi_string_get_cstr(context->data_str)[i * 3]; - temp_str2[1] = furi_string_get_cstr(context->data_str)[i * 3 + 1]; - temp_str2[2] = '\0'; - context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); - } - } - } - } - - result = true; - } while(0); - furi_string_free(temp_str); - flipper_format_free(fff_data_file); - if(result) { - FURI_LOG_I(TAG, "Loaded successfully"); - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, "Source loaded."); - } - return result; -} - -void ibtnfuzzer_scene_load_file_on_enter(iBtnFuzzerState* context) { - if(ibtnfuzzer_load_protocol_from_file(context)) { - context->current_scene = SceneSelectField; - } else { - ibtnfuzzer_scene_entrypoint_on_enter(context); - context->current_scene = SceneEntryPoint; - } -} - -void ibtnfuzzer_scene_load_file_on_exit(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_file_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_load_file_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - case InputKeyUp: - case InputKeyLeft: - case InputKeyRight: - case InputKeyOk: - case InputKeyBack: - context->current_scene = SceneEntryPoint; - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_load_file_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - UNUSED(context); - UNUSED(canvas); -} - -bool ibtnfuzzer_load_protocol_from_file(iBtnFuzzerState* context) { - FuriString* user_file_path; - user_file_path = furi_string_alloc(); - furi_string_set(user_file_path, IBUTTON_FUZZER_APP_PATH_FOLDER); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, IBUTTON_FUZZER_APP_EXTENSION, &I_ibutt_10px); - browser_options.base_path = IBUTTON_FUZZER_APP_PATH_FOLDER; - - // Input events and views are managed by file_select - bool res = dialog_file_browser_show( - context->dialogs, user_file_path, user_file_path, &browser_options); - - if(res) { - res = ibtnfuzzer_load(context, furi_string_get_cstr(user_file_path)); - } - - furi_string_free(user_file_path); - - return res; -} \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h deleted file mode 100644 index 34031d08c5..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_load_file.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_load_file_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_load_file_on_draw(Canvas* canvas, iBtnFuzzerState* context); -bool ibtnfuzzer_load_protocol_from_file(iBtnFuzzerState* context); \ No newline at end of file diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c deleted file mode 100644 index 13ec6e6be5..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.c +++ /dev/null @@ -1,501 +0,0 @@ -#include "ibtnfuzzer_scene_run_attack.h" -#include - -uint8_t counter = 0; - -uint8_t id_list_ds1990[18][8] = { - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x36, 0x00, 0xE1}, //– код универсального ключа, для Vizit - {0x01, 0xBE, 0x40, 0x11, 0x5A, 0x56, 0x00, 0xBB}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x00, 0x00, 0x00, 0x77}, //- проверен работает - {0x01, 0xBE, 0x40, 0x11, 0x0A, 0x00, 0x00, 0x1D}, //- проверен работает Визит иногда КЕЙМАНЫ - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2F}, //- проверен(метаком, цифрал, ВИЗИТ). - {0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x9B}, //- проверен Визит, Метакомы, КОНДОР - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, //???-Открываает 98% Метаком и некоторые Цифрал - {0x01, 0x00, 0x00, 0x00, 0x00, 0x90, 0x19, 0xFF}, //???-Отлично работает на старых домофонах - {0x01, 0x6F, 0x2E, 0x88, 0x8A, 0x00, 0x00, 0x4D}, //???-Открывать что-то должен - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x7E, 0x88}, //???-Cyfral, Metakom - {0x01, 0x53, 0xD4, 0xFE, 0x00, 0x00, 0x00, 0x6F}, //???-домофоны Визит (Vizit) - до 99% - {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D}, //???-домофоны Cyfral CCD-20 - до 70% - {0x01, 0x00, 0xBE, 0x11, 0xAA, 0x00, 0x00, 0xFB}, //???-домофоны Кейман (KEYMAN) - {0x01, 0x76, 0xB8, 0x2E, 0x0F, 0x00, 0x00, 0x5C}, //???-домофоны Форвард - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14}, // Only FF - {0x01, 0x78, 0x00, 0x48, 0xFD, 0xFF, 0xFF, 0xD1}, // StarNew Uni5 - {0x01, 0xA9, 0xE4, 0x3C, 0x09, 0x00, 0x00, 0xE6}, // Eltis Uni -}; - -uint8_t id_list_metakom[17][4] = { - {0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78}, // Incremental UID - {0x9A, 0x78, 0x56, 0x34}, // Decremental UID - {0x04, 0xd0, 0x9b, 0x0d}, // ?? - {0x34, 0x00, 0x29, 0x3d}, // ?? - {0x04, 0xdf, 0x00, 0x00}, // ?? - {0xCA, 0xCA, 0xCA, 0xCA}, // ?? -}; - -uint8_t id_list_cyfral[16][2] = { - {0x00, 0x00}, // Null bytes - {0xFF, 0xFF}, // Only FF - {0x11, 0x11}, // Only 11 - {0x22, 0x22}, // Only 22 - {0x33, 0x33}, // Only 33 - {0x44, 0x44}, // Only 44 - {0x55, 0x55}, // Only 55 - {0x66, 0x66}, // Only 66 - {0x77, 0x77}, // Only 77 - {0x88, 0x88}, // Only 88 - {0x99, 0x99}, // Only 99 - {0x12, 0x34}, // Incremental UID - {0x56, 0x34}, // Decremental UID - {0xCA, 0xCA}, // ?? - {0x8E, 0xC9}, // Elevator code - {0x6A, 0x50}, // VERY fresh code from smartkey -}; - -void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context) { - context->time_between_cards = 8; - context->attack_step = 0; - context->attack_stop_called = false; - context->protocols = ibutton_protocols_alloc(); - context->key = ibutton_key_alloc(ibutton_protocols_get_max_data_size(context->protocols)); - context->worker = ibutton_worker_alloc(context->protocols); - if(context->proto == Metakom) { - context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Metakom"); - } else if(context->proto == Cyfral) { - context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "Cyfral"); - } else { - context->keytype = ibutton_protocols_get_id_by_name(context->protocols, "DS1990"); - } - context->workr_rund = false; -} - -void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context) { - if(context->workr_rund) { - ibutton_worker_stop(context->worker); - ibutton_worker_stop_thread(context->worker); - context->workr_rund = false; - } - ibutton_key_free(context->key); - ibutton_worker_free(context->worker); - ibutton_protocols_free(context->protocols); - notification_message(context->notify, &sequence_blink_stop); -} - -void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context) { - if(context->is_attacking) { - if(1 == counter) { - ibutton_worker_start_thread(context->worker); - ibutton_key_set_protocol_id(context->key, context->keytype); - iButtonEditableData data; - ibutton_protocols_get_editable_data(context->protocols, context->key, &data); - data.size = sizeof(context->payload); - for(size_t i = 0; i < data.size; i++) { - data.ptr[i] = context->payload[i]; - } - - ibutton_worker_emulate_start(context->worker, context->key); - context->workr_rund = true; - } else if(0 == counter) { - if(context->workr_rund) { - ibutton_worker_stop(context->worker); - ibutton_worker_stop_thread(context->worker); - context->workr_rund = false; - furi_delay_ms(500); - } - switch(context->attack) { - case iBtnFuzzerAttackDefaultValues: - if(context->proto == DS1990) { - context->payload[0] = id_list_ds1990[context->attack_step][0]; - context->payload[1] = id_list_ds1990[context->attack_step][1]; - context->payload[2] = id_list_ds1990[context->attack_step][2]; - context->payload[3] = id_list_ds1990[context->attack_step][3]; - context->payload[4] = id_list_ds1990[context->attack_step][4]; - context->payload[5] = id_list_ds1990[context->attack_step][5]; - context->payload[6] = id_list_ds1990[context->attack_step][6]; - context->payload[7] = id_list_ds1990[context->attack_step][7]; - - if(context->attack_step == 17) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else if(context->proto == Metakom) { - context->payload[0] = id_list_metakom[context->attack_step][0]; - context->payload[1] = id_list_metakom[context->attack_step][1]; - context->payload[2] = id_list_metakom[context->attack_step][2]; - context->payload[3] = id_list_metakom[context->attack_step][3]; - - if(context->attack_step == 16) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = id_list_cyfral[context->attack_step][0]; - context->payload[1] = id_list_cyfral[context->attack_step][1]; - - if(context->attack_step == 15) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } else { - context->attack_step++; - } - break; - } - case iBtnFuzzerAttackLoadFile: - if(context->proto == DS1990) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - context->payload[4] = context->data[4]; - context->payload[5] = context->data[5]; - context->payload[6] = context->data[6]; - context->payload[7] = context->data[7]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else if(context->proto == Cyfral) { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } else { - context->payload[0] = context->data[0]; - context->payload[1] = context->data[1]; - context->payload[2] = context->data[2]; - context->payload[3] = context->data[3]; - - context->payload[context->key_index] = context->attack_step; - - if(context->attack_step == 255) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - break; - } else { - context->attack_step++; - } - break; - } - - case iBtnFuzzerAttackLoadFileCustomUids: - if(context->proto == DS1990) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 17) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 17) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 8; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else if(context->proto == Cyfral) { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 5) break; - break; - } - if(end_of_list) break; - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(furi_string_size(context->data_str) != 5) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 2; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } else { - bool end_of_list = false; - while(true) { - furi_string_reset(context->data_str); - if(!stream_read_line(context->uids_stream, context->data_str)) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - stream_rewind(context->uids_stream); - end_of_list = true; - break; - }; - if(furi_string_get_char(context->data_str, 0) == '#') continue; - if(furi_string_size(context->data_str) != 9) break; - break; - } - FURI_LOG_D(TAG, furi_string_get_cstr(context->data_str)); - if(end_of_list) break; - if(furi_string_size(context->data_str) != 9) { - context->attack_step = 0; - counter = 0; - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_error); - break; - }; - - // string is valid, parse it in context->payload - for(uint8_t i = 0; i < 4; i++) { - char temp_str[3]; - temp_str[0] = furi_string_get_cstr(context->data_str)[i * 2]; - temp_str[1] = furi_string_get_cstr(context->data_str)[i * 2 + 1]; - temp_str[2] = '\0'; - context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); - } - break; - } - } - } - - if(counter > context->time_between_cards) { - counter = 0; - } else { - counter++; - } - } -} - -void ibtnfuzzer_scene_run_attack_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - switch(event.key) { - case InputKeyDown: - break; - case InputKeyUp: - break; - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 4) { - context->time_between_cards--; - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 80) { - context->time_between_cards++; - } - } - break; - case InputKeyOk: - counter = 0; - if(!context->is_attacking) { - notification_message(context->notify, &sequence_blink_start_blue); - context->is_attacking = true; - } else { - context->is_attacking = false; - notification_message(context->notify, &sequence_blink_stop); - notification_message(context->notify, &sequence_single_vibro); - } - break; - case InputKeyBack: - context->is_attacking = false; - counter = 0; - - notification_message(context->notify, &sequence_blink_stop); - if(context->attack_stop_called) { - context->attack_stop_called = false; - context->attack_step = 0; - if(context->attack == iBtnFuzzerAttackLoadFileCustomUids) { - furi_string_reset(context->data_str); - stream_rewind(context->uids_stream); - buffered_file_stream_close(context->uids_stream); - } - - furi_string_reset(context->notification_msg); - context->current_scene = SceneEntryPoint; - } - - context->attack_stop_called = true; - break; - default: - break; - } - } - if(event.input_type == InputTypeLong) { - switch(event.key) { - case InputKeyLeft: - if(!context->is_attacking) { - if(context->time_between_cards > 4) { - if((context->time_between_cards - 10) > 4) { - context->time_between_cards -= 10; - } - } - } - break; - case InputKeyRight: - if(!context->is_attacking) { - if(context->time_between_cards < 80) { - context->time_between_cards += 10; - } - } - break; - default: - break; - } - } - } -} - -void ibtnfuzzer_scene_run_attack_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned( - canvas, 64, 2, AlignCenter, AlignTop, furi_string_get_cstr(context->attack_name)); - - char uid[25]; - char speed[16]; - if(context->proto == Metakom) { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3]); - } else if(context->proto == Cyfral) { - snprintf(uid, sizeof(uid), "%02X:%02X", context->payload[0], context->payload[1]); - } else { - snprintf( - uid, - sizeof(uid), - "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", - context->payload[0], - context->payload[1], - context->payload[2], - context->payload[3], - context->payload[4], - context->payload[5], - context->payload[6], - context->payload[7]); - } - - canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignTop, uid); - - canvas_set_font(canvas, FontSecondary); - - canvas_draw_str_aligned( - canvas, 64, 26, AlignCenter, AlignTop, furi_string_get_cstr(context->proto_name)); - - snprintf(speed, sizeof(speed), "Time delay: %d", context->time_between_cards); - - //canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Speed:"); - canvas_draw_str_aligned(canvas, 64, 14, AlignCenter, AlignTop, speed); - //char start_stop_msg[20]; - if(context->is_attacking) { - elements_button_center(canvas, "Stop"); - //snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop "); - } else { - elements_button_center(canvas, "Start"); - elements_button_left(canvas, "TD -"); - elements_button_right(canvas, "+ TD"); - } - //canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg); -} diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h deleted file mode 100644 index 9e44478f78..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_run_attack.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_run_attack_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_run_attack_on_draw(Canvas* canvas, iBtnFuzzerState* context); diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c deleted file mode 100644 index f3217f65ee..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.c +++ /dev/null @@ -1,160 +0,0 @@ -#include "ibtnfuzzer_scene_select_field.h" - -void ibtnfuzzer_center_displayed_key(iBtnFuzzerState* context, uint8_t index) { - char key_cstr[25]; - uint8_t key_len = 25; - uint8_t str_index = (index * 3); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - int key_index = 0; - - if(context->proto == DS1990) { - key_len = 25; - } - if(context->proto == Metakom) { - key_len = 13; - } - if(context->proto == Cyfral) { - key_len = 7; - } - - for(uint8_t i = 0; i < data_len; i++) { - if(context->data[i] < 9) { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "0%X ", context->data[i]); - } else { - key_index += - snprintf(&key_cstr[key_index], key_len - key_index, "%X ", context->data[i]); - } - } - - char display_menu[17] = { - 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; - - if(index > 1) { - display_menu[0] = key_cstr[str_index - 6]; - display_menu[1] = key_cstr[str_index - 5]; - } else { - display_menu[0] = ' '; - display_menu[1] = ' '; - } - - if(index > 0) { - display_menu[3] = key_cstr[str_index - 3]; - display_menu[4] = key_cstr[str_index - 2]; - } else { - display_menu[3] = ' '; - display_menu[4] = ' '; - } - - display_menu[7] = key_cstr[str_index]; - display_menu[8] = key_cstr[str_index + 1]; - - if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { - display_menu[11] = key_cstr[str_index + 3]; - display_menu[12] = key_cstr[str_index + 4]; - } else { - display_menu[11] = ' '; - display_menu[12] = ' '; - } - - if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { - display_menu[14] = key_cstr[str_index + 6]; - display_menu[15] = key_cstr[str_index + 7]; - } else { - display_menu[14] = ' '; - display_menu[15] = ' '; - } - - furi_string_reset(context->notification_msg); - furi_string_set(context->notification_msg, display_menu); -} - -void ibtnfuzzer_scene_select_field_on_enter(iBtnFuzzerState* context) { - furi_string_reset(context->notification_msg); -} - -void ibtnfuzzer_scene_select_field_on_exit(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_select_field_on_tick(iBtnFuzzerState* context) { - UNUSED(context); -} - -void ibtnfuzzer_scene_select_field_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context) { - if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - const char* key_cstr = furi_string_get_cstr(context->data_str); - int data_len = sizeof(context->data) / sizeof(context->data[0]); - - // don't look, it's ugly but I'm a python dev so... - uint8_t nb_bytes = 0; - for(uint8_t i = 0; i < strlen(key_cstr); i++) { - if(' ' == key_cstr[i]) { - nb_bytes++; - } - } - - switch(event.key) { - case InputKeyDown: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] - 1); - } - } - break; - case InputKeyUp: - for(uint8_t i = 0; i < data_len; i++) { - if(context->key_index == i) { - context->data[i] = (context->data[i] + 1); - } - } - break; - case InputKeyLeft: - if(context->key_index > 0) { - context->key_index = context->key_index - 1; - } - break; - case InputKeyRight: - if(context->key_index < nb_bytes) { - context->key_index = context->key_index + 1; - } - break; - case InputKeyOk: - furi_string_reset(context->notification_msg); - context->current_scene = SceneAttack; - break; - case InputKeyBack: - context->key_index = 0; - furi_string_reset(context->notification_msg); - context->current_scene = SceneSelectFile; - break; - default: - break; - } - FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); - } - } -} - -void ibtnfuzzer_scene_select_field_on_draw(Canvas* canvas, iBtnFuzzerState* context) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - //canvas_draw_frame(canvas, 0, 0, 128, 64); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 12, 5, AlignLeft, AlignTop, "Left and right: select byte"); - canvas_draw_str_aligned(canvas, 12, 15, AlignLeft, AlignTop, "Up and down: adjust byte"); - - char msg_index[18]; - canvas_set_font(canvas, FontPrimary); - snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); - canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignTop, msg_index); - - ibtnfuzzer_center_displayed_key(context, context->key_index); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignTop, furi_string_get_cstr(context->notification_msg)); -} diff --git a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h b/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h deleted file mode 100644 index b6c684c3b0..0000000000 --- a/applications/external/ibtn_fuzzer/scene/ibtnfuzzer_scene_select_field.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../ibtnfuzzer.h" - -void ibtnfuzzer_scene_select_field_on_enter(iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_exit(iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_tick(iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_event(iBtnFuzzerEvent event, iBtnFuzzerState* context); -void ibtnfuzzer_scene_select_field_on_draw(Canvas* canvas, iBtnFuzzerState* context); -void center_displayed_key(iBtnFuzzerState* context, uint8_t index); \ No newline at end of file diff --git a/applications/external/pacs_fuzzer/application.fam b/applications/external/multi_fuzzer/application.fam similarity index 81% rename from applications/external/pacs_fuzzer/application.fam rename to applications/external/multi_fuzzer/application.fam index 6098322ac0..a0ce1be0aa 100644 --- a/applications/external/pacs_fuzzer/application.fam +++ b/applications/external/multi_fuzzer/application.fam @@ -1,6 +1,6 @@ App( - appid="pacs_fuzzer_ibtn", - name="iButton Fuzzer [B]", + appid="fuzzer_ibtn", + name="iButton Fuzzer", apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_ibtn", requires=[ @@ -11,8 +11,8 @@ App( "notification", ], stack_size=2 * 1024, - fap_icon="icons/rfid_10px.png", - fap_category="Debug", + fap_icon="icons/ibutt_10px.png", + fap_category="Tools", fap_private_libs=[ Lib( name="worker", @@ -24,8 +24,8 @@ App( ) App( - appid="pacs_fuzzer_rfid", - name="RFID Fuzzer [B]", + appid="fuzzer_rfid", + name="RFID Fuzzer", apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_rfid", requires=[ @@ -36,8 +36,8 @@ App( "notification", ], stack_size=2 * 1024, - fap_icon="icons/125_10px.png", - fap_category="Debug", + fap_icon="icons/rfid_10px.png", + fap_category="Tools", fap_private_libs=[ Lib( name="worker", diff --git a/applications/external/pacs_fuzzer/fuzzer.c b/applications/external/multi_fuzzer/fuzzer.c similarity index 100% rename from applications/external/pacs_fuzzer/fuzzer.c rename to applications/external/multi_fuzzer/fuzzer.c diff --git a/applications/external/pacs_fuzzer/fuzzer_i.h b/applications/external/multi_fuzzer/fuzzer_i.h similarity index 100% rename from applications/external/pacs_fuzzer/fuzzer_i.h rename to applications/external/multi_fuzzer/fuzzer_i.h diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h b/applications/external/multi_fuzzer/helpers/fuzzer_custom_event.h similarity index 100% rename from applications/external/pacs_fuzzer/helpers/fuzzer_custom_event.h rename to applications/external/multi_fuzzer/helpers/fuzzer_custom_event.h diff --git a/applications/external/pacs_fuzzer/helpers/fuzzer_types.h b/applications/external/multi_fuzzer/helpers/fuzzer_types.h similarity index 100% rename from applications/external/pacs_fuzzer/helpers/fuzzer_types.h rename to applications/external/multi_fuzzer/helpers/fuzzer_types.h diff --git a/applications/external/flipfrid/images/125_10px.png b/applications/external/multi_fuzzer/icons/125_10px.png similarity index 100% rename from applications/external/flipfrid/images/125_10px.png rename to applications/external/multi_fuzzer/icons/125_10px.png diff --git a/applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png b/applications/external/multi_fuzzer/icons/ButtonLeft_4x7.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/ButtonLeft_4x7.png rename to applications/external/multi_fuzzer/icons/ButtonLeft_4x7.png diff --git a/applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png b/applications/external/multi_fuzzer/icons/ButtonRight_4x7.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/ButtonRight_4x7.png rename to applications/external/multi_fuzzer/icons/ButtonRight_4x7.png diff --git a/applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png b/applications/external/multi_fuzzer/icons/Ok_btn_9x9.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/Ok_btn_9x9.png rename to applications/external/multi_fuzzer/icons/Ok_btn_9x9.png diff --git a/applications/external/pacs_fuzzer/icons/Pin_arrow_up_7x9.png b/applications/external/multi_fuzzer/icons/Pin_arrow_up_7x9.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/Pin_arrow_up_7x9.png rename to applications/external/multi_fuzzer/icons/Pin_arrow_up_7x9.png diff --git a/applications/external/pacs_fuzzer/icons/Pin_back_arrow_10x8.png b/applications/external/multi_fuzzer/icons/Pin_back_arrow_10x8.png similarity index 100% rename from applications/external/pacs_fuzzer/icons/Pin_back_arrow_10x8.png rename to applications/external/multi_fuzzer/icons/Pin_back_arrow_10x8.png diff --git a/applications/external/ibtn_fuzzer/ibutt_10px.png b/applications/external/multi_fuzzer/icons/ibutt_10px.png similarity index 100% rename from applications/external/ibtn_fuzzer/ibutt_10px.png rename to applications/external/multi_fuzzer/icons/ibutt_10px.png diff --git a/applications/external/flipfrid/rfid_10px.png b/applications/external/multi_fuzzer/icons/rfid_10px.png similarity index 100% rename from applications/external/flipfrid/rfid_10px.png rename to applications/external/multi_fuzzer/icons/rfid_10px.png diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.c b/applications/external/multi_fuzzer/lib/worker/fake_worker.c similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/fake_worker.c rename to applications/external/multi_fuzzer/lib/worker/fake_worker.c diff --git a/applications/external/pacs_fuzzer/lib/worker/fake_worker.h b/applications/external/multi_fuzzer/lib/worker/fake_worker.h similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/fake_worker.h rename to applications/external/multi_fuzzer/lib/worker/fake_worker.h diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.c b/applications/external/multi_fuzzer/lib/worker/protocol.c similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/protocol.c rename to applications/external/multi_fuzzer/lib/worker/protocol.c diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol.h b/applications/external/multi_fuzzer/lib/worker/protocol.h similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/protocol.h rename to applications/external/multi_fuzzer/lib/worker/protocol.h diff --git a/applications/external/pacs_fuzzer/lib/worker/protocol_i.h b/applications/external/multi_fuzzer/lib/worker/protocol_i.h similarity index 100% rename from applications/external/pacs_fuzzer/lib/worker/protocol_i.h rename to applications/external/multi_fuzzer/lib/worker/protocol_i.h diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene.c similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene.c rename to applications/external/multi_fuzzer/scenes/fuzzer_scene.c diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene.h b/applications/external/multi_fuzzer/scenes/fuzzer_scene.h similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene.h rename to applications/external/multi_fuzzer/scenes/fuzzer_scene.h diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene_attack.c similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene_attack.c rename to applications/external/multi_fuzzer/scenes/fuzzer_scene_attack.c diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h b/applications/external/multi_fuzzer/scenes/fuzzer_scene_config.h similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene_config.h rename to applications/external/multi_fuzzer/scenes/fuzzer_scene_config.h diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene_field_editor.c similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene_field_editor.c rename to applications/external/multi_fuzzer/scenes/fuzzer_scene_field_editor.c diff --git a/applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c b/applications/external/multi_fuzzer/scenes/fuzzer_scene_main.c similarity index 100% rename from applications/external/pacs_fuzzer/scenes/fuzzer_scene_main.c rename to applications/external/multi_fuzzer/scenes/fuzzer_scene_main.c diff --git a/applications/external/pacs_fuzzer/todo.md b/applications/external/multi_fuzzer/todo.md similarity index 100% rename from applications/external/pacs_fuzzer/todo.md rename to applications/external/multi_fuzzer/todo.md diff --git a/applications/external/pacs_fuzzer/views/attack.c b/applications/external/multi_fuzzer/views/attack.c similarity index 100% rename from applications/external/pacs_fuzzer/views/attack.c rename to applications/external/multi_fuzzer/views/attack.c diff --git a/applications/external/pacs_fuzzer/views/attack.h b/applications/external/multi_fuzzer/views/attack.h similarity index 100% rename from applications/external/pacs_fuzzer/views/attack.h rename to applications/external/multi_fuzzer/views/attack.h diff --git a/applications/external/pacs_fuzzer/views/field_editor.c b/applications/external/multi_fuzzer/views/field_editor.c similarity index 100% rename from applications/external/pacs_fuzzer/views/field_editor.c rename to applications/external/multi_fuzzer/views/field_editor.c diff --git a/applications/external/pacs_fuzzer/views/field_editor.h b/applications/external/multi_fuzzer/views/field_editor.h similarity index 100% rename from applications/external/pacs_fuzzer/views/field_editor.h rename to applications/external/multi_fuzzer/views/field_editor.h diff --git a/applications/external/pacs_fuzzer/views/main_menu.c b/applications/external/multi_fuzzer/views/main_menu.c similarity index 100% rename from applications/external/pacs_fuzzer/views/main_menu.c rename to applications/external/multi_fuzzer/views/main_menu.c diff --git a/applications/external/pacs_fuzzer/views/main_menu.h b/applications/external/multi_fuzzer/views/main_menu.h similarity index 100% rename from applications/external/pacs_fuzzer/views/main_menu.h rename to applications/external/multi_fuzzer/views/main_menu.h diff --git a/applications/external/pacs_fuzzer/icons/125_10px.png b/applications/external/pacs_fuzzer/icons/125_10px.png deleted file mode 100644 index ce01284a2c1f3eb413f581b84f1fb3f3a2a7223b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)bWZjP>yH&963)5S4_<9hOs!iI<>&pI=m5)bW_|craZ$KesPZ!4!j_b)kjvx52z42i= z^Wk##wtdVzTiGS{99;2>!TC2M!yZeXz}?LkD}l;YOI#yLQW8s2t&)pUffR$0fsvuE zfvK*cNr<75m9c@9v4ysQft7*5`ikN&C>nC}Q!>*kp&E>VdO{3LtqcsU49p-Jly36? Qy~)7f>FVdQ&MBb@0C$~I0{{R3 diff --git a/applications/external/pacs_fuzzer/icons/rfid_10px.png b/applications/external/pacs_fuzzer/icons/rfid_10px.png deleted file mode 100644 index 8097f477552333f695733baf98f72e52ae840206..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2389 zcmcIleQXnD7{6^!#>U44<0gnZixCj6_tCqycdm@C?Y2%j%BZlR1g>}Qb?4f<$dYAbBp?=L>s9H44zi+G=z@ z$+GSR2r_^Bu5APL?}u;SJTN7D;oJYQXJbzy83Gd~}8wyVLJ?VRDO5w9Zzw8@g?;6Z|IeLEIrsbQiG#zUHZ2RGh zXD*aUd-8i4_PpADs0}HdxBi3jq2Z#Q^P&Dr^r?|WO%j;%cM#(P)0|Mc8; z{t8g`*A^{V)i?clE}f@2H&CjT}F< z?2jues!qd7PS)z04FoBfX?^mLy)Tp_$Rt&cG?`7IrJSH9?7UT9dorQHXauRON@~2& z3QRN#VzT0~4fhY&P+9cYRxu$Wr1?OLT-T+86?9@-1c8#I)9z+Pr-LUJp%g)pI7#A^ z8>2{$U^#~a&AepaJ-|V!`|Vrt9lHFc42XX!YK-a5tz}b zn0yjbjJa6^KQIJc)=XJdPz#Zds%@sn2DzpWkAYyf`V1Rfhy zjlu{PBk2f5aSnoGTnh;YM-b`I5OjjboBR#IOoSjf8osX&Rz+FroJeRW#03?@@6WtU}<<5`k-GmIOPTuus$(zJDPkQf=2B!Xdi7eP5vyx@MnDzsUZu=b~oE2;v- z$W@bzGC*KNw}3fBr-TVK?Z%=6L1S(pkqisLNim1EOqXHr@bS^87Ap}ViVmI>S)RcJ zh9WV*(*TPy(I`d}G$F90;3Qy6q1W>I)VQjLR1sDe;)?<&sd|Ek{*e=W4B(m)v)l~P z;VJ5514`GK>5mm)eP$Jx(Uj>pUa-9Gu?d#Q0Om>GmdB{x#CWFnceDTqI*$11FhiBh z4qgY|7_9WanhU=fd4q2spSnP~C5On1n8Y&u^S%9C@(-&evej?~Ro2+Su!z(GxDJJ~1-%>yfuE?__2!EG(Q-w?lmFd+!g0U&StM zdu5hu`a91J8fzAOad#fF`0WbM%BH;qRp;*CJ^0}3t1p(6l^kTQe)IdHQfq1l0}cMe I)$5-48`f?Vpa1{> From 98e0f5a8f6552c02dae3e4964cd721f17be0aa9d Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 02:59:59 +0300 Subject: [PATCH 092/102] Update docs --- ReadMe.md | 2 +- documentation/SubGHzRemoteProg.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index f9e759e0bf..0c545682cf 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -96,7 +96,7 @@ Encoders or sending made by @Eng1n33r(first implementation in Q2 2022) & @xMaste - CAME Atomo -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - Nice Flor S -> How to create new remote - [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - FAAC SLH (Spa) [External seed calculation required (For info contact me in Discord: Nano#8998)] -- Keeloq: BFT Mitto [External seed calculation required (For info contact me in Discord: Nano#8998)] -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) +- Keeloq: BFT Mitto -> Update! check out new [instructions](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/documentation/SubGHzRemoteProg.md) - Star Line - Security+ v1 & v2 (encoders was made in OFW) diff --git a/documentation/SubGHzRemoteProg.md b/documentation/SubGHzRemoteProg.md index 7cd8ea68a4..e53319b2f8 100644 --- a/documentation/SubGHzRemoteProg.md +++ b/documentation/SubGHzRemoteProg.md @@ -60,6 +60,8 @@ Watch this videos to learn more (videos in Russian language): https://www.youtub ## BFT Mitto +How to create new remote and bind it to receiver (will not conflict with original remotes): + 1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> BFT Mitto 433Mhz 2. Open your new remote file 3. You need to be in minimum 3 meters to receiver @@ -68,6 +70,32 @@ Watch this videos to learn more (videos in Russian language): https://www.youtub 6. Long press (Right Arrow) - (0xF button - Btn:F) on Flipper for like 3-5 sec 7. Done? +OR + +1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> BFT Mitto 433Mhz +2. Open your new remote file +3. Open your receiver board box +4. **Watch this video**: https://www.youtube.com/watch?v=5QXMBKI_-Ls +5. Long press (Right Arrow) - (0xF button - Btn:F) on Flipper for like 3-5 sec -> Will act like holding Button 1 & 2 on original remote as shown on video +6. Done? + +-- + +How to get seed to make full clone of your remote (**will conflict with original remote!!!!!**): + +**WARNING!!!! This method can desync your original remote, please avoid using it! It can be used in rare cases like when your remote works poorly or has broken buttons and you want to replace it with flipper** + +1. Open `Read` in SubGHz on your flipper +2. (ONLY FOR ORIGINAL REMOTES) Hold all buttons on your remote at same time, example -> for 2 button remote - press them both at same time and hold OR press hidden button on back of remote with a pin or paper clip +3. You will receive signal on your flipper, open that signal and see `Fix:` value, it should start from `F` like `F00F1C9B` +4. If `Fix:` is showing first `F` see `Hop:` value -> This is your remote Seed +5. Write down Hop value +6. Press button on your remote that you want to clone and receive its signal on your flipper +7. Open and write down `Fix:` value where first digit will be same as your button ID `Btn:` +8. Create new remote using BFT Mitto [Manual] - Enter FIX from step 7, enter counter `FF F9`, enter seed from step 5 +9. Using counter values like `FF F9` can help bypassing current original remote counter value, and in result it also can fully desync original remote, only one remote can work at same time using this method +10. Throw away your original remote since now it needs to be re-added into receiver board :C + ## CAME Atomo 1. Use google to find instructions - `how to program new CAME Atomo remote into receiver` From 5496fa29584e1882c4563966698e01c5a1b9a680 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 8 Jun 2023 03:00:53 +0300 Subject: [PATCH 093/102] upd anims --- .ci_files/anims_ofw.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.ci_files/anims_ofw.txt b/.ci_files/anims_ofw.txt index 4fa369f820..e3d3314c0a 100644 --- a/.ci_files/anims_ofw.txt +++ b/.ci_files/anims_ofw.txt @@ -120,6 +120,13 @@ Min level: 2 Max level: 2 Weight: 3 +Name: L2_Dj_128x64 +Min butthurt: 0 +Max butthurt: 8 +Min level: 2 +Max level: 3 +Weight: 4 + Name: L3_Furippa3_128x64 Min butthurt: 0 Max butthurt: 6 From 81f42afed0474a4c580ff698563c072eeab1b58a Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:11:38 +0100 Subject: [PATCH 094/102] Connection go much brrr --- applications/main/bad_kb/bad_kb_app.c | 96 +++---------------- applications/main/bad_kb/bad_kb_app.h | 5 - .../main/bad_kb/helpers/ducky_script.c | 95 ++++++++++++++++++ .../main/bad_kb/helpers/ducky_script.h | 13 +++ 4 files changed, 119 insertions(+), 90 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index c2d7e1fe3e..1bcfc57f80 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -125,74 +125,6 @@ void bad_kb_config_switch_remember_mode(BadKbApp* app) { bad_kb_reload_worker(app); } -int32_t bad_kb_connection_init(BadKbApp* app) { - app->prev_config.usb_mode = furi_hal_usb_get_config(); - furi_hal_usb_set_config(NULL, NULL); - - strcpy( - app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - memcpy( - app->prev_config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); - app->prev_config.bt_mode = furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); - - bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(app->bt); - bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH); - if(strcmp(app->config.bt_name, "") != 0) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); - } - if(app->bt_remember) { - furi_hal_bt_set_profile_mac_addr( - FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - } else { - if(memcmp( - app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) != - 0) { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); - } - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - } - bt_set_profile(app->bt, BtProfileHidKeyboard); - if(strcmp(app->config.bt_name, "") == 0) { - strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - } - if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) == - 0) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_KB_MAC_ADDRESS_LEN); - } - if(app->is_bt) { - furi_hal_bt_start_advertising(); - if(app->bt_remember) { - bt_enable_peer_key_update(app->bt); - } else { - bt_disable_peer_key_update(app->bt); - } - } else { - furi_hal_bt_stop_advertising(); - } - - return 0; -} - -void bad_kb_connection_deinit(BadKbApp* app) { - furi_hal_usb_set_config(app->prev_config.usb_mode, NULL); - - bt_disconnect(app->bt); - bt_keys_storage_set_default_path(app->bt); - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mode); - bt_set_profile(app->bt, BtProfileSerial); - bt_enable_peer_key_update(app->bt); -} - BadKbApp* bad_kb_app_alloc(char* arg) { BadKbApp* app = malloc(sizeof(BadKbApp)); @@ -258,23 +190,17 @@ BadKbApp* bad_kb_app_alloc(char* arg) { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - if(furi_hal_usb_is_locked()) { - app->error = BadKbAppErrorCloseRpc; - app->conn_init_thread = NULL; - scene_manager_next_scene(app->scene_manager, BadKbSceneError); + app->conn_mode = BadKbConnModeNone; + app->conn_init_thread = + furi_thread_alloc_ex("BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_conn_refresh, app); + furi_thread_start(app->conn_init_thread); + if(!furi_string_empty(app->file_path)) { + app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); + bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); + scene_manager_next_scene(app->scene_manager, BadKbSceneWork); } else { - app->conn_init_thread = furi_thread_alloc_ex( - "BadKbConnInit", 1024, (FuriThreadCallback)bad_kb_connection_init, app); - furi_thread_start(app->conn_init_thread); - if(!furi_string_empty(app->file_path)) { - app->bad_kb_script = - bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL, app); - bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); - scene_manager_next_scene(app->scene_manager, BadKbSceneWork); - } else { - furi_string_set(app->file_path, BAD_KB_APP_BASE_FOLDER); - scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect); - } + furi_string_set(app->file_path, BAD_KB_APP_BASE_FOLDER); + scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect); } return app; @@ -317,8 +243,8 @@ void bad_kb_app_free(BadKbApp* app) { if(app->conn_init_thread) { furi_thread_join(app->conn_init_thread); furi_thread_free(app->conn_init_thread); - bad_kb_connection_deinit(app); } + bad_kb_conn_reset(app); // Close records furi_record_close(RECORD_GUI); diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h index 20bc87ce44..662e051816 100644 --- a/applications/main/bad_kb/bad_kb_app.h +++ b/applications/main/bad_kb/bad_kb_app.h @@ -1,7 +1,6 @@ #pragma once #include "bad_kb_app.h" -#include "bad_kb_paths.h" #include "scenes/bad_kb_scene.h" #include "helpers/ducky_script.h" @@ -29,7 +28,3 @@ typedef enum { } BadKbAppView; void bad_kb_config_switch_remember_mode(BadKbApp* app); - -int32_t bad_kb_connection_init(BadKbApp* app); - -void bad_kb_connection_deinit(BadKbApp* app); diff --git a/applications/main/bad_kb/helpers/ducky_script.c b/applications/main/bad_kb/helpers/ducky_script.c index 57742a4643..8ffb1430f3 100644 --- a/applications/main/bad_kb/helpers/ducky_script.c +++ b/applications/main/bad_kb/helpers/ducky_script.c @@ -566,6 +566,93 @@ static uint32_t bad_kb_flags_get(uint32_t flags_mask, uint32_t timeout) { return flags; } +int32_t bad_kb_conn_refresh(BadKbApp* app) { + bool bt = app->is_bt; + + if(app->conn_mode != BadKbConnModeNone) bad_kb_conn_reset(app); + + if(bt) { + strcpy( + app->prev_config.bt_name, + furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); + memcpy( + app->prev_config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_ADDRESS_LEN); + app->prev_config.bt_mode = + furi_hal_bt_get_profile_pairing_method(FuriHalBtProfileHidKeyboard); + bt_timeout = bt_hid_delays[LevelRssi39_0]; + bt_disconnect(app->bt); + bt_keys_storage_set_storage_path(app->bt, BAD_KB_KEYS_PATH); + if(strcmp(app->config.bt_name, "") != 0) { + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); + } + if(app->bt_remember) { + furi_hal_bt_set_profile_mac_addr( + FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_KB_BOUND_MAC_ADDRESS); + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); + } else { + if(memcmp( + app->config.bt_mac, + (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, + BAD_KB_MAC_ADDRESS_LEN) != 0) { + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); + } + furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); + } + furi_check(bt_set_profile(app->bt, BtProfileHidKeyboard)); + if(strcmp(app->config.bt_name, "") == 0) { + strcpy( + app->config.bt_name, + furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); + } + if(memcmp( + app->config.bt_mac, (uint8_t*)&BAD_KB_EMPTY_MAC_ADDRESS, BAD_KB_MAC_ADDRESS_LEN) == + 0) { + memcpy( + app->config.bt_mac, + furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), + BAD_KB_MAC_ADDRESS_LEN); + } + if(app->bt_remember) { + bt_enable_peer_key_update(app->bt); + } else { + bt_disable_peer_key_update(app->bt); + } + furi_hal_bt_start_advertising(); + + app->conn_mode = BadKbConnModeBt; + + } else { + app->prev_config.usb_mode = furi_hal_usb_get_config(); + furi_hal_usb_unlock(); + furi_check(furi_hal_usb_set_config(NULL, NULL)); + + app->conn_mode = BadKbConnModeUsb; + } + + return 0; +} + +void bad_kb_conn_reset(BadKbApp* app) { + if(app->conn_mode == BadKbConnModeBt) { + bt_disconnect(app->bt); + bt_keys_storage_set_default_path(app->bt); + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); + furi_hal_bt_set_profile_pairing_method( + FuriHalBtProfileHidKeyboard, app->prev_config.bt_mode); + furi_check(bt_set_profile(app->bt, BtProfileSerial)); + bt_enable_peer_key_update(app->bt); + + } else if(app->conn_mode == BadKbConnModeUsb) { + furi_check(furi_hal_usb_set_config(app->prev_config.usb_mode, NULL)); + } + + app->conn_mode = BadKbConnModeNone; +} + static int32_t bad_kb_worker(void* context) { BadKbScript* bad_kb = context; @@ -579,9 +666,17 @@ static int32_t bad_kb_worker(void* context) { bad_kb->line_prev = furi_string_alloc(); bad_kb->string_print = furi_string_alloc(); + if(bad_kb->app->conn_init_thread) { + furi_thread_join(bad_kb->app->conn_init_thread); + furi_thread_free(bad_kb->app->conn_init_thread); + bad_kb->app->conn_init_thread = NULL; + } + if(bad_kb->bt) { + if(bad_kb->app->conn_mode != BadKbConnModeBt) bad_kb_conn_refresh(bad_kb->app); bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb); } else { + if(bad_kb->app->conn_mode != BadKbConnModeUsb) bad_kb_conn_refresh(bad_kb->app); furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, bad_kb); } diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index ecdf8fc02e..f4b9a4924d 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -14,6 +14,7 @@ extern "C" { #include #include #include "../views/bad_kb_view.h" +#include "../bad_kb_paths.h" #define FILE_BUFFER_LEN 16 @@ -127,6 +128,12 @@ typedef struct { GapPairing bt_mode; } BadKbConfig; +typedef enum { + BadKbConnModeNone, + BadKbConnModeUsb, + BadKbConnModeBt, +} BadKbConnMode; + struct BadKbApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -149,6 +156,8 @@ struct BadKbApp { bool bt_remember; BadKbConfig config; BadKbConfig prev_config; + + BadKbConnMode conn_mode; FuriThread* conn_init_thread; FuriThread* switch_mode_thread; }; @@ -157,6 +166,10 @@ int32_t bad_kb_config_switch_mode(BadKbApp* app); void bad_kb_config_refresh_menu(BadKbApp* app); +int32_t bad_kb_conn_refresh(BadKbApp* app); + +void bad_kb_conn_reset(BadKbApp* app); + #ifdef __cplusplus } #endif From cefad3dde63609a5b4106a34b35b4bb3ec858d83 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:32:24 +0100 Subject: [PATCH 095/102] Begone, uselessness! --- .../main/bad_kb/helpers/ducky_script.h | 1 - .../main/bad_kb/scenes/bad_kb_scene_error.c | 26 ------------------- 2 files changed, 27 deletions(-) diff --git a/applications/main/bad_kb/helpers/ducky_script.h b/applications/main/bad_kb/helpers/ducky_script.h index f4b9a4924d..9e4ed77f48 100644 --- a/applications/main/bad_kb/helpers/ducky_script.h +++ b/applications/main/bad_kb/helpers/ducky_script.h @@ -118,7 +118,6 @@ extern const uint8_t BAD_KB_EMPTY_MAC_ADDRESS[BAD_KB_MAC_ADDRESS_LEN]; typedef enum { BadKbAppErrorNoFiles, - BadKbAppErrorCloseRpc, } BadKbAppError; typedef struct { diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_error.c b/applications/main/bad_kb/scenes/bad_kb_scene_error.c index 53393016cb..eac04c4486 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_error.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_error.c @@ -1,5 +1,4 @@ #include "../bad_kb_app.h" -#include static void bad_kb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { @@ -26,31 +25,6 @@ void bad_kb_scene_error_on_enter(void* context) { "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); widget_add_button_element( app->widget, GuiButtonTypeLeft, "Back", bad_kb_scene_error_event_callback, app); - } else if(app->error == BadKbAppErrorCloseRpc) { - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->is_nsfw) { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Pull out from\nPC or phone to\nuse me like this."); - } else { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } } view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewError); From 1a45bf4f85e728aa8daaea54c275649b80817865 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 01:32:49 +0100 Subject: [PATCH 096/102] Format --- .../external/multi_fuzzer/application.fam | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/applications/external/multi_fuzzer/application.fam b/applications/external/multi_fuzzer/application.fam index a0ce1be0aa..6cd7969e12 100644 --- a/applications/external/multi_fuzzer/application.fam +++ b/applications/external/multi_fuzzer/application.fam @@ -4,21 +4,21 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_ibtn", requires=[ - "gui", + "gui", "storage", - "dialogs", - "input", + "dialogs", + "input", "notification", ], stack_size=2 * 1024, fap_icon="icons/ibutt_10px.png", fap_category="Tools", fap_private_libs=[ - Lib( - name="worker", - cdefines=["IBUTTON_PROTOCOL"], - ), - ], + Lib( + name="worker", + cdefines=["IBUTTON_PROTOCOL"], + ), + ], fap_icon_assets="icons", fap_icon_assets_symbol="fuzzer", ) @@ -29,21 +29,21 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="fuzzer_start_rfid", requires=[ - "gui", + "gui", "storage", - "dialogs", - "input", + "dialogs", + "input", "notification", ], stack_size=2 * 1024, fap_icon="icons/rfid_10px.png", fap_category="Tools", fap_private_libs=[ - Lib( - name="worker", - cdefines=["RFID_125_PROTOCOL"], - ), - ], + Lib( + name="worker", + cdefines=["RFID_125_PROTOCOL"], + ), + ], fap_icon_assets="icons", fap_icon_assets_symbol="fuzzer", ) From 436194e6c79ce143dee9ee25f3581812908cd346 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 8 Jun 2023 08:47:27 +0400 Subject: [PATCH 097/102] [FL-3346] fbt: added Flipper selection when multiple are connected over USB (#2723) * fbt: added Flipper selection when multiple are connected over USB * scripts: serial_cli: added --port (-p) option --- SConstruct | 6 ++++-- scripts/fbt_tools/fbt_dist.py | 2 +- scripts/fbt_tools/fbt_extapps.py | 3 +-- scripts/flipper/utils/cdc.py | 2 +- scripts/serial_cli.py | 6 +++++- scripts/ufbt/SConstruct | 4 ++-- scripts/ufbt/commandline.scons | 5 +++++ site_scons/commandline.scons | 5 +++++ 8 files changed, 24 insertions(+), 9 deletions(-) diff --git a/SConstruct b/SConstruct index e2568287df..b51154f703 100644 --- a/SConstruct +++ b/SConstruct @@ -171,7 +171,7 @@ distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_ fap_deploy = distenv.PhonyTarget( "fap_deploy", - "${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps", + "${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send ${SOURCE} /ext/apps", source=Dir("#/assets/resources/apps"), ) @@ -323,7 +323,9 @@ distenv.PhonyTarget( ) # Start Flipper CLI via PySerial's miniterm -distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py") +distenv.PhonyTarget( + "cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}" +) # Find blackmagic probe diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index a43d62e9dc..e47898bd9e 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -132,7 +132,7 @@ def generate(env): "UsbInstall": Builder( action=[ Action( - '${PYTHON3} "${SELFUPDATE_SCRIPT}" ${UPDATE_BUNDLE_DIR}/update.fuf' + '${PYTHON3} "${SELFUPDATE_SCRIPT}" -p ${FLIP_PORT} ${UPDATE_BUNDLE_DIR}/update.fuf' ), Touch("${TARGET}"), ] diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 1a1bad29e7..16d5dcbabd 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -431,7 +431,7 @@ def _add_host_app_to_targets(host_app): # print(deploy_sources, flipp_dist_paths) env.PhonyTarget( launch_target_name, - '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', + '${PYTHON3} "${APP_RUN_SCRIPT}" -p ${FLIP_PORT} ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', source=deploy_sources, FLIPPER_FILE_TARGETS=flipp_dist_paths, EXTRA_ARGS=run_script_extra_ars, @@ -443,7 +443,6 @@ def generate(env, **kw): env.SetDefault( EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", - STORAGE_SCRIPT="${FBT_SCRIPT_DIR}/storage.py", ) if not env["VERBOSE"]: env.SetDefault( diff --git a/scripts/flipper/utils/cdc.py b/scripts/flipper/utils/cdc.py index 7c73516702..9564088598 100644 --- a/scripts/flipper/utils/cdc.py +++ b/scripts/flipper/utils/cdc.py @@ -6,7 +6,7 @@ def resolve_port(logger, portname: str = "auto"): if portname != "auto": return portname # Try guessing - flippers = list(list_ports.grep("flip")) + flippers = list(list_ports.grep("flip_")) if len(flippers) == 1: flipper = flippers[0] logger.info(f"Using {flipper.serial_number} on {flipper.device}") diff --git a/scripts/serial_cli.py b/scripts/serial_cli.py index 6dae68be6f..8e35d57fac 100644 --- a/scripts/serial_cli.py +++ b/scripts/serial_cli.py @@ -1,3 +1,4 @@ +import argparse import logging import os import subprocess @@ -8,7 +9,10 @@ def main(): logger = logging.getLogger() - if not (port := resolve_port(logger, "auto")): + parser = argparse.ArgumentParser() + parser.add_argument("-p", "--port", help="CDC Port", default="auto") + args = parser.parse_args() + if not (port := resolve_port(logger, args.port)): logger.error("Is Flipper connected via USB and not in DFU mode?") return 1 subprocess.call( diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index a1acd270a7..8812a4e559 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -342,7 +342,7 @@ else: appenv.PhonyTarget( "cli", - '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py"', + '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}', ) # Linter @@ -469,7 +469,7 @@ if dolphin_src_dir.exists(): ) dist_env.PhonyTarget( "dolphin_ext", - '${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py send "${SOURCE}" /ext/dolphin', + '${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send "${SOURCE}" /ext/dolphin', source=ufbt_build_dir.Dir("dolphin"), ) else: diff --git a/scripts/ufbt/commandline.scons b/scripts/ufbt/commandline.scons index a9b91bbca9..349b4ef252 100644 --- a/scripts/ufbt/commandline.scons +++ b/scripts/ufbt/commandline.scons @@ -71,6 +71,11 @@ vars.AddVariables( validator=PathVariable.PathIsDir, default="", ), + ( + "FLIP_PORT", + "CDC Port of Flipper to use, if multiple are connected", + "auto", + ), ) Return("vars") diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index e31927c594..b70b5cff56 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -243,6 +243,11 @@ vars.AddVariables( " app can check what version it is being built for.", "Official", ), + ( + "FLIP_PORT", + "Full port name of Flipper to use, if multiple Flippers are connected", + "auto", + ), ) Return("vars") From c186d2b0ccb44ab9b2adb1d0aad1efd9219492ac Mon Sep 17 00:00:00 2001 From: "g3gg0.de" Date: Thu, 8 Jun 2023 07:30:53 +0200 Subject: [PATCH 098/102] added ISO15693 (NfcV) reading, saving, emulating and revealing from privacy mode (unlock) (#2316) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added support for ISO15693 (NfcV) emulation, added support for reading SLIX tags * SLIX: fixed crash situation when an invalid password was requested * ISO15693: show emulate menu when opening file * rename NfcV emulate scene to match other NfcV names * optimize allocation size for signals * ISO15693: further optimizations of allocation and free code * ISO15693: reduce latency on state machine reset * respond with block security status when option flag is set * increased maximum memory size to match standard added security status handling/load/save added SELECT/QUIET handling more fine grained allocation routines and checks fix memset sizes * added "Listen NfcV Reader" to sniff traffic from reader to card * added correct description to delete menu * also added DSFID/AFI handling and locking * increase sniff log size * scale NfcV frequency a bit, add echo mode, fix signal level at the end * use symbolic modulated/unmodulated GPIO levels * honor AFI field, decrease verbosity and removed debug code * refactor defines for less namespace pollution by using NFCV_ prefixes * correct an oversight that original cards return an generic error when addressing outside block range * use inverse modulation, increasing readable range significantly * rework and better document nfc chip initialization * nfcv code review fixes * Disable accidentally left on signal debug gpio output * Improve NFCV Read/Info GUIs. Authored by @xMasterX, committed by @nvx * Fix crash that occurs when you exit from NFCV emulation and start it again. Authored by @xMasterX, committed by @nvx * Remove delay from emulation loop. This improves compatibility when the reader is Android. * Lib: digital signal debug output pin info Co-authored-by: Tiernan Messmer Co-authored-by: MX <10697207+xMasterX@users.noreply.github.com> Co-authored-by: gornekich Co-authored-by: あく --- .../main/nfc/helpers/nfc_custom_event.h | 2 + applications/main/nfc/nfc.c | 3 + .../main/nfc/scenes/nfc_scene_config.h | 7 + .../main/nfc/scenes/nfc_scene_delete.c | 7 +- .../main/nfc/scenes/nfc_scene_extra_actions.c | 20 + .../main/nfc/scenes/nfc_scene_nfc_data_info.c | 162 +- .../main/nfc/scenes/nfc_scene_nfcv_emulate.c | 169 ++ .../nfc/scenes/nfc_scene_nfcv_key_input.c | 48 + .../main/nfc/scenes/nfc_scene_nfcv_menu.c | 68 + .../nfc/scenes/nfc_scene_nfcv_read_success.c | 94 ++ .../main/nfc/scenes/nfc_scene_nfcv_sniff.c | 155 ++ .../main/nfc/scenes/nfc_scene_nfcv_unlock.c | 154 ++ .../nfc/scenes/nfc_scene_nfcv_unlock_menu.c | 60 + applications/main/nfc/scenes/nfc_scene_read.c | 5 + applications/main/nfc/scenes/nfc_scene_rpc.c | 7 + .../main/nfc/scenes/nfc_scene_saved_menu.c | 3 + firmware/targets/f7/api_symbols.csv | 8 + lib/digital_signal/digital_signal.c | 13 +- lib/nfc/nfc_device.c | 376 ++++- lib/nfc/nfc_device.h | 4 + lib/nfc/nfc_worker.c | 245 ++- lib/nfc/nfc_worker.h | 11 + lib/nfc/nfc_worker_i.h | 2 + lib/nfc/protocols/nfcv.c | 1398 +++++++++++++++++ lib/nfc/protocols/nfcv.h | 291 ++++ lib/nfc/protocols/slix.c | 412 +++++ lib/nfc/protocols/slix.h | 46 + 27 files changed, 3737 insertions(+), 33 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_menu.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c create mode 100644 applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c create mode 100644 lib/nfc/protocols/nfcv.c create mode 100644 lib/nfc/protocols/nfcv.h create mode 100644 lib/nfc/protocols/slix.c create mode 100644 lib/nfc/protocols/slix.h diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index 4227a5b14e..aa932a3d85 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -12,4 +12,6 @@ enum NfcCustomEvent { NfcCustomEventDictAttackSkip, NfcCustomEventRpcLoad, NfcCustomEventRpcSessionClose, + NfcCustomEventUpdateLog, + NfcCustomEventSaveShadow, }; diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 4540f5d9f0..f68b7f2f24 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -290,6 +290,9 @@ int32_t nfc_app(void* p) { } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); DOLPHIN_DEED(DolphinDeedNfcEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); + DOLPHIN_DEED(DolphinDeedNfcEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) { scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo); } else { diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index a9da07dfda..f11d147983 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -14,6 +14,13 @@ ADD_SCENE(nfc, file_select, FileSelect) ADD_SCENE(nfc, emulate_uid, EmulateUid) ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess) ADD_SCENE(nfc, nfca_menu, NfcaMenu) +ADD_SCENE(nfc, nfcv_menu, NfcVMenu) +ADD_SCENE(nfc, nfcv_unlock_menu, NfcVUnlockMenu) +ADD_SCENE(nfc, nfcv_key_input, NfcVKeyInput) +ADD_SCENE(nfc, nfcv_unlock, NfcVUnlock) +ADD_SCENE(nfc, nfcv_emulate, NfcVEmulate) +ADD_SCENE(nfc, nfcv_sniff, NfcVSniff) +ADD_SCENE(nfc, nfcv_read_success, NfcVReadSuccess) ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess) ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData) ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu) diff --git a/applications/main/nfc/scenes/nfc_scene_delete.c b/applications/main/nfc/scenes/nfc_scene_delete.c index cbb52bfd0f..0808db45a3 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete.c +++ b/applications/main/nfc/scenes/nfc_scene_delete.c @@ -31,6 +31,8 @@ void nfc_scene_delete_on_enter(void* context) { nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); NfcProtocol protocol = nfc->dev->dev_data.protocol; + const char* nfc_type = "NFC-A"; + if(protocol == NfcDeviceProtocolEMV) { furi_string_set(temp_str, "EMV bank card"); } else if(protocol == NfcDeviceProtocolMifareUl) { @@ -39,12 +41,15 @@ void nfc_scene_delete_on_enter(void* context) { furi_string_set(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_set(temp_str, "MIFARE DESFire"); + } else if(protocol == NfcDeviceProtocolNfcV) { + furi_string_set(temp_str, "ISO15693 tag"); + nfc_type = "NFC-V"; } else { furi_string_set(temp_str, "Unknown ISO tag"); } widget_add_string_element( nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(temp_str)); - widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A"); + widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, nfc_type); furi_string_free(temp_str); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 66aaf5a26d..7f5bc7e758 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -4,6 +4,8 @@ enum SubmenuIndex { SubmenuIndexReadCardType, SubmenuIndexMfClassicKeys, SubmenuIndexMfUltralightUnlock, + SubmenuIndexNfcVUnlock, + SubmenuIndexNfcVSniff, }; void nfc_scene_extra_actions_submenu_callback(void* context, uint32_t index) { @@ -34,6 +36,18 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_add_item( + submenu, + "Unlock SLIX-L", + SubmenuIndexNfcVUnlock, + nfc_scene_extra_actions_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Listen NfcV Reader", + SubmenuIndexNfcVSniff, + nfc_scene_extra_actions_submenu_callback, + nfc); submenu_set_selected_item( submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); @@ -58,6 +72,12 @@ bool nfc_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state(nfc->scene_manager, NfcSceneReadCardType, 0); scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardType); consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlock) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + consumed = true; + } else if(event.event == SubmenuIndexNfcVSniff) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVSniff); + consumed = true; } scene_manager_set_scene_state(nfc->scene_manager, NfcSceneExtraActions, event.event); } diff --git a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c index b44bb5e64c..eb2f939c60 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c +++ b/applications/main/nfc/scenes/nfc_scene_nfc_data_info.c @@ -41,19 +41,165 @@ void nfc_scene_nfc_data_info_on_enter(void* context) { temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type)); } else if(protocol == NfcDeviceProtocolMifareDesfire) { furi_string_cat_printf(temp_str, "\e#MIFARE DESFire\n"); + } else if(protocol == NfcDeviceProtocolNfcV) { + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } } else { furi_string_cat_printf(temp_str, "\e#Unknown ISO tag\n"); } // Set tag iso data - char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; - furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); - furi_string_cat_printf(temp_str, "UID:"); - for(size_t i = 0; i < nfc_data->uid_len; i++) { - furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + if(protocol == NfcDeviceProtocolNfcV) { + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + + furi_string_cat_printf(temp_str, "UID:\n"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\n"); + + furi_string_cat_printf( + temp_str, + "DSFID: %02X %s\n", + nfcv_data->dsfid, + (nfcv_data->security_status[0] & NfcVLockBitDsfid) ? "(locked)" : ""); + furi_string_cat_printf( + temp_str, + "AFI: %02X %s\n", + nfcv_data->afi, + (nfcv_data->security_status[0] & NfcVLockBitAfi) ? "(locked)" : ""); + furi_string_cat_printf(temp_str, "IC Ref: %02X\n", nfcv_data->ic_ref); + furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "Type: Plain\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "Type: SLIX\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " EAS %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "Type: SLIX-S\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Read %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); + furi_string_cat_printf( + temp_str, + " Write %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); + furi_string_cat_printf( + temp_str, + " Privacy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); + furi_string_cat_printf( + temp_str, + " Destroy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); + furi_string_cat_printf( + temp_str, + " EAS %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "Type: SLIX-L\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Privacy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); + furi_string_cat_printf( + temp_str, + " Destroy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); + furi_string_cat_printf( + temp_str, + " EAS %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "Type: SLIX2\n"); + furi_string_cat_printf(temp_str, "Keys:\n"); + furi_string_cat_printf( + temp_str, + " Read %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_read, 4)); + furi_string_cat_printf( + temp_str, + " Write %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_write, 4)); + furi_string_cat_printf( + temp_str, + " Privacy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_privacy, 4)); + furi_string_cat_printf( + temp_str, + " Destroy %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_destroy, 4)); + furi_string_cat_printf( + temp_str, + " EAS %08llX\n", + nfc_util_bytes2num(nfcv_data->sub_data.slix.key_eas, 4)); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } + + furi_string_cat_printf( + temp_str, "Data (%d byte)\n", nfcv_data->block_num * nfcv_data->block_size); + + int maxBlocks = nfcv_data->block_num; + if(maxBlocks > 32) { + maxBlocks = 32; + furi_string_cat_printf(temp_str, "(truncated to %d blocks)\n", maxBlocks); + } + + for(int block = 0; block < maxBlocks; block++) { + const char* status = (nfcv_data->security_status[block] & 0x01) ? "(lck)" : ""; + for(int pos = 0; pos < nfcv_data->block_size; pos++) { + furi_string_cat_printf( + temp_str, " %02X", nfcv_data->data[block * nfcv_data->block_size + pos]); + } + furi_string_cat_printf(temp_str, " %s\n", status); + } + + } else { + char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3'; + furi_string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type); + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf( + temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); + furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); } - furi_string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]); - furi_string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak); // Set application specific data if(protocol == NfcDeviceProtocolMifareDesfire) { @@ -139,6 +285,8 @@ bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) { consumed = true; } else if(protocol == NfcDeviceProtocolMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicData); + } else if(protocol == NfcDeviceProtocolNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c new file mode 100644 index 0000000000..3dd7c460b5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c @@ -0,0 +1,169 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (200) + +enum { + NfcSceneNfcVEmulateStateWidget, + NfcSceneNfcVEmulateStateTextBox, +}; + +bool nfc_scene_nfcv_emulate_worker_callback(NfcWorkerEvent event, void* context) { + furi_assert(context); + Nfc* nfc = context; + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + } + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } + return true; +} + +void nfc_scene_nfcv_emulate_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_emulate_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { + FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data; + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); + widget_add_string_multiline_element( + widget, 87, 13, AlignCenter, AlignTop, FontPrimary, "Emulating\nNFC V"); + if(strcmp(nfc->dev->dev_name, "")) { + furi_string_printf(info_str, "%s", nfc->dev->dev_name); + } else { + for(uint8_t i = 0; i < data->uid_len; i++) { + furi_string_cat_printf(info_str, "%02X ", data->uid[i]); + } + } + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 52, 40, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_emulate_widget_callback, nfc); + } + } +} + +void nfc_scene_nfcv_emulate_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_nfcv_emulate_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_nfcv_emulate_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_nfcv_emulate_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVEmulate); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventUpdateLog) { + // Add data button to widget if data is received for the first time + if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_emulate_widget_config(nfc, true); + } + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVEmulateStateWidget) { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateTextBox); + } + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneNfcVEmulateStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVEmulate, NfcSceneNfcVEmulateStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_nfcv_emulate_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c new file mode 100644 index 0000000000..cc53c4dcb4 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_key_input.c @@ -0,0 +1,48 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_nfcv_key_input_byte_input_callback(void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + memcpy(data->key_privacy, nfc->byte_input_store, 4); + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); +} + +void nfc_scene_nfcv_key_input_on_enter(void* context) { + Nfc* nfc = context; + + // Setup view + ByteInput* byte_input = nfc->byte_input; + byte_input_set_header_text(byte_input, "Enter The Password In Hex"); + byte_input_set_result_callback( + byte_input, + nfc_scene_nfcv_key_input_byte_input_callback, + NULL, + nfc, + nfc->byte_input_store, + 4); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); +} + +bool nfc_scene_nfcv_key_input_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventByteInputDone) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_nfcv_key_input_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + byte_input_set_result_callback(nfc->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(nfc->byte_input, ""); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c new file mode 100644 index 0000000000..7c6780b7c7 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_menu.c @@ -0,0 +1,68 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexSave, + SubmenuIndexEmulate, + SubmenuIndexInfo, +}; + +void nfc_scene_nfcv_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_nfcv_menu_submenu_callback, nfc); + submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_nfcv_menu_submenu_callback, nfc); + + submenu_set_selected_item( + nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVMenu)); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSave) { + nfc->dev->format = NfcDeviceSaveFormatNfcV; + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); + if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + DOLPHIN_DEED(DolphinDeedNfcAddEmulate); + } else { + DOLPHIN_DEED(DolphinDeedNfcEmulate); + } + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_previous_scene(nfc->scene_manager); + } + + return consumed; +} + +void nfc_scene_nfcv_menu_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c new file mode 100644 index 0000000000..bdf7692ccb --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_read_success.c @@ -0,0 +1,94 @@ +#include "../nfc_i.h" + +void nfc_scene_nfcv_read_success_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + furi_assert(context); + Nfc* nfc = context; + + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_read_success_on_enter(void* context) { + Nfc* nfc = context; + NfcDeviceData* dev_data = &nfc->dev->dev_data; + FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + // Setup view + Widget* widget = nfc->widget; + widget_add_button_element( + widget, GuiButtonTypeLeft, "Retry", nfc_scene_nfcv_read_success_widget_callback, nfc); + widget_add_button_element( + widget, GuiButtonTypeRight, "More", nfc_scene_nfcv_read_success_widget_callback, nfc); + + FuriString* temp_str = furi_string_alloc(); + + switch(dev_data->nfcv_data.sub_type) { + case NfcVTypePlain: + furi_string_cat_printf(temp_str, "\e#ISO15693\n"); + break; + case NfcVTypeSlix: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX\n"); + break; + case NfcVTypeSlixS: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-S\n"); + break; + case NfcVTypeSlixL: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX-L\n"); + break; + case NfcVTypeSlix2: + furi_string_cat_printf(temp_str, "\e#ISO15693 SLIX2\n"); + break; + default: + furi_string_cat_printf(temp_str, "\e#ISO15693 (unknown)\n"); + break; + } + furi_string_cat_printf(temp_str, "UID:"); + for(size_t i = 0; i < nfc_data->uid_len; i++) { + furi_string_cat_printf(temp_str, " %02X", nfc_data->uid[i]); + } + furi_string_cat_printf(temp_str, "\n"); + furi_string_cat_printf(temp_str, "Blocks: %02X\n", nfcv_data->block_num); + furi_string_cat_printf(temp_str, "Blocksize: %02X\n", nfcv_data->block_size); + + widget_add_text_scroll_element(widget, 0, 0, 128, 52, furi_string_get_cstr(temp_str)); + furi_string_free(temp_str); + + notification_message_block(nfc->notifications, &sequence_set_green_255); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_nfcv_read_success_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm); + consumed = true; + } else if(event.event == GuiButtonTypeRight) { + // Clear device name + nfc_device_set_name(nfc->dev, ""); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVMenu); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm); + consumed = true; + } + + return consumed; +} + +void nfc_scene_nfcv_read_success_on_exit(void* context) { + Nfc* nfc = context; + + notification_message_block(nfc->notifications, &sequence_reset_green); + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c new file mode 100644 index 0000000000..2c0f17981b --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_sniff.c @@ -0,0 +1,155 @@ +#include "../nfc_i.h" + +#define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (800) + +enum { + NfcSceneNfcVSniffStateWidget, + NfcSceneNfcVSniffStateTextBox, +}; + +bool nfc_scene_nfcv_sniff_worker_callback(NfcWorkerEvent event, void* context) { + UNUSED(event); + furi_assert(context); + Nfc* nfc = context; + + switch(event) { + case NfcWorkerEventNfcVCommandExecuted: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventUpdateLog); + break; + case NfcWorkerEventNfcVContentChanged: + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventSaveShadow); + break; + default: + break; + } + return true; +} + +void nfc_scene_nfcv_sniff_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_nfcv_sniff_textbox_callback(void* context) { + furi_assert(context); + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +static void nfc_scene_nfcv_sniff_widget_config(Nfc* nfc, bool data_received) { + Widget* widget = nfc->widget; + widget_reset(widget); + FuriString* info_str; + info_str = furi_string_alloc(); + + widget_add_icon_element(widget, 0, 3, &I_RFIDDolphinSend_97x61); + widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Listen NfcV"); + furi_string_trim(info_str); + widget_add_text_box_element( + widget, 56, 43, 70, 21, AlignCenter, AlignTop, furi_string_get_cstr(info_str), true); + furi_string_free(info_str); + if(data_received) { + widget_add_button_element( + widget, GuiButtonTypeCenter, "Log", nfc_scene_nfcv_sniff_widget_callback, nfc); + } +} + +void nfc_scene_nfcv_sniff_on_enter(void* context) { + Nfc* nfc = context; + + // Setup Widget + nfc_scene_nfcv_sniff_widget_config(nfc, false); + // Setup TextBox + TextBox* text_box = nfc->text_box; + text_box_set_font(text_box, TextBoxFontHex); + text_box_set_focus(text_box, TextBoxFocusEnd); + text_box_set_text(text_box, ""); + furi_string_reset(nfc->text_box_store); + + // Set Widget state and view + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + // Start worker + memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVSniff, + &nfc->dev->dev_data, + nfc_scene_nfcv_sniff_worker_callback, + nfc); + + nfc_blink_emulate_start(nfc); +} + +bool nfc_scene_nfcv_sniff_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + NfcVData* nfcv_data = &nfc->dev->dev_data.nfcv_data; + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVSniff); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcCustomEventUpdateLog) { + // Add data button to widget if data is received for the first time + if(strlen(nfcv_data->last_command) > 0) { + if(!furi_string_size(nfc->text_box_store)) { + nfc_scene_nfcv_sniff_widget_config(nfc, true); + } + /* use the last n bytes from the log so there's enough space for the new log entry */ + size_t maxSize = + NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX - (strlen(nfcv_data->last_command) + 1); + if(furi_string_size(nfc->text_box_store) >= maxSize) { + furi_string_right(nfc->text_box_store, (strlen(nfcv_data->last_command) + 1)); + } + furi_string_cat_printf(nfc->text_box_store, "%s", nfcv_data->last_command); + furi_string_push_back(nfc->text_box_store, '\n'); + text_box_set_text(nfc->text_box, furi_string_get_cstr(nfc->text_box_store)); + + /* clear previously logged command */ + strcpy(nfcv_data->last_command, ""); + } + consumed = true; + } else if(event.event == NfcCustomEventSaveShadow) { + if(furi_string_size(nfc->dev->load_path)) { + nfc_device_save_shadow(nfc->dev, furi_string_get_cstr(nfc->dev->load_path)); + } + consumed = true; + } else if(event.event == GuiButtonTypeCenter && state == NfcSceneNfcVSniffStateWidget) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateTextBox); + consumed = true; + } else if(event.event == NfcCustomEventViewExit && state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + if(state == NfcSceneNfcVSniffStateTextBox) { + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVSniff, NfcSceneNfcVSniffStateWidget); + consumed = true; + } + } + + return consumed; +} + +void nfc_scene_nfcv_sniff_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + + // Clear view + widget_reset(nfc->widget); + text_box_reset(nfc->text_box); + furi_string_reset(nfc->text_box_store); + + nfc_blink_stop(nfc); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c new file mode 100644 index 0000000000..26de304de5 --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock.c @@ -0,0 +1,154 @@ +#include "../nfc_i.h" +#include + +typedef enum { + NfcSceneNfcVUnlockStateIdle, + NfcSceneNfcVUnlockStateDetecting, + NfcSceneNfcVUnlockStateUnlocked, + NfcSceneNfcVUnlockStateAlreadyUnlocked, + NfcSceneNfcVUnlockStateNotSupportedCard, +} NfcSceneNfcVUnlockState; + +static bool nfc_scene_nfcv_unlock_worker_callback(NfcWorkerEvent event, void* context) { + Nfc* nfc = context; + NfcVSlixData* data = &nfc->dev->dev_data.nfcv_data.sub_data.slix; + + if(event == NfcWorkerEventNfcVPassKey) { + memcpy(data->key_privacy, nfc->byte_input_store, 4); + } else { + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); + } + return true; +} + +void nfc_scene_nfcv_unlock_popup_callback(void* context) { + Nfc* nfc = context; + view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); +} + +void nfc_scene_nfcv_unlock_set_state(Nfc* nfc, NfcSceneNfcVUnlockState state) { + FuriHalNfcDevData* nfc_data = &(nfc->dev->dev_data.nfc_data); + NfcVData* nfcv_data = &(nfc->dev->dev_data.nfcv_data); + + uint32_t curr_state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock); + if(curr_state != state) { + Popup* popup = nfc->popup; + if(state == NfcSceneNfcVUnlockStateDetecting) { + popup_reset(popup); + popup_set_text( + popup, "Put figurine on\nFlipper's back", 97, 24, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); + } else if(state == NfcSceneNfcVUnlockStateUnlocked) { + popup_reset(popup); + + if(nfc_worker_get_state(nfc->worker) == NfcWorkerStateNfcVUnlockAndSave) { + snprintf( + nfc->dev->dev_name, + sizeof(nfc->dev->dev_name), + "SLIX_%02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + + nfc->dev->format = NfcDeviceSaveFormatNfcV; + + if(nfc_save_file(nfc)) { + popup_set_header(popup, "Successfully\nsaved", 94, 3, AlignCenter, AlignTop); + } else { + popup_set_header( + popup, "Unlocked but\nsave failed!", 94, 3, AlignCenter, AlignTop); + } + } else { + popup_set_header(popup, "Successfully\nunlocked", 94, 3, AlignCenter, AlignTop); + } + + notification_message(nfc->notifications, &sequence_single_vibro); + //notification_message(nfc->notifications, &sequence_success); + + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + + } else if(state == NfcSceneNfcVUnlockStateAlreadyUnlocked) { + popup_reset(popup); + + popup_set_header(popup, "Already\nUnlocked!", 94, 3, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, nfc); + popup_set_callback(popup, nfc_scene_nfcv_unlock_popup_callback); + popup_set_timeout(popup, 1500); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + } else if(state == NfcSceneNfcVUnlockStateNotSupportedCard) { + popup_reset(popup); + popup_set_header(popup, "Wrong Type Of Card!", 64, 3, AlignCenter, AlignTop); + popup_set_text(popup, nfcv_data->error, 4, 22, AlignLeft, AlignTop); + popup_set_icon(popup, 73, 20, &I_DolphinCommon_56x48); + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlock, state); + } +} + +void nfc_scene_nfcv_unlock_on_enter(void* context) { + Nfc* nfc = context; + + nfc_device_clear(nfc->dev); + // Setup view + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup); + + // Start worker + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVUnlockAndSave, + &nfc->dev->dev_data, + nfc_scene_nfcv_unlock_worker_callback, + nfc); + + nfc_blink_read_start(nfc); +} + +bool nfc_scene_nfcv_unlock_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == NfcWorkerEventCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventAborted) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateAlreadyUnlocked); + consumed = true; + } else if(event.event == NfcWorkerEventNoCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateDetecting); + consumed = true; + } else if(event.event == NfcWorkerEventWrongCardDetected) { + nfc_scene_nfcv_unlock_set_state(nfc, NfcSceneNfcVUnlockStateNotSupportedCard); + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneNfcVUnlockMenu); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_on_exit(void* context) { + Nfc* nfc = context; + + // Stop worker + nfc_worker_stop(nfc->worker); + // Clear view + popup_reset(nfc->popup); + nfc_blink_stop(nfc); + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneNfcVUnlock, NfcSceneNfcVUnlockStateIdle); +} diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c new file mode 100644 index 0000000000..9c4c81fbda --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_unlock_menu.c @@ -0,0 +1,60 @@ +#include "../nfc_i.h" +#include + +enum SubmenuIndex { + SubmenuIndexNfcVUnlockMenuManual, + SubmenuIndexNfcVUnlockMenuTonieBox, +}; + +void nfc_scene_nfcv_unlock_menu_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_nfcv_unlock_menu_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu); + submenu_add_item( + submenu, + "Enter PWD Manually", + SubmenuIndexNfcVUnlockMenuManual, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Auth As TonieBox", + SubmenuIndexNfcVUnlockMenuTonieBox, + nfc_scene_nfcv_unlock_menu_submenu_callback, + nfc); + submenu_set_selected_item(submenu, state); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_nfcv_unlock_menu_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNfcVUnlockMenuManual) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodManual; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVKeyInput); + consumed = true; + } else if(event.event == SubmenuIndexNfcVUnlockMenuTonieBox) { + nfc->dev->dev_data.nfcv_data.auth_method = NfcVAuthMethodTonieBox; + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVUnlock); + DOLPHIN_DEED(DolphinDeedNfcRead); + consumed = true; + } + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcVUnlockMenu, event.event); + } + return consumed; +} + +void nfc_scene_nfcv_unlock_menu_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_read.c b/applications/main/nfc/scenes/nfc_scene_read.c index 938f2da675..5a1814b6c6 100644 --- a/applications/main/nfc/scenes/nfc_scene_read.c +++ b/applications/main/nfc/scenes/nfc_scene_read.c @@ -68,6 +68,11 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess); DOLPHIN_DEED(DolphinDeedNfcReadSuccess); consumed = true; + } else if(event.event == NfcWorkerEventReadNfcV) { + notification_message(nfc->notifications, &sequence_success); + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVReadSuccess); + DOLPHIN_DEED(DolphinDeedNfcReadSuccess); + consumed = true; } else if(event.event == NfcWorkerEventReadMfUltralight) { notification_message(nfc->notifications, &sequence_success); // Set unlock password input to 0xFFFFFFFF only on fresh read diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c index 60d01a30da..d06ee75646 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -55,6 +55,13 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { &nfc->dev->dev_data, nfc_scene_rpc_emulate_callback, nfc); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + nfc_worker_start( + nfc->worker, + NfcWorkerStateNfcVEmulate, + &nfc->dev->dev_data, + nfc_scene_rpc_emulate_callback, + nfc); } else { nfc_worker_start( nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); diff --git a/applications/main/nfc/scenes/nfc_scene_saved_menu.c b/applications/main/nfc/scenes/nfc_scene_saved_menu.c index 4573cdc452..8412c17bcb 100644 --- a/applications/main/nfc/scenes/nfc_scene_saved_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_saved_menu.c @@ -44,6 +44,7 @@ void nfc_scene_saved_menu_on_enter(void* context) { } else if( (nfc->dev->format == NfcDeviceSaveFormatMifareUl && mf_ul_emulation_supported(&nfc->dev->dev_data.mf_ul_data)) || + nfc->dev->format == NfcDeviceSaveFormatNfcV || nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { submenu_add_item( submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc); @@ -118,6 +119,8 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightEmulate); } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicEmulate); + } else if(nfc->dev->format == NfcDeviceSaveFormatNfcV) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcVEmulate); } else { scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid); } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b6eb8c7658..e2de368363 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2090,6 +2090,14 @@ Function,-,nfca_get_crc16,uint16_t,"uint8_t*, uint16_t" Function,-,nfca_signal_alloc,NfcaSignal*, Function,-,nfca_signal_encode,void,"NfcaSignal*, uint8_t*, uint16_t, uint8_t*" Function,-,nfca_signal_free,void,NfcaSignal* +Function,+,nfcv_emu_deinit,void,NfcVData* +Function,+,nfcv_emu_init,void,"FuriHalNfcDevData*, NfcVData*" +Function,+,nfcv_emu_loop,_Bool,"FuriHalNfcTxRxContext*, FuriHalNfcDevData*, NfcVData*, uint32_t" +Function,+,nfcv_emu_send,void,"FuriHalNfcTxRxContext*, NfcVData*, uint8_t*, uint8_t, NfcVSendFlags, uint32_t" +Function,-,nfcv_inventory,ReturnCode,uint8_t* +Function,-,nfcv_read_blocks,ReturnCode,"NfcVReader*, NfcVData*" +Function,-,nfcv_read_card,_Bool,"NfcVReader*, FuriHalNfcDevData*, NfcVData*" +Function,-,nfcv_read_sysinfo,ReturnCode,"FuriHalNfcDevData*, NfcVData*" Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" diff --git a/lib/digital_signal/digital_signal.c b/lib/digital_signal/digital_signal.c index 94dbeeab92..39aa9cbc6e 100644 --- a/lib/digital_signal/digital_signal.c +++ b/lib/digital_signal/digital_signal.c @@ -9,7 +9,7 @@ #include /* must be on bank B */ -#define DEBUG_OUTPUT gpio_ext_pb3 +// For debugging purposes use `--extra-define=DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN=gpio_ext_pb3` fbt option struct ReloadBuffer { uint32_t* buffer; /* DMA ringbuffer */ @@ -194,9 +194,9 @@ void digital_signal_prepare_arr(DigitalSignal* signal) { uint32_t bit_set = internals->gpio->pin; uint32_t bit_reset = internals->gpio->pin << 16; -#ifdef DEBUG_OUTPUT - bit_set |= DEBUG_OUTPUT.pin; - bit_reset |= DEBUG_OUTPUT.pin << 16; +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + bit_set |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin; + bit_reset |= DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN.pin << 16; #endif if(signal->start_level) { @@ -540,8 +540,9 @@ bool digital_sequence_send(DigitalSequence* sequence) { struct ReloadBuffer* dma_buffer = sequence->dma_buffer; furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -#ifdef DEBUG_OUTPUT - furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +#ifdef DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN + furi_hal_gpio_init( + &DIGITAL_SIGNAL_DEBUG_OUTPUT_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); #endif if(sequence->bake) { diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 5179130702..9646c262e5 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -58,6 +58,8 @@ static void nfc_device_prepare_format_string(NfcDevice* dev, FuriString* format_ furi_string_set(format_string, "Mifare Classic"); } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { furi_string_set(format_string, "Mifare DESFire"); + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + furi_string_set(format_string, "ISO15693"); } else { furi_string_set(format_string, "Unknown"); } @@ -93,6 +95,11 @@ static bool nfc_device_parse_format_string(NfcDevice* dev, FuriString* format_st dev->dev_data.protocol = NfcDeviceProtocolMifareDesfire; return true; } + if(furi_string_start_with_str(format_string, "ISO15693")) { + dev->format = NfcDeviceSaveFormatNfcV; + dev->dev_data.protocol = NfcDeviceProtocolNfcV; + return true; + } return false; } @@ -650,7 +657,327 @@ bool nfc_device_load_mifare_df_data(FlipperFormat* file, NfcDevice* dev) { return parsed; } -// Leave for backward compatibility +static bool nfc_device_save_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX specific data")) break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-S specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_s_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX-L specific data")) break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix_l_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + + do { + if(!flipper_format_write_comment_cstr(file, "SLIX2 specific data")) break; + if(!flipper_format_write_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_write_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_write_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_write_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_write_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_write_bool(file, "Privacy Mode", &data->privacy, 1)) break; + saved = true; + } while(false); + + return saved; +} + +bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; + memset(data, 0, sizeof(NfcVSlixData)); + + do { + if(!flipper_format_read_hex(file, "Password Read", data->key_read, sizeof(data->key_read))) + break; + if(!flipper_format_read_hex( + file, "Password Write", data->key_write, sizeof(data->key_write))) + break; + if(!flipper_format_read_hex( + file, "Password Privacy", data->key_privacy, sizeof(data->key_privacy))) + break; + if(!flipper_format_read_hex( + file, "Password Destroy", data->key_destroy, sizeof(data->key_destroy))) + break; + if(!flipper_format_read_hex(file, "Password EAS", data->key_eas, sizeof(data->key_eas))) + break; + if(!flipper_format_read_bool(file, "Privacy Mode", &data->privacy, 1)) break; + + parsed = true; + } while(false); + + return parsed; +} + +static bool nfc_device_save_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_uint8 = 0; + + if(!flipper_format_write_comment_cstr(file, "Data Storage Format Identifier")) break; + if(!flipper_format_write_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_write_comment_cstr(file, "Application Family Identifier")) break; + if(!flipper_format_write_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_write_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + temp_uint32 = data->block_num; + if(!flipper_format_write_comment_cstr(file, "Number of memory blocks, usually 0 to 256")) + break; + if(!flipper_format_write_uint32(file, "Block Count", &temp_uint32, 1)) break; + if(!flipper_format_write_comment_cstr(file, "Size of a single memory block, usually 4")) + break; + if(!flipper_format_write_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_write_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + if(!flipper_format_write_comment_cstr( + file, "First byte: DSFID (0x01) / AFI (0x02) lock info, others: block lock info")) + break; + if(!flipper_format_write_hex( + file, "Security Status", data->security_status, 1 + data->block_num)) + break; + if(!flipper_format_write_comment_cstr( + file, + "Subtype of this card (0 = ISO15693, 1 = SLIX, 2 = SLIX-S, 3 = SLIX-L, 4 = SLIX2)")) + break; + temp_uint8 = (uint8_t)data->sub_type; + if(!flipper_format_write_hex(file, "Subtype", &temp_uint8, 1)) break; + + switch(data->sub_type) { + case NfcVTypePlain: + if(!flipper_format_write_comment_cstr(file, "End of ISO15693 parameters")) break; + saved = true; + break; + case NfcVTypeSlix: + saved = nfc_device_save_slix_data(file, dev); + break; + case NfcVTypeSlixS: + saved = nfc_device_save_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + saved = nfc_device_save_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + saved = nfc_device_save_slix2_data(file, dev); + break; + default: + break; + } + } while(false); + + return saved; +} + +bool nfc_device_load_nfcv_data(FlipperFormat* file, NfcDevice* dev) { + bool parsed = false; + NfcVData* data = &dev->dev_data.nfcv_data; + + memset(data, 0x00, sizeof(NfcVData)); + + do { + uint32_t temp_uint32 = 0; + uint8_t temp_value = 0; + + if(!flipper_format_read_hex(file, "DSFID", &(data->dsfid), 1)) break; + if(!flipper_format_read_hex(file, "AFI", &(data->afi), 1)) break; + if(!flipper_format_read_hex(file, "IC Reference", &(data->ic_ref), 1)) break; + if(!flipper_format_read_uint32(file, "Block Count", &temp_uint32, 1)) break; + data->block_num = temp_uint32; + if(!flipper_format_read_hex(file, "Block Size", &(data->block_size), 1)) break; + if(!flipper_format_read_hex( + file, "Data Content", data->data, data->block_num * data->block_size)) + break; + + /* optional, as added later */ + if(flipper_format_key_exist(file, "Security Status")) { + if(!flipper_format_read_hex( + file, "Security Status", data->security_status, 1 + data->block_num)) + break; + } + if(!flipper_format_read_hex(file, "Subtype", &temp_value, 1)) break; + data->sub_type = temp_value; + + switch(data->sub_type) { + case NfcVTypePlain: + parsed = true; + break; + case NfcVTypeSlix: + parsed = nfc_device_load_slix_data(file, dev); + break; + case NfcVTypeSlixS: + parsed = nfc_device_load_slix_s_data(file, dev); + break; + case NfcVTypeSlixL: + parsed = nfc_device_load_slix_l_data(file, dev); + break; + case NfcVTypeSlix2: + parsed = nfc_device_load_slix2_data(file, dev); + break; + default: + break; + } + } while(false); + + return parsed; +} + +static bool nfc_device_save_bank_card_data(FlipperFormat* file, NfcDevice* dev) { + bool saved = false; + EmvData* data = &dev->dev_data.emv_data; + uint32_t data_temp = 0; + + do { + // Write Bank card specific data + if(!flipper_format_write_comment_cstr(file, "Bank card specific data")) break; + if(!flipper_format_write_hex(file, "AID", data->aid, data->aid_len)) break; + if(!flipper_format_write_string_cstr(file, "Name", data->name)) break; + if(!flipper_format_write_hex(file, "Number", data->number, data->number_len)) break; + if(data->exp_mon) { + uint8_t exp_data[2] = {data->exp_mon, data->exp_year}; + if(!flipper_format_write_hex(file, "Exp data", exp_data, sizeof(exp_data))) break; + } + if(data->country_code) { + data_temp = data->country_code; + if(!flipper_format_write_uint32(file, "Country code", &data_temp, 1)) break; + } + if(data->currency_code) { + data_temp = data->currency_code; + if(!flipper_format_write_uint32(file, "Currency code", &data_temp, 1)) break; + } + saved = true; + } while(false); + + return saved; +} + bool nfc_device_load_bank_card_data(FlipperFormat* file, NfcDevice* dev) { bool parsed = false; EmvData* data = &dev->dev_data.emv_data; @@ -1069,23 +1396,32 @@ bool nfc_device_save(NfcDevice* dev, const char* dev_name) { if(!flipper_format_write_header_cstr(file, nfc_file_header, nfc_file_version)) break; // Write nfc device type if(!flipper_format_write_comment_cstr( - file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic")) + file, "Nfc device type can be UID, Mifare Ultralight, Mifare Classic or ISO15693")) break; nfc_device_prepare_format_string(dev, temp_str); if(!flipper_format_write_string(file, "Device type", temp_str)) break; - // Write UID, ATQA, SAK - if(!flipper_format_write_comment_cstr(file, "UID, ATQA and SAK are common for all formats")) - break; + // Write UID + if(!flipper_format_write_comment_cstr(file, "UID is common for all formats")) break; if(!flipper_format_write_hex(file, "UID", data->uid, data->uid_len)) break; - // Save ATQA in MSB order for correct companion apps display - uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; - if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; - if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + + if(dev->format != NfcDeviceSaveFormatNfcV) { + // Write ATQA, SAK + if(!flipper_format_write_comment_cstr(file, "ISO14443 specific fields")) break; + // Save ATQA in MSB order for correct companion apps display + uint8_t atqa[2] = {data->atqa[1], data->atqa[0]}; + if(!flipper_format_write_hex(file, "ATQA", atqa, 2)) break; + if(!flipper_format_write_hex(file, "SAK", &data->sak, 1)) break; + } + // Save more data if necessary if(dev->format == NfcDeviceSaveFormatMifareUl) { if(!nfc_device_save_mifare_ul_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_save_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_save_nfcv_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatBankCard) { + if(!nfc_device_save_bank_card_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareClassic) { // Save data if(!nfc_device_save_mifare_classic_data(file, dev)) break; @@ -1160,18 +1496,20 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_parse_format_string(dev, temp_str)) break; // Read and parse UID, ATQA and SAK if(!flipper_format_get_value_count(file, "UID", &data_cnt)) break; - if(!(data_cnt == 4 || data_cnt == 7)) break; + if(!(data_cnt == 4 || data_cnt == 7 || data_cnt == 8)) break; data->uid_len = data_cnt; if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break; - if(version == version_with_lsb_atqa) { - if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; - } else { - uint8_t atqa[2] = {}; - if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; - data->atqa[0] = atqa[1]; - data->atqa[1] = atqa[0]; + if(dev->format != NfcDeviceSaveFormatNfcV) { + if(version == version_with_lsb_atqa) { + if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break; + } else { + uint8_t atqa[2] = {}; + if(!flipper_format_read_hex(file, "ATQA", atqa, 2)) break; + data->atqa[0] = atqa[1]; + data->atqa[1] = atqa[0]; + } + if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; } - if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break; // Load CUID uint8_t* cuid_start = data->uid; if(data->uid_len == 7) { @@ -1186,6 +1524,8 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia if(!nfc_device_load_mifare_classic_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatMifareDesfire) { if(!nfc_device_load_mifare_df_data(file, dev)) break; + } else if(dev->format == NfcDeviceSaveFormatNfcV) { + if(!nfc_device_load_nfcv_data(file, dev)) break; } else if(dev->format == NfcDeviceSaveFormatBankCard) { if(!nfc_device_load_bank_card_data(file, dev)) break; } diff --git a/lib/nfc/nfc_device.h b/lib/nfc/nfc_device.h index df37ec3df5..20df4f8918 100644 --- a/lib/nfc/nfc_device.h +++ b/lib/nfc/nfc_device.h @@ -11,6 +11,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -31,6 +32,7 @@ typedef enum { NfcDeviceProtocolMifareUl, NfcDeviceProtocolMifareClassic, NfcDeviceProtocolMifareDesfire, + NfcDeviceProtocolNfcV } NfcProtocol; typedef enum { @@ -39,6 +41,7 @@ typedef enum { NfcDeviceSaveFormatMifareUl, NfcDeviceSaveFormatMifareClassic, NfcDeviceSaveFormatMifareDesfire, + NfcDeviceSaveFormatNfcV, } NfcDeviceSaveFormat; typedef struct { @@ -73,6 +76,7 @@ typedef struct { MfUltralightData mf_ul_data; MfClassicData mf_classic_data; MifareDesfireData mf_df_data; + NfcVData nfcv_data; }; FuriString* parsed_data; } NfcDeviceData; diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index daa8fee59a..293f1ce705 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -111,6 +111,14 @@ int32_t nfc_worker_task(void* context) { nfc_worker_mf_classic_dict_attack(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { nfc_worker_analyze_reader(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + nfc_worker_nfcv_emulate(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVSniff) { + nfc_worker_nfcv_sniff(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlock) { + nfc_worker_nfcv_unlock(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + nfc_worker_nfcv_unlock(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -118,6 +126,236 @@ int32_t nfc_worker_task(void* context) { return 0; } +static bool nfc_worker_read_nfcv(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { + bool read_success = false; + NfcVReader reader = {}; + + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + furi_hal_nfc_sleep(); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + do { + if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; + if(!nfcv_read_card(&reader, nfc_data, nfcv_data)) break; + + read_success = true; + } while(false); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + + return read_success; +} + +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_emu_init(nfc_data, nfcv_data); + while(nfc_worker->state == NfcWorkerStateNfcVEmulate) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + if(nfcv_data->modified) { + nfc_worker->callback(NfcWorkerEventNfcVContentChanged, nfc_worker->context); + nfcv_data->modified = false; + } + } + } + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + nfcv_data->sub_type = NfcVTypeSniff; + nfcv_emu_init(nfc_data, nfcv_data); + + while(nfc_worker->state == NfcWorkerStateNfcVSniff) { + if(nfcv_emu_loop(&tx_rx, nfc_data, nfcv_data, 100)) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventNfcVCommandExecuted, nfc_worker->context); + } + } + } + nfcv_emu_deinit(nfcv_data); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { + furi_assert(nfc_worker); + furi_assert(nfc_worker->callback); + + NfcVData* nfcv_data = &nfc_worker->dev_data->nfcv_data; + FuriHalNfcTxRxContext tx_rx = {}; + uint8_t* key_data = nfcv_data->sub_data.slix.key_privacy; + uint32_t key = 0; + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + + furi_hal_nfc_sleep(); + + while((nfc_worker->state == NfcWorkerStateNfcVUnlock) || + (nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave)) { + furi_hal_nfc_exit_sleep(); + furi_hal_nfc_ll_txrx_on(); + furi_hal_nfc_ll_poll(); + if(furi_hal_nfc_ll_set_mode( + FuriHalNfcModePollNfcv, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48) != + FuriHalNfcReturnOk) { + break; + } + + furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCV_POLLER); + furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCV_POLLER); + furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); + furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCV); + + FURI_LOG_D(TAG, "Detect presence"); + ReturnCode ret = slix_get_random(nfcv_data); + + if(ret == ERR_NONE) { + /* there is some chip, responding with a RAND */ + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + FURI_LOG_D(TAG, " Chip detected. In privacy?"); + ret = nfcv_inventory(NULL); + + if(ret == ERR_NONE) { + /* chip is also visible, so no action required, just save */ + if(nfc_worker->state == NfcWorkerStateNfcVUnlockAndSave) { + NfcVReader reader = {}; + + if(!nfcv_read_card(&reader, &nfc_worker->dev_data->nfc_data, nfcv_data)) { + FURI_LOG_D(TAG, " => failed, wait for chip to disappear."); + snprintf(nfcv_data->error, sizeof(nfcv_data->error), "Read card\nfailed"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + } else { + FURI_LOG_D(TAG, " => success, wait for chip to disappear."); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + } else { + FURI_LOG_D(TAG, " => success, wait for chip to disappear."); + nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context); + } + + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + FURI_LOG_D(TAG, " => chip is already visible, wait for chip to disappear.\r\n"); + nfc_worker->callback(NfcWorkerEventAborted, nfc_worker->context); + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + + key_data[0] = 0; + key_data[1] = 0; + key_data[2] = 0; + key_data[3] = 0; + + } else { + /* chip is invisible, try to unlock */ + FURI_LOG_D(TAG, " chip is invisible, unlocking"); + + if(nfcv_data->auth_method == NfcVAuthMethodManual) { + key |= key_data[0] << 24; + key |= key_data[1] << 16; + key |= key_data[2] << 8; + key |= key_data[3] << 0; + + ret = slix_unlock(nfcv_data, 4); + } else { + key = 0x7FFD6E5B; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_unlock(nfcv_data, 4); + + if(ret != ERR_NONE) { + /* main key failed, trying second one */ + FURI_LOG_D(TAG, " trying second key after resetting"); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + if(slix_get_random(nfcv_data) != ERR_NONE) { + FURI_LOG_D(TAG, " reset failed"); + } + + key = 0x0F0F0F0F; + key_data[0] = key >> 24; + key_data[1] = key >> 16; + key_data[2] = key >> 8; + key_data[3] = key >> 0; + ret = slix_unlock(nfcv_data, 4); + } + } + if(ret != ERR_NONE) { + /* unlock failed */ + FURI_LOG_D(TAG, " => failed, wait for chip to disappear."); + snprintf( + nfcv_data->error, sizeof(nfcv_data->error), "Passwords not\naccepted"); + nfc_worker->callback(NfcWorkerEventWrongCardDetected, nfc_worker->context); + + /* reset chip */ + furi_hal_nfc_ll_txrx_off(); + furi_delay_ms(20); + furi_hal_nfc_ll_txrx_on(); + + /* wait for disappearing */ + while(slix_get_random(NULL) == ERR_NONE) { + furi_delay_ms(100); + } + } + } + } else { + nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context); + } + + furi_hal_nfc_ll_txrx_off(); + furi_hal_nfc_sleep(); + furi_delay_ms(100); + } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { bool read_success = false; MfUltralightReader reader = {}; @@ -317,7 +555,12 @@ void nfc_worker_read(NfcWorker* nfc_worker) { event = NfcWorkerEventReadUidNfcF; break; } else if(nfc_data->type == FuriHalNfcTypeV) { - event = NfcWorkerEventReadUidNfcV; + FURI_LOG_I(TAG, "NfcV detected"); + nfc_worker->dev_data->protocol = NfcDeviceProtocolNfcV; + if(nfc_worker_read_nfcv(nfc_worker, &tx_rx)) { + FURI_LOG_I(TAG, "nfc_worker_read_nfcv success"); + } + event = NfcWorkerEventReadNfcV; break; } } else { diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 8e993fc6aa..722f148574 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -18,6 +18,10 @@ typedef enum { NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, NfcWorkerStateAnalyzeReader, + NfcWorkerStateNfcVEmulate, + NfcWorkerStateNfcVUnlock, + NfcWorkerStateNfcVUnlockAndSave, + NfcWorkerStateNfcVSniff, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -39,6 +43,7 @@ typedef enum { NfcWorkerEventReadMfClassicDone, NfcWorkerEventReadMfClassicLoadKeyCache, NfcWorkerEventReadMfClassicDictAttackRequired, + NfcWorkerEventReadNfcV, // Nfc worker common events NfcWorkerEventSuccess, @@ -69,6 +74,9 @@ typedef enum { // Mifare Ultralight events NfcWorkerEventMfUltralightPassKey, // NFC worker requesting manual key NfcWorkerEventMfUltralightPwdAuth, // Reader sent auth command + NfcWorkerEventNfcVPassKey, // NFC worker requesting manual key + NfcWorkerEventNfcVCommandExecuted, + NfcWorkerEventNfcVContentChanged, } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); @@ -87,3 +95,6 @@ void nfc_worker_start( void* context); void nfc_worker_stop(NfcWorker* nfc_worker); +void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker); +void nfc_worker_nfcv_emulate(NfcWorker* nfc_worker); +void nfc_worker_nfcv_sniff(NfcWorker* nfc_worker); \ No newline at end of file diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h index 701ecb90c6..b678573ec0 100644 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include struct NfcWorker { diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c new file mode 100644 index 0000000000..ac818b7a4b --- /dev/null +++ b/lib/nfc/protocols/nfcv.c @@ -0,0 +1,1398 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nfcv.h" +#include "nfc_util.h" +#include "slix.h" + +#define TAG "NfcV" + +/* macros to map "modulate field" flag to GPIO level */ +#define GPIO_LEVEL_MODULATED NFCV_LOAD_MODULATION_POLARITY +#define GPIO_LEVEL_UNMODULATED (!GPIO_LEVEL_MODULATED) + +/* timing macros */ +#define DIGITAL_SIGNAL_UNIT_S (100000000000.0f) +#define DIGITAL_SIGNAL_UNIT_US (100000.0f) + +ReturnCode nfcv_inventory(uint8_t* uid) { + uint16_t received = 0; + rfalNfcvInventoryRes res; + ReturnCode ret = ERR_NONE; + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerInventory(RFAL_NFCV_NUM_SLOTS_1, 0, NULL, &res, &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + if(uid != NULL) { + memcpy(uid, res.UID, NFCV_UID_LENGTH); + } + } + + return ret; +} + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* nfcv_data) { + UNUSED(reader); + + uint16_t received = 0; + for(size_t block = 0; block < nfcv_data->block_num; block++) { + uint8_t rxBuf[32]; + FURI_LOG_D(TAG, "Reading block %d/%d", block, (nfcv_data->block_num - 1)); + + ReturnCode ret = ERR_NONE; + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + ret = rfalNfcvPollerReadSingleBlock( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, block, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + if(ret != ERR_NONE) { + FURI_LOG_D(TAG, "failed to read: %d", ret); + return ret; + } + memcpy( + &(nfcv_data->data[block * nfcv_data->block_size]), &rxBuf[1], nfcv_data->block_size); + FURI_LOG_D( + TAG, + " %02X %02X %02X %02X", + nfcv_data->data[block * nfcv_data->block_size + 0], + nfcv_data->data[block * nfcv_data->block_size + 1], + nfcv_data->data[block * nfcv_data->block_size + 2], + nfcv_data->data[block * nfcv_data->block_size + 3]); + } + + return ERR_NONE; +} + +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + uint8_t rxBuf[32]; + uint16_t received = 0; + ReturnCode ret = ERR_NONE; + + FURI_LOG_D(TAG, "Read SYSTEM INFORMATION..."); + + for(int tries = 0; tries < NFCV_COMMAND_RETRIES; tries++) { + /* TODO: needs proper abstraction via fury_hal(_ll)_* */ + ret = rfalNfcvPollerGetSystemInformation( + RFAL_NFCV_REQ_FLAG_DEFAULT, NULL, rxBuf, sizeof(rxBuf), &received); + + if(ret == ERR_NONE) { + break; + } + } + + if(ret == ERR_NONE) { + nfc_data->type = FuriHalNfcTypeV; + nfc_data->uid_len = NFCV_UID_LENGTH; + /* UID is stored reversed in this response */ + for(int pos = 0; pos < nfc_data->uid_len; pos++) { + nfc_data->uid[pos] = rxBuf[2 + (NFCV_UID_LENGTH - 1 - pos)]; + } + nfcv_data->dsfid = rxBuf[NFCV_UID_LENGTH + 2]; + nfcv_data->afi = rxBuf[NFCV_UID_LENGTH + 3]; + nfcv_data->block_num = rxBuf[NFCV_UID_LENGTH + 4] + 1; + nfcv_data->block_size = rxBuf[NFCV_UID_LENGTH + 5] + 1; + nfcv_data->ic_ref = rxBuf[NFCV_UID_LENGTH + 6]; + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + FURI_LOG_D( + TAG, + " DSFID %d, AFI %d, Blocks %d, Size %d, IC Ref %d", + nfcv_data->dsfid, + nfcv_data->afi, + nfcv_data->block_num, + nfcv_data->block_size, + nfcv_data->ic_ref); + return ret; + } + FURI_LOG_D(TAG, "Failed: %d", ret); + + return ret; +} + +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(reader); + furi_assert(nfc_data); + furi_assert(nfcv_data); + + if(nfcv_read_sysinfo(nfc_data, nfcv_data) != ERR_NONE) { + return false; + } + + if(nfcv_read_blocks(reader, nfcv_data) != ERR_NONE) { + return false; + } + + if(slix_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX detected"); + nfcv_data->sub_type = NfcVTypeSlix; + } else if(slix2_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX2 detected"); + nfcv_data->sub_type = NfcVTypeSlix2; + } else if(slix_s_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-S detected"); + nfcv_data->sub_type = NfcVTypeSlixS; + } else if(slix_l_check_card_type(nfc_data)) { + FURI_LOG_I(TAG, "NXP SLIX-L detected"); + nfcv_data->sub_type = NfcVTypeSlixL; + } else { + nfcv_data->sub_type = NfcVTypePlain; + } + + return true; +} + +void nfcv_crc(uint8_t* data, uint32_t length) { + uint32_t reg = 0xFFFF; + + for(size_t i = 0; i < length; i++) { + reg = reg ^ ((uint32_t)data[i]); + for(size_t j = 0; j < 8; j++) { + if(reg & 0x0001) { + reg = (reg >> 1) ^ 0x8408; + } else { + reg = (reg >> 1); + } + } + } + + uint16_t crc = ~(uint16_t)(reg & 0xffff); + + data[length + 0] = crc & 0xFF; + data[length + 1] = crc >> 8; +} + +void nfcv_emu_free_signals(NfcVEmuAirSignals* signals) { + furi_assert(signals); + + if(signals->nfcv_resp_one) { + digital_signal_free(signals->nfcv_resp_one); + } + if(signals->nfcv_resp_zero) { + digital_signal_free(signals->nfcv_resp_zero); + } + if(signals->nfcv_resp_sof) { + digital_signal_free(signals->nfcv_resp_sof); + } + if(signals->nfcv_resp_eof) { + digital_signal_free(signals->nfcv_resp_eof); + } + signals->nfcv_resp_one = NULL; + signals->nfcv_resp_zero = NULL; + signals->nfcv_resp_sof = NULL; + signals->nfcv_resp_eof = NULL; +} + +bool nfcv_emu_alloc_signals(NfcVEmuAir* air, NfcVEmuAirSignals* signals, uint32_t slowdown) { + furi_assert(air); + furi_assert(signals); + + bool success = true; + + if(!signals->nfcv_resp_one) { + /* logical one: unmodulated then 8 pulses */ + signals->nfcv_resp_one = digital_signal_alloc( + slowdown * (air->nfcv_resp_unmod->edge_cnt + 8 * air->nfcv_resp_pulse->edge_cnt)); + if(!signals->nfcv_resp_one) { + return false; + } + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 8; i++) { + success &= digital_signal_append(signals->nfcv_resp_one, air->nfcv_resp_pulse); + } + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_zero) { + /* logical zero: 8 pulses then unmodulated */ + signals->nfcv_resp_zero = digital_signal_alloc( + slowdown * (8 * air->nfcv_resp_pulse->edge_cnt + air->nfcv_resp_unmod->edge_cnt)); + if(!signals->nfcv_resp_zero) { + return false; + } + for(size_t i = 0; i < slowdown * 8; i++) { + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_pulse); + } + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_zero, air->nfcv_resp_unmod); + } + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_sof) { + /* SOF: unmodulated, 24 pulses, logic 1 */ + signals->nfcv_resp_sof = digital_signal_alloc( + slowdown * (3 * air->nfcv_resp_unmod->edge_cnt + 24 * air->nfcv_resp_pulse->edge_cnt) + + signals->nfcv_resp_one->edge_cnt); + if(!signals->nfcv_resp_sof) { + return false; + } + for(size_t i = 0; i < slowdown * 3; i++) { + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_unmod); + } + for(size_t i = 0; i < slowdown * 24; i++) { + success &= digital_signal_append(signals->nfcv_resp_sof, air->nfcv_resp_pulse); + } + success &= digital_signal_append(signals->nfcv_resp_sof, signals->nfcv_resp_one); + if(!success) { + return false; + } + } + if(!signals->nfcv_resp_eof) { + /* EOF: logic 0, 24 pulses, unmodulated */ + signals->nfcv_resp_eof = digital_signal_alloc( + signals->nfcv_resp_zero->edge_cnt + + slowdown * (24 * air->nfcv_resp_pulse->edge_cnt + 3 * air->nfcv_resp_unmod->edge_cnt) + + air->nfcv_resp_unmod->edge_cnt); + if(!signals->nfcv_resp_eof) { + return false; + } + success &= digital_signal_append(signals->nfcv_resp_eof, signals->nfcv_resp_zero); + for(size_t i = 0; i < slowdown * 23; i++) { + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_pulse); + } + /* we don't want to add the last level as we just want a transition to "unmodulated" again */ + for(size_t i = 0; i < slowdown; i++) { + success &= digital_signal_append(signals->nfcv_resp_eof, air->nfcv_resp_half_pulse); + } + } + return success; +} + +bool nfcv_emu_alloc(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + if(!nfcv_data->frame) { + nfcv_data->frame = malloc(NFCV_FRAMESIZE_MAX); + if(!nfcv_data->frame) { + return false; + } + } + + if(!nfcv_data->emu_air.nfcv_signal) { + /* assuming max frame length is 255 bytes */ + nfcv_data->emu_air.nfcv_signal = digital_sequence_alloc(8 * 255 + 2, &gpio_spi_r_mosi); + if(!nfcv_data->emu_air.nfcv_signal) { + return false; + } + } + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + /* unmodulated 256/fc or 1024/fc signal as building block */ + nfcv_data->emu_air.nfcv_resp_unmod = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_unmod) { + return false; + } + nfcv_data->emu_air.nfcv_resp_unmod->start_level = GPIO_LEVEL_UNMODULATED; + nfcv_data->emu_air.nfcv_resp_unmod->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_UNMOD_256 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_unmod->edge_cnt = 1; + } + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_pulse) { + return false; + } + nfcv_data->emu_air.nfcv_resp_pulse->start_level = GPIO_LEVEL_MODULATED; + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_timings[1] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_pulse->edge_cnt = 2; + } + + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + /* modulated fc/32 or fc/8 pulse as building block */ + nfcv_data->emu_air.nfcv_resp_half_pulse = digital_signal_alloc(4); + if(!nfcv_data->emu_air.nfcv_resp_half_pulse) { + return false; + } + nfcv_data->emu_air.nfcv_resp_half_pulse->start_level = GPIO_LEVEL_MODULATED; + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_timings[0] = + (uint32_t)(NFCV_RESP_SUBC1_PULSE_32 * DIGITAL_SIGNAL_UNIT_S); + nfcv_data->emu_air.nfcv_resp_half_pulse->edge_cnt = 1; + } + + bool success = true; + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_high, 1); + success &= nfcv_emu_alloc_signals(&nfcv_data->emu_air, &nfcv_data->emu_air.signals_low, 4); + + if(!success) { + FURI_LOG_E(TAG, "Failed to allocate signals"); + return false; + } + + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_SOF, + nfcv_data->emu_air.signals_high.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT0, + nfcv_data->emu_air.signals_high.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_BIT1, + nfcv_data->emu_air.signals_high.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_EOF, + nfcv_data->emu_air.signals_high.nfcv_resp_eof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_SOF, + nfcv_data->emu_air.signals_low.nfcv_resp_sof); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT0, + nfcv_data->emu_air.signals_low.nfcv_resp_zero); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_BIT1, + nfcv_data->emu_air.signals_low.nfcv_resp_one); + digital_sequence_set_signal( + nfcv_data->emu_air.nfcv_signal, + NFCV_SIG_LOW_EOF, + nfcv_data->emu_air.signals_low.nfcv_resp_eof); + + return true; +} + +void nfcv_emu_free(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + if(nfcv_data->frame) { + free(nfcv_data->frame); + } + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + } + if(nfcv_data->emu_air.nfcv_resp_unmod) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_unmod); + } + if(nfcv_data->emu_air.nfcv_resp_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_pulse); + } + if(nfcv_data->emu_air.nfcv_resp_half_pulse) { + digital_signal_free(nfcv_data->emu_air.nfcv_resp_half_pulse); + } + if(nfcv_data->emu_air.nfcv_signal) { + digital_sequence_free(nfcv_data->emu_air.nfcv_signal); + } + if(nfcv_data->emu_air.reader_signal) { + // Stop pulse reader and disable bus before free + pulse_reader_stop(nfcv_data->emu_air.reader_signal); + // Free pulse reader + pulse_reader_free(nfcv_data->emu_air.reader_signal); + } + + nfcv_data->frame = NULL; + nfcv_data->emu_air.nfcv_resp_unmod = NULL; + nfcv_data->emu_air.nfcv_resp_pulse = NULL; + nfcv_data->emu_air.nfcv_resp_half_pulse = NULL; + nfcv_data->emu_air.nfcv_signal = NULL; + nfcv_data->emu_air.reader_signal = NULL; + + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_high); + nfcv_emu_free_signals(&nfcv_data->emu_air.signals_low); +} + +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time) { + furi_assert(tx_rx); + furi_assert(nfcv); + + /* picked default value (0) to match the most common format */ + if(!flags) { + flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof | + NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; + } + + if(flags & NfcVSendFlagsCrc) { + nfcv_crc(data, length); + length += 2; + } + + /* depending on the request flags, send with high or low rate */ + uint32_t bit0 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT0 : NFCV_SIG_LOW_BIT0; + uint32_t bit1 = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_BIT1 : NFCV_SIG_LOW_BIT1; + uint32_t sof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_SOF : NFCV_SIG_LOW_SOF; + uint32_t eof = (flags & NfcVSendFlagsHighRate) ? NFCV_SIG_EOF : NFCV_SIG_LOW_EOF; + + digital_sequence_clear(nfcv->emu_air.nfcv_signal); + + if(flags & NfcVSendFlagsSof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, sof); + } + + for(int bit_total = 0; bit_total < length * 8; bit_total++) { + uint32_t byte_pos = bit_total / 8; + uint32_t bit_pos = bit_total % 8; + uint8_t bit_val = 0x01 << bit_pos; + + digital_sequence_add(nfcv->emu_air.nfcv_signal, (data[byte_pos] & bit_val) ? bit1 : bit0); + } + + if(flags & NfcVSendFlagsEof) { + digital_sequence_add(nfcv->emu_air.nfcv_signal, eof); + } + + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + digital_sequence_set_sendtime(nfcv->emu_air.nfcv_signal, send_time); + digital_sequence_send(nfcv->emu_air.nfcv_signal); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + + if(tx_rx->sniff_tx) { + tx_rx->sniff_tx(data, length * 8, false, tx_rx->sniff_context); + } +} + +static void nfcv_revuidcpy(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + dst[pos] = src[NFCV_UID_LENGTH - 1 - pos]; + } +} + +static int nfcv_revuidcmp(uint8_t* dst, uint8_t* src) { + for(int pos = 0; pos < NFCV_UID_LENGTH; pos++) { + if(dst[pos] != src[NFCV_UID_LENGTH - 1 - pos]) { + return 1; + } + } + return 0; +} + +void nfcv_emu_handle_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + if(nfcv_data->echo_mode) { + nfcv_emu_send( + tx_rx, + nfcv_data, + nfcv_data->frame, + nfcv_data->frame_length, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->selected = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); + ctx->response_flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof; + ctx->send_time = nfcv_data->eof_timestamp + NFCV_FDT_FC(4380); + + if(ctx->flags & NFCV_REQ_FLAG_DATA_RATE) { + ctx->response_flags |= NfcVSendFlagsHighRate; + } + if(ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) { + ctx->response_flags |= NfcVSendFlagsTwoSubcarrier; + } + + if(ctx->payload_offset + 2 > nfcv_data->frame_length) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "command 0x%02X, but packet is too short", ctx->command); +#endif + return; + } + + /* standard behavior is implemented */ + if(ctx->addressed) { + uint8_t* address = &nfcv_data->frame[ctx->address_offset]; + if(nfcv_revuidcmp(address, nfc_data->uid)) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "addressed command 0x%02X, but not for us:", ctx->command); + FURI_LOG_D( + TAG, + " dest: %02X%02X%02X%02X%02X%02X%02X%02X", + address[7], + address[6], + address[5], + address[4], + address[3], + address[2], + address[1], + address[0]); + FURI_LOG_D( + TAG, + " our UID: %02X%02X%02X%02X%02X%02X%02X%02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); +#endif + return; + } + } + + if(ctx->selected && !nfcv_data->selected) { +#ifdef NFCV_VERBOSE + FURI_LOG_D( + TAG, + "selected card shall execute command 0x%02X, but we were not selected", + ctx->command); +#endif + return; + } + + /* then give control to the card subtype specific protocol filter */ + if(ctx->emu_protocol_filter != NULL) { + if(ctx->emu_protocol_filter(tx_rx, nfc_data, nfcv_data)) { + if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE + FURI_LOG_D( + TAG, "Received command %s (handled by filter)", nfcv_data->last_command); +#endif + } + return; + } + } + + switch(ctx->command) { + case NFCV_CMD_INVENTORY: { + bool respond = false; + + if(ctx->flags & NFCV_REQ_FLAG_AFI) { + uint8_t afi = nfcv_data->frame[ctx->payload_offset]; + if(afi == nfcv_data->afi) { + respond = true; + } + } else { + respond = true; + } + + if(!nfcv_data->quiet && respond) { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; + + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY"); + } else { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "INVENTORY (quiet)"); + } + break; + } + + case NFCV_CMD_STAY_QUIET: { + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "STAYQUIET"); + nfcv_data->quiet = true; + break; + } + + case NFCV_CMD_LOCK_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + nfcv_data->security_status[block] |= 0x01; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK BLOCK %d", block); + break; + } + + case NFCV_CMD_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->dsfid = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE DSFID %02X", id); + break; + } + + case NFCV_CMD_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->afi = id; + nfcv_data->modified = true; + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "WRITE AFI %02X", id); + break; + } + + case NFCV_CMD_LOCK_DSFID: { + if(!(nfcv_data->security_status[0] & NfcVLockBitDsfid)) { + nfcv_data->security_status[0] |= NfcVLockBitDsfid; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK DSFID"); + break; + } + + case NFCV_CMD_LOCK_AFI: { + if(!(nfcv_data->security_status[0] & NfcVLockBitAfi)) { + nfcv_data->security_status[0] |= NfcVLockBitAfi; + nfcv_data->modified = true; + + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } + + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "LOCK AFI"); + break; + } + + case NFCV_CMD_SELECT: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->selected = true; + nfcv_data->quiet = false; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SELECT"); + break; + } + + case NFCV_CMD_RESET_TO_READY: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->quiet = false; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "RESET_TO_READY"); + break; + } + + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + if(block + blocks <= nfcv_data->block_num) { + uint8_t buffer_pos = 0; + + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + + for(int block_index = 0; block_index < blocks; block_index++) { + int block_current = block + block_index; + /* prepend security status */ + if(ctx->flags & NFCV_REQ_FLAG_OPTION) { + ctx->response_buffer[buffer_pos++] = + nfcv_data->security_status[1 + block_current]; + } + /* then the data block */ + memcpy( + &ctx->response_buffer[buffer_pos], + &nfcv_data->data[nfcv_data->block_size * block_current], + nfcv_data->block_size); + buffer_pos += nfcv_data->block_size; + } + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "READ BLOCK %d", block); + + break; + } + + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { + uint8_t blocks = 1; + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t data_pos = ctx->payload_offset + 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[data_pos] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[data_pos]; + uint32_t data_len = nfcv_data->block_size * blocks; + + if((block + blocks) <= nfcv_data->block_num && + (data_pos + data_len + 2) == nfcv_data->frame_length) { + ctx->response_buffer[0] = NFCV_NOERROR; + memcpy( + &nfcv_data->data[nfcv_data->block_size * block], + &nfcv_data->frame[data_pos], + data_len); + nfcv_data->modified = true; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + } else { + ctx->response_buffer[0] = NFCV_RES_FLAG_ERROR; + ctx->response_buffer[1] = NFCV_ERROR_GENERIC; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 2, ctx->response_flags, ctx->send_time); + } + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE MULTI BLOCK %d, %d blocks", + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "WRITE BLOCK %d <- %02X %02X %02X %02X", + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case NFCV_CMD_GET_SYSTEM_INFO: { + int buffer_pos = 0; + ctx->response_buffer[buffer_pos++] = NFCV_NOERROR; + ctx->response_buffer[buffer_pos++] = NFCV_SYSINFO_FLAG_DSFID | NFCV_SYSINFO_FLAG_AFI | + NFCV_SYSINFO_FLAG_MEMSIZE | NFCV_SYSINFO_FLAG_ICREF; + nfcv_revuidcpy(&ctx->response_buffer[buffer_pos], nfc_data->uid); + buffer_pos += NFCV_UID_LENGTH; + ctx->response_buffer[buffer_pos++] = nfcv_data->dsfid; /* DSFID */ + ctx->response_buffer[buffer_pos++] = nfcv_data->afi; /* AFI */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_num - 1; /* number of blocks */ + ctx->response_buffer[buffer_pos++] = nfcv_data->block_size - 1; /* block size */ + ctx->response_buffer[buffer_pos++] = nfcv_data->ic_ref; /* IC reference */ + + nfcv_emu_send( + tx_rx, + nfcv_data, + ctx->response_buffer, + buffer_pos, + ctx->response_flags, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "SYSTEMINFO"); + break; + } + + case NFCV_CMD_CUST_ECHO_MODE: { + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_data->echo_mode = true; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO mode"); + break; + } + + case NFCV_CMD_CUST_ECHO_DATA: { + nfcv_emu_send( + tx_rx, + nfcv_data, + &nfcv_data->frame[ctx->payload_offset], + nfcv_data->frame_length - ctx->payload_offset - 2, + NfcVSendFlagsSof | NfcVSendFlagsHighRate | NfcVSendFlagsEof, + ctx->send_time); + snprintf(nfcv_data->last_command, sizeof(nfcv_data->last_command), "ECHO data"); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "unsupported: %02X", + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { +#ifdef NFCV_VERBOSE + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); +#endif + } +} + +void nfcv_emu_sniff_packet( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + + if(nfcv_data->frame_length < 2) { + return; + } + + /* parse the frame data for the upcoming part 3 handling */ + ctx->flags = nfcv_data->frame[0]; + ctx->command = nfcv_data->frame[1]; + ctx->selected = (ctx->flags & NFCV_REQ_FLAG_SELECT); + ctx->addressed = !(ctx->flags & NFCV_REQ_FLAG_INVENTORY) && + (ctx->flags & NFCV_REQ_FLAG_ADDRESS); + ctx->advanced = (ctx->command >= NFCV_CMD_ADVANCED); + ctx->address_offset = 2 + (ctx->advanced ? 1 : 0); + ctx->payload_offset = ctx->address_offset + (ctx->addressed ? NFCV_UID_LENGTH : 0); + + char flags_string[5]; + + snprintf( + flags_string, + 5, + "%c%c%c%d", + (ctx->flags & NFCV_REQ_FLAG_INVENTORY) ? + 'I' : + (ctx->addressed ? 'A' : (ctx->selected ? 'S' : '*')), + ctx->advanced ? 'X' : ' ', + (ctx->flags & NFCV_REQ_FLAG_DATA_RATE) ? 'h' : 'l', + (ctx->flags & NFCV_REQ_FLAG_SUB_CARRIER) ? 2 : 1); + + switch(ctx->command) { + case NFCV_CMD_INVENTORY: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s INVENTORY", flags_string); + break; + } + + case NFCV_CMD_STAY_QUIET: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s STAYQUIET", flags_string); + nfcv_data->quiet = true; + break; + } + + case NFCV_CMD_LOCK_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK %d", + flags_string, + block); + break; + } + + case NFCV_CMD_WRITE_DSFID: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR DSFID %d", + flags_string, + id); + break; + } + + case NFCV_CMD_WRITE_AFI: { + uint8_t id = nfcv_data->frame[ctx->payload_offset]; + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WR AFI %d", + flags_string, + id); + break; + } + + case NFCV_CMD_LOCK_DSFID: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s LOCK DSFID", + flags_string); + break; + } + + case NFCV_CMD_LOCK_AFI: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s LOCK AFI", flags_string); + break; + } + + case NFCV_CMD_SELECT: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s SELECT", flags_string); + break; + } + + case NFCV_CMD_RESET_TO_READY: { + snprintf( + nfcv_data->last_command, sizeof(nfcv_data->last_command), "%s RESET", flags_string); + break; + } + + case NFCV_CMD_READ_MULTI_BLOCK: + case NFCV_CMD_READ_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + + if(ctx->command == NFCV_CMD_READ_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + } + + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s READ %d cnt: %d", + flags_string, + block, + blocks); + + break; + } + + case NFCV_CMD_WRITE_MULTI_BLOCK: + case NFCV_CMD_WRITE_BLOCK: { + uint8_t block = nfcv_data->frame[ctx->payload_offset]; + uint8_t blocks = 1; + uint8_t data_pos = 1; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + blocks = nfcv_data->frame[ctx->payload_offset + 1] + 1; + data_pos++; + } + + uint8_t* data = &nfcv_data->frame[ctx->payload_offset + data_pos]; + + if(ctx->command == NFCV_CMD_WRITE_MULTI_BLOCK) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d, cnd %d", + flags_string, + block, + blocks); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s WRITE %d %02X %02X %02X %02X", + flags_string, + block, + data[0], + data[1], + data[2], + data[3]); + } + break; + } + + case NFCV_CMD_GET_SYSTEM_INFO: { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s SYSTEMINFO", + flags_string); + break; + } + + default: + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "%s unsupported: %02X", + flags_string, + ctx->command); + break; + } + + if(strlen(nfcv_data->last_command) > 0) { + FURI_LOG_D(TAG, "Received command %s", nfcv_data->last_command); + } +} + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data) { + furi_assert(nfc_data); + furi_assert(nfcv_data); + + if(!nfcv_emu_alloc(nfcv_data)) { + FURI_LOG_E(TAG, "Failed to allocate structures"); + nfcv_data->ready = false; + return; + } + + strcpy(nfcv_data->last_command, ""); + nfcv_data->quiet = false; + nfcv_data->selected = false; + nfcv_data->modified = false; + + /* everything is initialized */ + nfcv_data->ready = true; + + /* ensure the GPIO is already in unmodulated state */ + furi_hal_gpio_init(&gpio_spi_r_mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_write(&gpio_spi_r_mosi, GPIO_LEVEL_UNMODULATED); + + rfal_platform_spi_acquire(); + /* stop operation to configure for transparent and passive mode */ + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); + /* set enable, rx_enable and field detector enable */ + st25r3916WriteRegister( + ST25R3916_REG_OP_CONTROL, + ST25R3916_REG_OP_CONTROL_en | ST25R3916_REG_OP_CONTROL_rx_en | + ST25R3916_REG_OP_CONTROL_en_fd_auto_efd); + /* explicitely set the modulation resistor in case system config changes for some reason */ + st25r3916WriteRegister( + ST25R3916_REG_PT_MOD, + (0 << ST25R3916_REG_PT_MOD_ptm_res_shift) | (15 << ST25R3916_REG_PT_MOD_pt_res_shift)); + /* target mode: target, other fields do not have any effect as we use transparent mode */ + st25r3916WriteRegister(ST25R3916_REG_MODE, ST25R3916_REG_MODE_targ); + /* let us modulate the field using MOSI, read ASK modulation using IRQ */ + st25r3916ExecuteCommand(ST25R3916_CMD_TRANSPARENT_MODE); + + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_nfc); + + /* if not set already, initialize the default protocol handler */ + if(!nfcv_data->emu_protocol_ctx) { + nfcv_data->emu_protocol_ctx = malloc(sizeof(NfcVEmuProtocolCtx)); + if(nfcv_data->sub_type == NfcVTypeSniff) { + nfcv_data->emu_protocol_handler = &nfcv_emu_sniff_packet; + } else { + nfcv_data->emu_protocol_handler = &nfcv_emu_handle_packet; + } + } + + FURI_LOG_D(TAG, "Starting NfcV emulation"); + FURI_LOG_D( + TAG, + " UID: %02X %02X %02X %02X %02X %02X %02X %02X", + nfc_data->uid[0], + nfc_data->uid[1], + nfc_data->uid[2], + nfc_data->uid[3], + nfc_data->uid[4], + nfc_data->uid[5], + nfc_data->uid[6], + nfc_data->uid[7]); + + switch(nfcv_data->sub_type) { + case NfcVTypeSlixL: + FURI_LOG_D(TAG, " Card type: SLIX-L"); + slix_l_prepare(nfcv_data); + break; + case NfcVTypeSlixS: + FURI_LOG_D(TAG, " Card type: SLIX-S"); + slix_s_prepare(nfcv_data); + break; + case NfcVTypeSlix2: + FURI_LOG_D(TAG, " Card type: SLIX2"); + slix2_prepare(nfcv_data); + break; + case NfcVTypeSlix: + FURI_LOG_D(TAG, " Card type: SLIX"); + slix_prepare(nfcv_data); + break; + case NfcVTypePlain: + FURI_LOG_D(TAG, " Card type: Plain"); + break; + case NfcVTypeSniff: + FURI_LOG_D(TAG, " Card type: Sniffing"); + break; + } + + /* allocate a 512 edge buffer, more than enough */ + nfcv_data->emu_air.reader_signal = + pulse_reader_alloc(&gpio_nfc_irq_rfid_pull, NFCV_PULSE_BUFFER); + /* timebase shall be 1 ns */ + pulse_reader_set_timebase(nfcv_data->emu_air.reader_signal, PulseReaderUnitNanosecond); + /* and configure to already calculate the number of bits */ + pulse_reader_set_bittime(nfcv_data->emu_air.reader_signal, NFCV_PULSE_DURATION_NS); + /* this IO is fed into the µC via a diode, so we need a pulldown */ + pulse_reader_set_pull(nfcv_data->emu_air.reader_signal, GpioPullDown); + + /* start sampling */ + pulse_reader_start(nfcv_data->emu_air.reader_signal); +} + +void nfcv_emu_deinit(NfcVData* nfcv_data) { + furi_assert(nfcv_data); + + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + nfcv_emu_free(nfcv_data); + + if(nfcv_data->emu_protocol_ctx) { + free(nfcv_data->emu_protocol_ctx); + nfcv_data->emu_protocol_ctx = NULL; + } + + /* set registers back to how we found them */ + st25r3916WriteRegister(ST25R3916_REG_OP_CONTROL, 0x00); + st25r3916WriteRegister(ST25R3916_REG_MODE, 0x08); + rfal_platform_spi_release(); +} + +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data); + + bool ret = false; + uint32_t frame_state = NFCV_FRAME_STATE_SOF1; + uint32_t periods_previous = 0; + uint32_t frame_pos = 0; + uint32_t byte_value = 0; + uint32_t bits_received = 0; + uint32_t timeout = timeout_ms * 1000; + bool wait_for_pulse = false; + + if(!nfcv_data->ready) { + return false; + } + +#ifdef NFCV_DIAGNOSTIC_DUMPS + uint8_t period_buffer[NFCV_DIAGNOSTIC_DUMP_SIZE]; + uint32_t period_buffer_pos = 0; +#endif + + while(true) { + uint32_t periods = pulse_reader_receive(nfcv_data->emu_air.reader_signal, timeout); + uint32_t timestamp = DWT->CYCCNT; + + /* when timed out, reset to SOF state */ + if(periods == PULSE_READER_NO_EDGE || periods == PULSE_READER_LOST_EDGE) { + break; + } + +#ifdef NFCV_DIAGNOSTIC_DUMPS + if(period_buffer_pos < sizeof(period_buffer)) { + period_buffer[period_buffer_pos++] = periods; + } +#endif + + /* short helper for detecting a pulse position */ + if(wait_for_pulse) { + wait_for_pulse = false; + if(periods != 1) { + frame_state = NFCV_FRAME_STATE_RESET; + } + continue; + } + + switch(frame_state) { + case NFCV_FRAME_STATE_SOF1: + if(periods == 1) { + frame_state = NFCV_FRAME_STATE_SOF2; + } else { + frame_state = NFCV_FRAME_STATE_SOF1; + break; + } + break; + + case NFCV_FRAME_STATE_SOF2: + /* waiting for the second low period, telling us about coding */ + if(periods == 6) { + frame_state = NFCV_FRAME_STATE_CODING_256; + periods_previous = 0; + wait_for_pulse = true; + } else if(periods == 4) { + frame_state = NFCV_FRAME_STATE_CODING_4; + periods_previous = 2; + wait_for_pulse = true; + } else { + frame_state = NFCV_FRAME_STATE_RESET; + } + break; + + case NFCV_FRAME_STATE_CODING_256: + if(periods_previous > periods) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + + if(periods > 512) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } + + periods_previous = 512 - (periods + 1); + byte_value = (periods - 1) / 2; + if(frame_pos < NFCV_FRAMESIZE_MAX) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } + + wait_for_pulse = true; + + break; + + case NFCV_FRAME_STATE_CODING_4: + if(periods_previous > periods) { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + /* previous symbol left us with some pulse periods */ + periods -= periods_previous; + periods_previous = 0; + + byte_value >>= 2; + bits_received += 2; + + if(periods == 1) { + byte_value |= 0x00 << 6; + periods_previous = 6; + } else if(periods == 3) { + byte_value |= 0x01 << 6; + periods_previous = 4; + } else if(periods == 5) { + byte_value |= 0x02 << 6; + periods_previous = 2; + } else if(periods == 7) { + byte_value |= 0x03 << 6; + periods_previous = 0; + } else if(periods == 2) { + frame_state = NFCV_FRAME_STATE_EOF; + break; + } else { + frame_state = NFCV_FRAME_STATE_RESET; + break; + } + + if(bits_received >= 8) { + if(frame_pos < NFCV_FRAMESIZE_MAX) { + nfcv_data->frame[frame_pos++] = (uint8_t)byte_value; + } + bits_received = 0; + } + wait_for_pulse = true; + break; + } + + /* post-state-machine cleanup and reset */ + if(frame_state == NFCV_FRAME_STATE_RESET) { + frame_state = NFCV_FRAME_STATE_SOF1; + } else if(frame_state == NFCV_FRAME_STATE_EOF) { + nfcv_data->frame_length = frame_pos; + nfcv_data->eof_timestamp = timestamp; + break; + } + } + + if(frame_state == NFCV_FRAME_STATE_EOF) { + /* we know that this code uses TIM2, so stop pulse reader */ + pulse_reader_stop(nfcv_data->emu_air.reader_signal); + if(tx_rx->sniff_rx) { + tx_rx->sniff_rx(nfcv_data->frame, frame_pos * 8, false, tx_rx->sniff_context); + } + nfcv_data->emu_protocol_handler(tx_rx, nfc_data, nfcv_data); + + pulse_reader_start(nfcv_data->emu_air.reader_signal); + ret = true; + + } +#ifdef NFCV_VERBOSE + else { + if(frame_state != NFCV_FRAME_STATE_SOF1) { + FURI_LOG_T(TAG, "leaving while in state: %lu", frame_state); + } + } +#endif + +#ifdef NFCV_DIAGNOSTIC_DUMPS + if(period_buffer_pos) { + FURI_LOG_T(TAG, "pulses:"); + for(uint32_t pos = 0; pos < period_buffer_pos; pos++) { + FURI_LOG_T(TAG, " #%lu: %u", pos, period_buffer[pos]); + } + } +#endif + + return ret; +} diff --git a/lib/nfc/protocols/nfcv.h b/lib/nfc/protocols/nfcv.h new file mode 100644 index 0000000000..87a6967375 --- /dev/null +++ b/lib/nfc/protocols/nfcv.h @@ -0,0 +1,291 @@ +#pragma once + +#include +#include + +#include +#include +#include "nfc_util.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* true: modulating releases load, false: modulating adds load resistor to field coil */ +#define NFCV_LOAD_MODULATION_POLARITY (false) + +#define NFCV_FC (13560000.0f) /* MHz */ +#define NFCV_RESP_SUBC1_PULSE_32 (1.0f / (NFCV_FC / 32) / 2.0f) /* 1.1799 µs */ +#define NFCV_RESP_SUBC1_UNMOD_256 (256.0f / NFCV_FC) /* 18.8791 µs */ +#define NFCV_PULSE_DURATION_NS (128.0f * 1000000000.0f / NFCV_FC) + +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum number of blocks is defined as 256 */ +#define NFCV_BLOCKS_MAX 256 +/* ISO/IEC 15693-3:2019(E) 10.4.12: maximum size of blocks is defined as 32 */ +#define NFCV_BLOCKSIZE_MAX 32 +/* the resulting memory size a card can have */ +#define NFCV_MEMSIZE_MAX (NFCV_BLOCKS_MAX * NFCV_BLOCKSIZE_MAX) +/* ISO/IEC 15693-3:2019(E) 7.1b: standard allows up to 8192, the maxium frame length that we are expected to receive/send is less */ +#define NFCV_FRAMESIZE_MAX (1 + NFCV_MEMSIZE_MAX + NFCV_BLOCKS_MAX) + +/* maximum string length for log messages */ +#define NFCV_LOG_STR_LEN 128 +/* maximum of pulses to be buffered by pulse reader */ +#define NFCV_PULSE_BUFFER 512 + +//#define NFCV_DIAGNOSTIC_DUMPS +//#define NFCV_DIAGNOSTIC_DUMP_SIZE 256 +//#define NFCV_VERBOSE + +/* helpers to calculate the send time based on DWT->CYCCNT */ +#define NFCV_FDT_USEC(usec) ((usec)*64) +#define NFCV_FDT_FC(ticks) ((ticks)*6400 / 1356) + +/* state machine when receiving frame bits */ +#define NFCV_FRAME_STATE_SOF1 0 +#define NFCV_FRAME_STATE_SOF2 1 +#define NFCV_FRAME_STATE_CODING_4 2 +#define NFCV_FRAME_STATE_CODING_256 3 +#define NFCV_FRAME_STATE_EOF 4 +#define NFCV_FRAME_STATE_RESET 5 + +/* sequences for every section of a frame */ +#define NFCV_SIG_SOF 0 +#define NFCV_SIG_BIT0 1 +#define NFCV_SIG_BIT1 2 +#define NFCV_SIG_EOF 3 +#define NFCV_SIG_LOW_SOF 4 +#define NFCV_SIG_LOW_BIT0 5 +#define NFCV_SIG_LOW_BIT1 6 +#define NFCV_SIG_LOW_EOF 7 + +/* various constants */ +#define NFCV_COMMAND_RETRIES 5 +#define NFCV_UID_LENGTH 8 + +/* ISO15693 protocol flags */ +typedef enum { + /* ISO15693 protocol flags when INVENTORY is NOT set */ + NFCV_REQ_FLAG_SUB_CARRIER = (1 << 0), + NFCV_REQ_FLAG_DATA_RATE = (1 << 1), + NFCV_REQ_FLAG_INVENTORY = (1 << 2), + NFCV_REQ_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_REQ_FLAG_SELECT = (1 << 4), + NFCV_REQ_FLAG_ADDRESS = (1 << 5), + NFCV_REQ_FLAG_OPTION = (1 << 6), + /* ISO15693 protocol flags when INVENTORY flag is set */ + NFCV_REQ_FLAG_AFI = (1 << 4), + NFCV_REQ_FLAG_NB_SLOTS = (1 << 5) +} NfcVRequestFlags; + +/* ISO15693 protocol flags */ +typedef enum { + NFCV_RES_FLAG_ERROR = (1 << 0), + NFCV_RES_FLAG_VALIDITY = (1 << 1), + NFCV_RES_FLAG_FINAL = (1 << 2), + NFCV_RES_FLAG_PROTOCOL_EXT = (1 << 3), + NFCV_RES_FLAG_SEC_LEN1 = (1 << 4), + NFCV_RES_FLAG_SEC_LEN2 = (1 << 5), + NFCV_RES_FLAG_WAIT_EXT = (1 << 6), +} NfcVRsponseFlags; + +/* flags for SYSINFO response */ +typedef enum { + NFCV_SYSINFO_FLAG_DSFID = (1 << 0), + NFCV_SYSINFO_FLAG_AFI = (1 << 1), + NFCV_SYSINFO_FLAG_MEMSIZE = (1 << 2), + NFCV_SYSINFO_FLAG_ICREF = (1 << 3) +} NfcVSysinfoFlags; + +/* ISO15693 command codes */ +typedef enum { + /* mandatory command codes */ + NFCV_CMD_INVENTORY = 0x01, + NFCV_CMD_STAY_QUIET = 0x02, + /* optional command codes */ + NFCV_CMD_READ_BLOCK = 0x20, + NFCV_CMD_WRITE_BLOCK = 0x21, + NFCV_CMD_LOCK_BLOCK = 0x22, + NFCV_CMD_READ_MULTI_BLOCK = 0x23, + NFCV_CMD_WRITE_MULTI_BLOCK = 0x24, + NFCV_CMD_SELECT = 0x25, + NFCV_CMD_RESET_TO_READY = 0x26, + NFCV_CMD_WRITE_AFI = 0x27, + NFCV_CMD_LOCK_AFI = 0x28, + NFCV_CMD_WRITE_DSFID = 0x29, + NFCV_CMD_LOCK_DSFID = 0x2A, + NFCV_CMD_GET_SYSTEM_INFO = 0x2B, + NFCV_CMD_READ_MULTI_SECSTATUS = 0x2C, + /* advanced command codes */ + NFCV_CMD_ADVANCED = 0xA0, + /* flipper zero custom command codes */ + NFCV_CMD_CUST_ECHO_MODE = 0xDE, + NFCV_CMD_CUST_ECHO_DATA = 0xDF +} NfcVCommands; + +/* ISO15693 Response error codes */ +typedef enum { + NFCV_NOERROR = 0x00, + NFCV_ERROR_CMD_NOT_SUP = 0x01, // Command not supported + NFCV_ERROR_CMD_NOT_REC = 0x02, // Command not recognized (eg. parameter error) + NFCV_ERROR_CMD_OPTION = 0x03, // Command option not supported + NFCV_ERROR_GENERIC = 0x0F, // No additional Info about this error + NFCV_ERROR_BLOCK_UNAVAILABLE = 0x10, + NFCV_ERROR_BLOCK_LOCKED_ALREADY = 0x11, // cannot lock again + NFCV_ERROR_BLOCK_LOCKED = 0x12, // cannot be changed + NFCV_ERROR_BLOCK_WRITE = 0x13, // Writing was unsuccessful + NFCV_ERROR_BLOCL_WRITELOCK = 0x14 // Locking was unsuccessful +} NfcVErrorcodes; + +typedef enum { + NfcVLockBitDsfid = 1, + NfcVLockBitAfi = 2, +} NfcVLockBits; + +typedef enum { + NfcVAuthMethodManual, + NfcVAuthMethodTonieBox, +} NfcVAuthMethod; + +typedef enum { + NfcVTypePlain = 0, + NfcVTypeSlix = 1, + NfcVTypeSlixS = 2, + NfcVTypeSlixL = 3, + NfcVTypeSlix2 = 4, + NfcVTypeSniff = 255, +} NfcVSubtype; + +typedef enum { + NfcVSendFlagsNormal = 0, + NfcVSendFlagsSof = 1 << 0, + NfcVSendFlagsCrc = 1 << 1, + NfcVSendFlagsEof = 1 << 2, + NfcVSendFlagsOneSubcarrier = 0, + NfcVSendFlagsTwoSubcarrier = 1 << 3, + NfcVSendFlagsLowRate = 0, + NfcVSendFlagsHighRate = 1 << 4 +} NfcVSendFlags; + +typedef struct { + uint8_t key_read[4]; + uint8_t key_write[4]; + uint8_t key_privacy[4]; + uint8_t key_destroy[4]; + uint8_t key_eas[4]; + uint8_t rand[2]; + bool privacy; +} NfcVSlixData; + +typedef union { + NfcVSlixData slix; +} NfcVSubtypeData; + +typedef struct { + DigitalSignal* nfcv_resp_sof; + DigitalSignal* nfcv_resp_one; + DigitalSignal* nfcv_resp_zero; + DigitalSignal* nfcv_resp_eof; +} NfcVEmuAirSignals; + +typedef struct { + PulseReader* reader_signal; + DigitalSignal* nfcv_resp_pulse; /* pulse length, fc/32 */ + DigitalSignal* nfcv_resp_half_pulse; /* half pulse length, fc/32 */ + DigitalSignal* nfcv_resp_unmod; /* unmodulated length 256/fc */ + NfcVEmuAirSignals signals_high; + NfcVEmuAirSignals signals_low; + DigitalSequence* nfcv_signal; +} NfcVEmuAir; + +typedef void (*NfcVEmuProtocolHandler)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); +typedef bool (*NfcVEmuProtocolFilter)( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data); + +/* the default ISO15693 handler context */ +typedef struct { + uint8_t flags; /* ISO15693-3 flags of the header as specified */ + uint8_t command; /* ISO15693-3 command at offset 1 as specified */ + bool selected; /* ISO15693-3 flags: selected frame */ + bool addressed; /* ISO15693-3 flags: addressed frame */ + bool advanced; /* ISO15693-3 command: advanced command */ + uint8_t address_offset; /* ISO15693-3 offset of the address in frame, if addressed is set */ + uint8_t payload_offset; /* ISO15693-3 offset of the payload in frame */ + + uint8_t response_buffer[NFCV_FRAMESIZE_MAX]; /* pre-allocated response buffer */ + NfcVSendFlags response_flags; /* flags to use when sending response */ + uint32_t send_time; /* timestamp when to send the response */ + + NfcVEmuProtocolFilter emu_protocol_filter; +} NfcVEmuProtocolCtx; + +typedef struct { + /* common ISO15693 fields, being specified in ISO15693-3 */ + uint8_t dsfid; + uint8_t afi; + uint8_t ic_ref; + uint16_t block_num; + uint8_t block_size; + uint8_t data[NFCV_MEMSIZE_MAX]; + uint8_t security_status[1 + NFCV_BLOCKS_MAX]; + bool selected; + bool quiet; + + bool modified; + bool ready; + bool echo_mode; + + /* specfic variant infos */ + NfcVSubtype sub_type; + NfcVSubtypeData sub_data; + NfcVAuthMethod auth_method; + + /* precalced air level data */ + NfcVEmuAir emu_air; + + uint8_t* frame; /* [NFCV_FRAMESIZE_MAX] ISO15693-2 incoming raw data from air layer */ + uint8_t frame_length; /* ISO15693-2 length of incoming data */ + uint32_t eof_timestamp; /* ISO15693-2 EOF timestamp, read from DWT->CYCCNT */ + + /* handler for the protocol layer as specified in ISO15693-3 */ + NfcVEmuProtocolHandler emu_protocol_handler; + void* emu_protocol_ctx; + /* runtime data */ + char last_command[NFCV_LOG_STR_LEN]; + char error[NFCV_LOG_STR_LEN]; +} NfcVData; + +typedef struct { + uint16_t blocks_to_read; + int16_t blocks_read; +} NfcVReader; + +ReturnCode nfcv_read_blocks(NfcVReader* reader, NfcVData* data); +ReturnCode nfcv_read_sysinfo(FuriHalNfcDevData* nfc_data, NfcVData* data); +ReturnCode nfcv_inventory(uint8_t* uid); +bool nfcv_read_card(NfcVReader* reader, FuriHalNfcDevData* nfc_data, NfcVData* data); + +void nfcv_emu_init(FuriHalNfcDevData* nfc_data, NfcVData* nfcv_data); +void nfcv_emu_deinit(NfcVData* nfcv_data); +bool nfcv_emu_loop( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + NfcVData* nfcv_data, + uint32_t timeout_ms); +void nfcv_emu_send( + FuriHalNfcTxRxContext* tx_rx, + NfcVData* nfcv, + uint8_t* data, + uint8_t length, + NfcVSendFlags flags, + uint32_t send_time); + +#ifdef __cplusplus +} +#endif diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c new file mode 100644 index 0000000000..ec3afc248b --- /dev/null +++ b/lib/nfc/protocols/slix.c @@ -0,0 +1,412 @@ + +#include +#include "nfcv.h" +#include "slix.h" +#include "nfc_util.h" +#include +#include "furi_hal_nfc.h" +#include + +#define TAG "SLIX" + +static uint32_t slix_read_be(uint8_t* data, uint32_t length) { + uint32_t value = 0; + + for(uint32_t pos = 0; pos < length; pos++) { + value <<= 8; + value |= data[pos]; + } + + return value; +} + +uint8_t slix_get_ti(FuriHalNfcDevData* nfc_data) { + return (nfc_data->uid[3] >> 3) & 3; +} + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 2) { + return true; + } + return false; +} + +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x01) && + slix_get_ti(nfc_data) == 1) { + return true; + } + return false; +} + +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x02)) { + return true; + } + return false; +} + +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data) { + if((nfc_data->uid[0] == 0xE0) && (nfc_data->uid[1] == 0x04) && (nfc_data->uid[2] == 0x03)) { + return true; + } + return false; +} + +ReturnCode slix_get_random(NfcVData* data) { + uint16_t received = 0; + uint8_t rxBuf[32]; + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_GET_RANDOM_NUMBER, + RFAL_NFCV_REQ_FLAG_DEFAULT, + NFCV_MANUFACTURER_NXP, + NULL, + NULL, + 0, + rxBuf, + sizeof(rxBuf), + &received); + + if(ret == ERR_NONE) { + if(received != 3) { + return ERR_PROTO; + } + if(data != NULL) { + data->sub_data.slix.rand[0] = rxBuf[2]; + data->sub_data.slix.rand[1] = rxBuf[1]; + } + } + + return ret; +} + +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id) { + furi_assert(rand); + + uint16_t received = 0; + uint8_t rxBuf[32]; + uint8_t cmd_set_pass[] = { + password_id, + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0], + data->sub_data.slix.rand[1], + data->sub_data.slix.rand[0]}; + uint8_t* password = NULL; + + switch(password_id) { + case SLIX_PASS_READ: + password = data->sub_data.slix.key_read; + break; + case SLIX_PASS_WRITE: + password = data->sub_data.slix.key_write; + break; + case SLIX_PASS_PRIVACY: + password = data->sub_data.slix.key_privacy; + break; + case SLIX_PASS_DESTROY: + password = data->sub_data.slix.key_destroy; + break; + case SLIX_PASS_EASAFI: + password = data->sub_data.slix.key_eas; + break; + default: + break; + } + + if(!password) { + return ERR_NOTSUPP; + } + + for(int pos = 0; pos < 4; pos++) { + cmd_set_pass[1 + pos] ^= password[3 - pos]; + } + + ReturnCode ret = rfalNfcvPollerTransceiveReq( + NFCV_CMD_NXP_SET_PASSWORD, + RFAL_NFCV_REQ_FLAG_DATA_RATE, + NFCV_MANUFACTURER_NXP, + NULL, + cmd_set_pass, + sizeof(cmd_set_pass), + rxBuf, + sizeof(rxBuf), + &received); + + return ret; +} + +bool slix_generic_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in, + uint32_t password_supported) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + NfcVData* nfcv_data = (NfcVData*)nfcv_data_in; + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + NfcVSlixData* slix = &nfcv_data->sub_data.slix; + + if(slix->privacy && ctx->command != NFCV_CMD_NXP_GET_RANDOM_NUMBER && + ctx->command != NFCV_CMD_NXP_SET_PASSWORD) { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "command 0x%02X ignored, privacy mode", + ctx->command); + FURI_LOG_D(TAG, "%s", nfcv_data->last_command); + return true; + } + + bool handled = false; + + switch(ctx->command) { + case NFCV_CMD_NXP_GET_RANDOM_NUMBER: { + slix->rand[0] = furi_hal_random_get(); + slix->rand[1] = furi_hal_random_get(); + + ctx->response_buffer[0] = NFCV_NOERROR; + ctx->response_buffer[1] = slix->rand[1]; + ctx->response_buffer[2] = slix->rand[0]; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 3, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "GET_RANDOM_NUMBER -> 0x%02X%02X", + slix->rand[0], + slix->rand[1]); + + handled = true; + break; + } + + case NFCV_CMD_NXP_SET_PASSWORD: { + uint8_t password_id = nfcv_data->frame[ctx->payload_offset]; + + if(!(password_id & password_supported)) { + break; + } + + uint8_t* password_xored = &nfcv_data->frame[ctx->payload_offset + 1]; + uint8_t* rand = slix->rand; + uint8_t* password = NULL; + uint8_t password_rcv[4]; + + switch(password_id) { + case SLIX_PASS_READ: + password = slix->key_read; + break; + case SLIX_PASS_WRITE: + password = slix->key_write; + break; + case SLIX_PASS_PRIVACY: + password = slix->key_privacy; + break; + case SLIX_PASS_DESTROY: + password = slix->key_destroy; + break; + case SLIX_PASS_EASAFI: + password = slix->key_eas; + break; + default: + break; + } + + if(!password) { + break; + } + + for(int pos = 0; pos < 4; pos++) { + password_rcv[pos] = password_xored[3 - pos] ^ rand[pos % 2]; + } + uint32_t pass_expect = slix_read_be(password, 4); + uint32_t pass_received = slix_read_be(password_rcv, 4); + + /* if the password is all-zeroes, just accept any password*/ + if(!pass_expect || pass_expect == pass_received) { + switch(password_id) { + case SLIX_PASS_READ: + break; + case SLIX_PASS_WRITE: + break; + case SLIX_PASS_PRIVACY: + slix->privacy = false; + nfcv_data->modified = true; + break; + case SLIX_PASS_DESTROY: + FURI_LOG_D(TAG, "Pooof! Got destroyed"); + break; + case SLIX_PASS_EASAFI: + break; + default: + break; + } + ctx->response_buffer[0] = NFCV_NOERROR; + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX OK", + password_id, + pass_received); + } else { + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "SET_PASSWORD #%02X 0x%08lX/%08lX FAIL", + password_id, + pass_received, + pass_expect); + } + handled = true; + break; + } + + case NFCV_CMD_NXP_ENABLE_PRIVACY: { + ctx->response_buffer[0] = NFCV_NOERROR; + + nfcv_emu_send( + tx_rx, nfcv_data, ctx->response_buffer, 1, ctx->response_flags, ctx->send_time); + snprintf( + nfcv_data->last_command, + sizeof(nfcv_data->last_command), + "NFCV_CMD_NXP_ENABLE_PRIVACY"); + + slix->privacy = true; + handled = true; + break; + } + } + + return handled; +} + +bool slix_l_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter( + tx_rx, + nfc_data, + nfcv_data_in, + SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_l_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_l_protocol_filter; +} + +bool slix_s_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix_s_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_s_protocol_filter; +} + +bool slix_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_EASAFI)) { + return true; + } + + return handled; +} + +void slix_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix_protocol_filter; +} + +bool slix2_protocol_filter( + FuriHalNfcTxRxContext* tx_rx, + FuriHalNfcDevData* nfc_data, + void* nfcv_data_in) { + furi_assert(tx_rx); + furi_assert(nfc_data); + furi_assert(nfcv_data_in); + + bool handled = false; + + /* many SLIX share some of the functions, place that in a generic handler */ + if(slix_generic_protocol_filter(tx_rx, nfc_data, nfcv_data_in, SLIX_PASS_ALL)) { + return true; + } + + return handled; +} + +void slix2_prepare(NfcVData* nfcv_data) { + FURI_LOG_D( + TAG, " Privacy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_privacy, 4)); + FURI_LOG_D( + TAG, " Destroy pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_destroy, 4)); + FURI_LOG_D(TAG, " EAS pass: 0x%08lX", slix_read_be(nfcv_data->sub_data.slix.key_eas, 4)); + FURI_LOG_D(TAG, " Privacy mode: %s", nfcv_data->sub_data.slix.privacy ? "ON" : "OFF"); + + NfcVEmuProtocolCtx* ctx = nfcv_data->emu_protocol_ctx; + ctx->emu_protocol_filter = &slix2_protocol_filter; +} diff --git a/lib/nfc/protocols/slix.h b/lib/nfc/protocols/slix.h new file mode 100644 index 0000000000..701fa2f820 --- /dev/null +++ b/lib/nfc/protocols/slix.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include "nfc_util.h" +#include + +#define NFCV_MANUFACTURER_NXP 0x04 + +/* ISO15693-3 CUSTOM NXP COMMANDS */ +#define NFCV_CMD_NXP_SET_EAS 0xA2 +#define NFCV_CMD_NXP_RESET_EAS 0xA3 +#define NFCV_CMD_NXP_LOCK_EAS 0xA4 +#define NFCV_CMD_NXP_EAS_ALARM 0xA5 +#define NFCV_CMD_NXP_PASSWORD_PROTECT_EAS_AFI 0xA6 +#define NFCV_CMD_NXP_WRITE_EAS_ID 0xA7 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ 0xB0 +#define NFCV_CMD_NXP_INVENTORY_PAGE_READ_FAST 0xB1 +#define NFCV_CMD_NXP_GET_RANDOM_NUMBER 0xB2 +#define NFCV_CMD_NXP_SET_PASSWORD 0xB3 +#define NFCV_CMD_NXP_WRITE_PASSWORD 0xB4 +#define NFCV_CMD_NXP_DESTROY 0xB9 +#define NFCV_CMD_NXP_ENABLE_PRIVACY 0xBA + +/* available passwords */ +#define SLIX_PASS_READ 0x01 +#define SLIX_PASS_WRITE 0x02 +#define SLIX_PASS_PRIVACY 0x04 +#define SLIX_PASS_DESTROY 0x08 +#define SLIX_PASS_EASAFI 0x10 + +#define SLIX_PASS_ALL \ + (SLIX_PASS_READ | SLIX_PASS_WRITE | SLIX_PASS_PRIVACY | SLIX_PASS_DESTROY | SLIX_PASS_EASAFI) + +bool slix_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix2_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_s_check_card_type(FuriHalNfcDevData* nfc_data); +bool slix_l_check_card_type(FuriHalNfcDevData* nfc_data); + +ReturnCode slix_get_random(NfcVData* data); +ReturnCode slix_unlock(NfcVData* data, uint32_t password_id); + +void slix_prepare(NfcVData* nfcv_data); +void slix_s_prepare(NfcVData* nfcv_data); +void slix_l_prepare(NfcVData* nfcv_data); +void slix2_prepare(NfcVData* nfcv_data); From 3226254876d993b9dc7d54a8c069b8fe5d4cf670 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 8 Jun 2023 10:16:01 +0400 Subject: [PATCH 099/102] [FL-3351] github: re-enabled f18 build (#2743) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * github: re-enabled f18 build * scripts: storage: better transfer logging * Fix PVS warnings Co-authored-by: あく --- .github/workflows/build.yml | 6 +++--- .../main/nfc/scenes/nfc_scene_nfcv_emulate.c | 2 +- lib/nfc/nfc_device.c | 2 +- lib/nfc/nfc_worker.c | 16 +++++++-------- lib/nfc/protocols/nfcv.c | 4 ++-- lib/nfc/protocols/slix.c | 2 +- scripts/flipper/storage.py | 20 +++++++++++-------- 7 files changed, 28 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e8b76a02cc..fb0f13e207 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ on: pull_request: env: - TARGETS: f7 + TARGETS: f7 f18 DEFAULT_TARGET: f7 FBT_TOOLCHAIN_PATH: /runner/_work @@ -96,8 +96,8 @@ jobs: - name: 'Copy map analyser files' if: ${{ !github.event.pull_request.head.repo.fork }} run: | - cp build/f7-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map - cp build/f7-firmware-*/firmware.elf map_analyser_files/firmware.elf + cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf.map map_analyser_files/firmware.elf.map + cp build/${DEFAULT_TARGET}-firmware-*/firmware.elf map_analyser_files/firmware.elf cp ${{ github.event_path }} map_analyser_files/event.json - name: 'Analyse map file' diff --git a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c index 3dd7c460b5..d812988bdf 100644 --- a/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_nfcv_emulate.c @@ -50,7 +50,7 @@ static void nfc_scene_nfcv_emulate_widget_config(Nfc* nfc, bool data_received) { widget_add_icon_element(widget, 0, 3, &I_NFC_dolphin_emulation_47x61); widget_add_string_multiline_element( widget, 87, 13, AlignCenter, AlignTop, FontPrimary, "Emulating\nNFC V"); - if(strcmp(nfc->dev->dev_name, "")) { + if(strcmp(nfc->dev->dev_name, "") != 0) { furi_string_printf(info_str, "%s", nfc->dev->dev_name); } else { for(uint8_t i = 0; i < data->uid_len; i++) { diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index 9646c262e5..952fca254b 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -808,7 +808,7 @@ static bool nfc_device_save_slix2_data(FlipperFormat* file, NfcDevice* dev) { return saved; } -bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { +bool nfc_device_load_slix2_data(FlipperFormat* file, NfcDevice* dev) { // -V524 bool parsed = false; NfcVSlixData* data = &dev->dev_data.nfcv_data.sub_data.slix; memset(data, 0, sizeof(NfcVSlixData)); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index 293f1ce705..974bb0a4d1 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -297,10 +297,10 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { ret = slix_unlock(nfcv_data, 4); } else { key = 0x7FFD6E5B; - key_data[0] = key >> 24; - key_data[1] = key >> 16; - key_data[2] = key >> 8; - key_data[3] = key >> 0; + key_data[0] = (key >> 24) & 0xFF; + key_data[1] = (key >> 16) & 0xFF; + key_data[2] = (key >> 8) & 0xFF; + key_data[3] = (key >> 0) & 0xFF; ret = slix_unlock(nfcv_data, 4); if(ret != ERR_NONE) { @@ -317,10 +317,10 @@ void nfc_worker_nfcv_unlock(NfcWorker* nfc_worker) { } key = 0x0F0F0F0F; - key_data[0] = key >> 24; - key_data[1] = key >> 16; - key_data[2] = key >> 8; - key_data[3] = key >> 0; + key_data[0] = (key >> 24) & 0xFF; + key_data[1] = (key >> 16) & 0xFF; + key_data[2] = (key >> 8) & 0xFF; + key_data[3] = (key >> 0) & 0xFF; ret = slix_unlock(nfcv_data, 4); } } diff --git a/lib/nfc/protocols/nfcv.c b/lib/nfc/protocols/nfcv.c index ac818b7a4b..3c37153d84 100644 --- a/lib/nfc/protocols/nfcv.c +++ b/lib/nfc/protocols/nfcv.c @@ -438,7 +438,7 @@ void nfcv_emu_send( furi_assert(nfcv); /* picked default value (0) to match the most common format */ - if(!flags) { + if(flags == NfcVSendFlagsNormal) { flags = NfcVSendFlagsSof | NfcVSendFlagsCrc | NfcVSendFlagsEof | NfcVSendFlagsOneSubcarrier | NfcVSendFlagsHighRate; } @@ -1326,7 +1326,7 @@ bool nfcv_emu_loop( bits_received += 2; if(periods == 1) { - byte_value |= 0x00 << 6; + byte_value |= 0x00 << 6; // -V684 periods_previous = 6; } else if(periods == 3) { byte_value |= 0x01 << 6; diff --git a/lib/nfc/protocols/slix.c b/lib/nfc/protocols/slix.c index ec3afc248b..1c14c0bf94 100644 --- a/lib/nfc/protocols/slix.c +++ b/lib/nfc/protocols/slix.c @@ -381,7 +381,7 @@ void slix_prepare(NfcVData* nfcv_data) { ctx->emu_protocol_filter = &slix_protocol_filter; } -bool slix2_protocol_filter( +bool slix2_protocol_filter( // -V524 FuriHalNfcTxRxContext* tx_rx, FuriHalNfcDevData* nfc_data, void* nfcv_data_in) { diff --git a/scripts/flipper/storage.py b/scripts/flipper/storage.py index f4d622bfe4..2c9c043d5a 100644 --- a/scripts/flipper/storage.py +++ b/scripts/flipper/storage.py @@ -257,12 +257,12 @@ def send_file(self, filename_from: str, filename_to: str): self.read.until(self.CLI_PROMPT) ftell = file.tell() - percent = str(math.ceil(ftell / filesize * 100)) - total_chunks = str(math.ceil(filesize / buffer_size)) - current_chunk = str(math.ceil(ftell / buffer_size)) + percent = math.ceil(ftell / filesize * 100) + total_chunks = math.ceil(filesize / buffer_size) + current_chunk = math.ceil(ftell / buffer_size) approx_speed = ftell / (time.time() - start_time + 0.0001) sys.stdout.write( - f"\r{percent}%, chunk {current_chunk} of {total_chunks} @ {approx_speed/1024:.2f} kb/s" + f"\r<{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" ) sys.stdout.flush() print() @@ -270,6 +270,7 @@ def send_file(self, filename_from: str, filename_to: str): def read_file(self, filename: str): """Receive file from Flipper, and get filedata (bytes)""" buffer_size = self.chunk_size + start_time = time.time() self.send_and_wait_eol( 'storage read_chunks "' + filename + '" ' + str(buffer_size) + "\r" ) @@ -290,10 +291,13 @@ def read_file(self, filename: str): filedata.extend(self.port.read(chunk_size)) read_size = read_size + chunk_size - percent = str(math.ceil(read_size / size * 100)) - total_chunks = str(math.ceil(size / buffer_size)) - current_chunk = str(math.ceil(read_size / buffer_size)) - sys.stdout.write(f"\r{percent}%, chunk {current_chunk} of {total_chunks}") + percent = math.ceil(read_size / size * 100) + total_chunks = math.ceil(size / buffer_size) + current_chunk = math.ceil(read_size / buffer_size) + approx_speed = read_size / (time.time() - start_time + 0.0001) + sys.stdout.write( + f"\r>{percent:3d}%, chunk {current_chunk:2d} of {total_chunks:2d} @ {approx_speed/1024:.2f} kb/s" + ) sys.stdout.flush() print() self.read.until(self.CLI_PROMPT) From e5343fdc9a44397a80221c8d26722a17082975f5 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Thu, 8 Jun 2023 01:28:08 -0700 Subject: [PATCH 100/102] Scripts: WiFi board updater (#2625) * Scripts: wifi updater * WiFi board updater: lint, process download error * WiFi board updater: auto cleanup temp dir * Scripts: fix server address --- scripts/wifi_board.py | 240 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100755 scripts/wifi_board.py diff --git a/scripts/wifi_board.py b/scripts/wifi_board.py new file mode 100755 index 0000000000..3f89ebdc65 --- /dev/null +++ b/scripts/wifi_board.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 + +from flipper.app import App +from serial.tools.list_ports_common import ListPortInfo + +import logging +import os +import tempfile +import subprocess +import serial.tools.list_ports as list_ports +import json +import requests +import tarfile + + +class UpdateDownloader: + UPDATE_SERVER = "https://update.flipperzero.one" + UPDATE_PROJECT = "/blackmagic-firmware" + UPDATE_INDEX = UPDATE_SERVER + UPDATE_PROJECT + "/directory.json" + UPDATE_TYPE = "full_tgz" + + CHANNEL_ID_ALIAS = { + "dev": "development", + "rc": "release-candidate", + "r": "release", + "rel": "release", + } + + def __init__(self): + self.logger = logging.getLogger() + + def download(self, channel_id: str, dir: str) -> bool: + # Aliases + if channel_id in self.CHANNEL_ID_ALIAS: + channel_id = self.CHANNEL_ID_ALIAS[channel_id] + + # Make directory + if not os.path.exists(dir): + self.logger.info(f"Creating directory {dir}") + os.makedirs(dir) + + # Download json index + self.logger.info(f"Downloading {self.UPDATE_INDEX}") + response = requests.get(self.UPDATE_INDEX) + if response.status_code != 200: + self.logger.error(f"Failed to download {self.UPDATE_INDEX}") + return False + + # Parse json index + try: + index = json.loads(response.content) + except Exception as e: + self.logger.error(f"Failed to parse json index: {e}") + return False + + # Find channel + channel = None + for channel_candidate in index["channels"]: + if channel_candidate["id"] == channel_id: + channel = channel_candidate + break + + # Check if channel found + if channel is None: + self.logger.error( + f"Channel '{channel_id}' not found. Valid channels: {', '.join([c['id'] for c in index['channels']])}" + ) + return False + + self.logger.info(f"Using channel '{channel_id}'") + + # Get latest version + try: + version = channel["versions"][0] + except Exception as e: + self.logger.error(f"Failed to get version: {e}") + return False + + self.logger.info(f"Using version '{version['version']}'") + + # Get changelog + changelog = None + try: + changelog = version["changelog"] + except Exception as e: + self.logger.error(f"Failed to get changelog: {e}") + + # print changelog + if changelog is not None: + self.logger.info(f"Changelog:") + for line in changelog.split("\n"): + if line.strip() == "": + continue + self.logger.info(f" {line}") + + # Find file + file_url = None + for file_candidate in version["files"]: + if file_candidate["type"] == self.UPDATE_TYPE: + file_url = file_candidate["url"] + break + + if file_url is None: + self.logger.error(f"File not found") + return False + + # Make file path + file_name = file_url.split("/")[-1] + file_path = os.path.join(dir, file_name) + + # Download file + self.logger.info(f"Downloading {file_url} to {file_path}") + with open(file_path, "wb") as f: + response = requests.get(file_url) + f.write(response.content) + + # Unzip tgz + self.logger.info(f"Unzipping {file_path}") + with tarfile.open(file_path, "r") as tar: + tar.extractall(dir) + + return True + + +class Main(App): + def init(self): + self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") + self.parser.add_argument( + "-c", "--channel", help="Channel name", default="release" + ) + self.parser.set_defaults(func=self.update) + + # logging + self.logger = logging.getLogger() + + def find_wifi_board(self) -> bool: + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + blackmagics: list[ListPortInfo] = list(list_ports.grep("blackmagic")) # type: ignore + daps: list[ListPortInfo] = list(list_ports.grep("CMSIS-DAP")) # type: ignore + + return len(blackmagics) > 0 or len(daps) > 0 + + def find_wifi_board_bootloader(self): + # idk why, but python thinks that list_ports.grep returns tuple[str, str, str] + ports: list[ListPortInfo] = list(list_ports.grep("ESP32-S2")) # type: ignore + + if len(ports) == 0: + # Blackmagic probe serial port not found, will be handled later + pass + elif len(ports) > 1: + raise Exception("More than one WiFi board found") + else: + port = ports[0] + if os.name == "nt": + port.device = f"\\\\.\\{port.device}" + return port.device + + def update(self): + try: + port = self.find_wifi_board_bootloader() + except Exception as e: + self.logger.error(f"{e}") + return 1 + + if self.args.port != "auto": + port = self.args.port + + available_ports = [p[0] for p in list(list_ports.comports())] + if port not in available_ports: + self.logger.error(f"Port {port} not found") + return 1 + + if port is None: + if self.find_wifi_board(): + self.logger.error("WiFi board found, but not in bootloader mode.") + self.logger.info("Please hold down BOOT button and press RESET button") + else: + self.logger.error("WiFi board not found") + self.logger.info( + "Please connect WiFi board to your computer, hold down BOOT button and press RESET button" + ) + return 1 + + # get temporary dir + with tempfile.TemporaryDirectory() as temp_dir: + downloader = UpdateDownloader() + + # download latest channel update + try: + if not downloader.download(self.args.channel, temp_dir): + self.logger.error(f"Cannot download update") + return 1 + except Exception as e: + self.logger.error(f"Cannot download update: {e}") + return 1 + + with open(os.path.join(temp_dir, "flash.command"), "r") as f: + flash_command = f.read() + + flash_command = flash_command.replace("\n", "").replace("\r", "") + flash_command = flash_command.replace("(PORT)", port) + + # We can't reset the board after flashing via usb + flash_command = flash_command.replace( + "--after hard_reset", "--after no_reset_stub" + ) + + args = flash_command.split(" ")[0:] + args = list(filter(None, args)) + + esptool_params = [] + esptool_params.extend(args) + + self.logger.info(f'Running command: "{" ".join(args)}" in "{temp_dir}"') + + process = subprocess.Popen( + esptool_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=temp_dir, + bufsize=1, + universal_newlines=True, + ) + + while process.poll() is None: + if process.stdout is not None: + for line in process.stdout: + self.logger.debug(f"{line.strip()}") + + if process.returncode != 0: + self.logger.error(f"Failed to flash WiFi board") + else: + self.logger.info("WiFi board flashed successfully") + self.logger.info("Press RESET button on WiFi board to start it") + + return process.returncode + + +if __name__ == "__main__": + Main()() From 5c2e7f9e08edaf04815d21c44aaf0686560c75c4 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 15:53:24 +0100 Subject: [PATCH 101/102] Auto unlock rpc for u2f --- .../main/u2f/scenes/u2f_scene_error.c | 25 ------------------- applications/main/u2f/u2f_app.c | 14 ++++------- applications/main/u2f/u2f_app_i.h | 1 - 3 files changed, 5 insertions(+), 35 deletions(-) diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 33839a61a1..36e490f1c2 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -25,31 +25,6 @@ void u2f_scene_error_on_enter(void* context) { "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); widget_add_button_element( app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); - } else if(app->error == U2fAppErrorCloseRpc) { - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->is_nsfw) { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Pull out from\nPC or phone to\nuse me like this."); - } else { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } } view_dispatcher_switch_to_view(app->view_dispatcher, U2fAppViewError); diff --git a/applications/main/u2f/u2f_app.c b/applications/main/u2f/u2f_app.c index db94a398c3..1c8db40d09 100644 --- a/applications/main/u2f/u2f_app.c +++ b/applications/main/u2f/u2f_app.c @@ -49,16 +49,12 @@ U2fApp* u2f_app_alloc() { view_dispatcher_add_view( app->view_dispatcher, U2fAppViewMain, u2f_view_get_view(app->u2f_view)); - if(furi_hal_usb_is_locked()) { - app->error = U2fAppErrorCloseRpc; - scene_manager_next_scene(app->scene_manager, U2fSceneError); + furi_hal_usb_unlock(); + if(u2f_data_check(true)) { + scene_manager_next_scene(app->scene_manager, U2fSceneMain); } else { - if(u2f_data_check(true)) { - scene_manager_next_scene(app->scene_manager, U2fSceneMain); - } else { - app->error = U2fAppErrorNoFiles; - scene_manager_next_scene(app->scene_manager, U2fSceneError); - } + app->error = U2fAppErrorNoFiles; + scene_manager_next_scene(app->scene_manager, U2fSceneError); } return app; diff --git a/applications/main/u2f/u2f_app_i.h b/applications/main/u2f/u2f_app_i.h index 2896684c37..7a06caf052 100644 --- a/applications/main/u2f/u2f_app_i.h +++ b/applications/main/u2f/u2f_app_i.h @@ -18,7 +18,6 @@ typedef enum { U2fAppErrorNoFiles, - U2fAppErrorCloseRpc, } U2fAppError; typedef enum { From f2cc1fcac1f5c3dc82c24a1096ceb6d00466d7f2 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:51:02 +0100 Subject: [PATCH 102/102] Add sanity checks for pin code validity --- applications/services/desktop/desktop.c | 8 ++++++-- applications/services/desktop/helpers/pin.c | 9 +++++++++ applications/services/desktop/helpers/pin.h | 2 ++ .../services/desktop/scenes/desktop_scene_lock_menu.c | 9 +++++---- .../scenes/desktop_settings_scene_pin_auth.c | 2 +- .../scenes/desktop_settings_scene_pin_menu.c | 3 ++- 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index b10fa53a77..9d48c580e4 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -237,7 +237,7 @@ static void desktop_clock_timer_callback(void* context) { } void desktop_lock(Desktop* desktop, bool pin_lock) { - pin_lock = pin_lock && desktop->settings.pin_code.length > 0; + pin_lock = pin_lock && desktop_pin_is_valid(&desktop->settings.pin_code); if(!furi_hal_rtc_is_flag_set(FuriHalRtcFlagLock)) { furi_hal_rtc_set_pin_fails(0); } @@ -457,7 +457,11 @@ int32_t desktop_srv(void* p) { Desktop* desktop = desktop_alloc(); - if(!DESKTOP_SETTINGS_LOAD(&desktop->settings)) { + bool ok = DESKTOP_SETTINGS_LOAD(&desktop->settings); + if(ok) { + ok = desktop_pin_is_valid(&desktop->settings.pin_code); + } + if(!ok) { memset(&desktop->settings, 0, sizeof(desktop->settings)); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); } diff --git a/applications/services/desktop/helpers/pin.c b/applications/services/desktop/helpers/pin.c index d6ab48df2c..b410015524 100644 --- a/applications/services/desktop/helpers/pin.c +++ b/applications/services/desktop/helpers/pin.c @@ -53,3 +53,12 @@ bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2) { return result; } + +bool desktop_pin_is_valid(const PinCode* pin_code) { + bool ok = pin_code->length >= MIN_PIN_SIZE && pin_code->length <= MAX_PIN_SIZE; + for(size_t i = 0; ok && i < pin_code->length; i++) { + ok = ok && (pin_code->data[i] == InputKeyUp || pin_code->data[i] == InputKeyDown || + pin_code->data[i] == InputKeyRight || pin_code->data[i] == InputKeyLeft); + } + return ok; +} diff --git a/applications/services/desktop/helpers/pin.h b/applications/services/desktop/helpers/pin.h index e5410723e5..ce9fcfec3d 100644 --- a/applications/services/desktop/helpers/pin.h +++ b/applications/services/desktop/helpers/pin.h @@ -9,3 +9,5 @@ void desktop_pin_lock_error_notify(); uint32_t desktop_pin_lock_get_fail_timeout(); bool desktop_pin_compare(const PinCode* pin_code1, const PinCode* pin_code2); + +bool desktop_pin_is_valid(const PinCode* pin_code); diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index c56fd16f2d..d1c7e48bcc 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -27,7 +27,8 @@ void desktop_scene_lock_menu_on_enter(void* context) { DESKTOP_SETTINGS_LOAD(&desktop->settings); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop); - desktop_lock_menu_set_pin_state(desktop->lock_menu, desktop->settings.pin_code.length > 0); + desktop_lock_menu_set_pin_state( + desktop->lock_menu, desktop_pin_is_valid(&desktop->settings.pin_code)); desktop_lock_menu_set_stealth_mode_state( desktop->lock_menu, furi_hal_rtc_is_flag_set(FuriHalRtcFlagStealthMode)); desktop_lock_menu_set_idx(desktop->lock_menu, 3); @@ -63,7 +64,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); if(check_pin_changed) { DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(desktop->settings.pin_code.length > 0) { + if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock_menu_set_pin_state(desktop->lock_menu, true); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); desktop_lock(desktop, true); @@ -90,7 +91,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventLockPin: desktop_scene_lock_menu_save_settings(desktop); - if(desktop->settings.pin_code.length > 0) { + if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock(desktop, true); } else { LoaderStatus status = @@ -105,7 +106,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { break; case DesktopLockMenuEventLockPinOff: desktop_scene_lock_menu_save_settings(desktop); - if(desktop->settings.pin_code.length > 0) { + if(desktop_pin_is_valid(&desktop->settings.pin_code)) { desktop_lock(desktop, true); Power* power = furi_record_open(RECORD_POWER); furi_delay_ms(500); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c index f153aac7ab..86886ead9d 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_auth.c @@ -34,7 +34,7 @@ void desktop_settings_scene_pin_auth_on_enter(void* context) { DesktopSettingsApp* app = context; DESKTOP_SETTINGS_LOAD(&app->settings); - furi_assert(app->settings.pin_code.length > 0); + furi_assert(desktop_pin_is_valid(&app->settings.pin_code)); desktop_view_pin_input_set_context(app->pin_input_view, app); desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_auth_back_callback); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c index 5721846c65..bd9dda727d 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_menu.c @@ -4,6 +4,7 @@ #include "../desktop_settings_app.h" #include "desktop_settings_scene.h" #include "desktop_settings_scene_i.h" +#include "desktop/helpers/pin.h" #define SCENE_EVENT_SET_PIN 0 #define SCENE_EVENT_CHANGE_PIN 1 @@ -19,7 +20,7 @@ void desktop_settings_scene_pin_menu_on_enter(void* context) { Submenu* submenu = app->submenu; submenu_reset(submenu); - if(!app->settings.pin_code.length) { + if(!desktop_pin_is_valid(&app->settings.pin_code)) { submenu_add_item( submenu, "Set Pin",

Fc87HT~&L9^=BZz|H0U{wlGOFDf9!RT5 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..bedf366c6ccf988ca20807c9062e7e7f7b70d9b9 GIT binary patch literal 1305 zcmV+!1?KvRP)td%uS!`N3?sl70Tq=Y_f8vQs0;XV zED)XBPReiX#sDdJNwAypTe}2E!%Bi3mEYPWz|Z5!90#}7NcLlqyRyF$(j0f5e<};0 zCiXnf))PJj@VgcxQ^qg>IOU%TJa%s#gKO{I4ERen`2OlI;e-hACFOGq+UGdanpyhd zh!MrGLQwU>SqPTdX_8FqT&Rq|IwonSQ)8_nr<(vPcE43V*Un>>xuDj1kF+*XGW7m2 z5h_SfY5_)6{wn0bMdEf^hqv5|WpkfN@}%+Map534OM)^2-~o+*G)b*bD!9^Q!o?jG z4_K5>jj4(RWdwjhQ>Dxf&9qw0b0&(G7Mh#)i249W;)uM`O5n^)per5j2#Xbv$inqQ znRfg+C)4VBBFd}C2tIKf?8()m@+)>*2NgG1+?y5~M8@T^0I-u&^ZqFCqGD1=*@Nci zB^QBX5NUkFVgRXF(ywdzZ*?BO0@&PBB;b@yt)UiANG?MfqX#AeSo~X5281<_UjaBa zOagcf=j&Rfk?Myl2ub%vQ4#}S3*gPAx%+kjr4+7MBl&4g9(6sFt$0xSXc&CO*Q4@w z0ab~M#6q585J#rU&2b+MQvj7gnRH8V2Gi7NC2$mmJQ}u(tPy0S0zV50;42>ED&@EK zLthE2CMHD!jtD#M{Ekl)f>wYdffZW12>X#_Xl*oXO@Q6w2`UA&KvMqfj3MO$Mk(lo zN+S;Jv|UVtw!h?qVOR2=~qUvR(D`zla#?~1=3FjjX;$@uZ{~Oa}A+I^}UTn`J627{ZTOhg(`EX z`O_@4V2@LRXV+`W*W8i?TM=NCPpmX@aM3L!kTQm<{-N9P&UKqo@TyX%7ciLX?>S_M zEY%7;j(!ogX#iY9~!cz8q4sMj3sPQ2dGy5OmJhgzk@Z?a zfZ6HVXhc&v>`pjHY3}kukOyB=JVewIaLT0oz2*WUWEf6>(@eStmeMb7-&$@bOJ^Vv z3?V>N=_^a3Goc;)Sn~U-R50{-xCZY|_g2R&@+=j*S&+^LIw<8@&rmWXa1)?=1ibFXJXvWloWgxRI<@+<&e+sR~C zVdb1g^j)3>RPD0r-G4!&{Fg&#D&LE$O5>Ms9}oX=Z1u~b-raxAe;)q;>SwfYfJwge P00000NkvXXu0mjfKQdP` literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_30.png new file mode 100644 index 0000000000000000000000000000000000000000..0731760dffbaa07d4e1f50c1fd65bd0eb1f6ef19 GIT binary patch literal 1677 zcmV;826Fj{P)Pk0p{ai`))_7(%Z%c13qA~ z?!K<;z5d{Rf4{x&Tl`!542k!Bulv6L%Aug2Yxj7;$MZEuWOG|s-&N3I`;Rtxx1%Lk z&hL^%|Guu=df)f`dfoT^Wyclk$>O;#+4(CS|F1!^rxjqc0O|C_N89<@m1A~)^m+nN z2Dcc2zJ^TcHO6Rj)9*9aM^c;`V7%9i!oROvPYVK+J|2d(Y7a|4UT@Ge>aqEUE5;T2 z9~oc(O}&&6Bo?s7NT1JY4Uf$~Qbo(7NUI67`?A+4R@5mSGEg-Z4JZj7n?JnrjyMVF z{M`&-9X)pz#Iw}PS%t!5^N%!fg#n@pR2ZNFZ_v#sx)V~KAK<&6zo++EHvdWi(H-j9 z!9RroqUdIZNPtQ~Ky`C5KxT(>=TW2Z0_7wI(CVHD=njnPjt+Rqemoe=8h{SS)Bx6^ zD2<^RATtKxd`Sns<7VDqyvj;F9{pHGAV zvhXrMrWhOJ#%n~lWB^pbH`C=5$7_HyKqLip1X(lz#|7? z7*Bm5t%2)mjX`52dyMsbs?PTy!T<g*NrmcQKtaJ zt{*Zad0rj8vbt-EEUZ%q{|>?c(vBKJrLpuoex*uBjLXx9lydf&f);WthOC<_u;w zwsd^>Ko31j0xZQURBo|H#9%XqLBYddU9vNHu4_(3JMx@F-&vaN*!dJc!ReZgkH;XM zDmpt>1H$=SzfLFcTpL+;CnHfv^}rKR(wbNsQz?)T&L8Ne{#}T!iKo`D-vwybF))iP z18^O7!K*{%7`p@StNb!3ijEY3+^s8j|4Lbi^!oMVQ0?lO&{8n76aEFN_5{POwDgq% zRtSrplV^>ii0o)VhLXwOBZ2Bdx@&-$GW<0JXy2cjAGyO2rw?tO6Knvf0jw0@{l3yG z=tf32t8;l$p$Mq~IK1a>$`CwF@MEB6EKAnzqu;E3UDXY-Hs~oJYjA|4I+{IQyAxGs zjJYanhSUJjB2Sw`l0VZ0y}P*_#JUjkSo2o`3VI6gwv}f^AT`>|fY#1Gt@C$c5PZf2 z)^&!+X<;}OT>v5H@}A$L^NsiPa}=`ngfM_-A<|PhdL8rdqtogXz;)?3vaZnC3rG#n zg=z{w$+u1zk-4(=jLM0~f_DPF5r`-pxm-GTCjV=Cqc_6JX9Y8q`v6*g)9%yG`Vu@H zIdm<-^{G71kkHMlQWuu~%~56msfnY3=T%Rms5=Ez9Fj=`QAFmKd@)N2%WD8DuU3%N z)iTa$$4CvH4W+HK<=+FKjJ6{MbR(j#=e|ggN@=*|-vb~jbE#ICJqxG=LbN`idhTF~ ztc|(8Wd@Lnz^db`6?|lT=P|Mq-Xf2KZzkxo09xQ7nkcIpL5PP?(RI%5_kQm^_wzbo zjDz+8q~fm120I!--IU(dw30R)b-X0ETmxwNc~?&C9y6OSTQ~7}yz=OLJT1)RT-To; zhGdi+f~?}qv&bEdAWnrDtSbB@r_#@@F?5`Ezxl%uj{~wh&l_Cyy>$wRfX|h2rdF(0 z0|U)UvtrRa1w^CFgRY&EnNu25rSV1Pwuv5187CM(s~0L zESuji*b7p0MxEZ-_wUBaz{!B?2de=r$L}cOcA?T)Tp=se2=ZX&_|uD^Q;=~cf^B{Q X1UzU}jMrLx00000NkvXXu0mjfn1~uQ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_31.png new file mode 100644 index 0000000000000000000000000000000000000000..898efdc4b8fe693c99eb9613e82bb5762b1d1645 GIT binary patch literal 1639 zcmV-t2AKJYP)OFJ=4bXJY&xJ?|e>!-lyPvB>sHvdZy8o=pu-&y$T)tzh#s*&p5yYF#~-|i5fwP5th5uK;OYkVpP zYOG~w{+VFsE0k%WHMxQeU9e>V=H`=#DuT_pjmc_=bFwHwmnW+=3Q1Z}6s|PJl)NiH2l+em#-{@b4L9=pb`XIDy4uJGysIFFVH za0`g>rKCx72L3E62MS3(ME9Bu+RqZ*R0PnNMiFr+kMS+ul*Sv9SdZ>KM**e#$r^t# zmpDWVcj~?N4Q?*wC5k*!i{>?cIipmYhZP{K0R7H_U zc}V5!j9?WGCeesgFS{%MKE^mx1)w1pP4rNnRwjKLZ;-1b0xaY4&w6HoCOZ<{><;cs zqN<=df=70)bHoN;D{Syk?h&3FPfJLu>s*hF8G3Kn!C8FAz+Warp*fGFK(53fPi*b`BpGl~78!dq2=i|2qP#i5V}{oPY~2EocYCK+?i3IT6`1xanA&Z^_>V zRyTrBctbYoFtjS!RUvPS5TP6(I#FWGQBlHG;tgT_Hh+%K2_{mFzr(I6R3Qse_3+a5 zCm_@ZSOYR0K&`B@&flBoptZJ+Xz{k)KSogwusZ@W&pD6Eo#-*J#QTlLj2z{k=)@gp z3)k9ZQngVbM}@OgI^7!`W{jd7fcO5rXa23^3^i~?*Y5-(Ilw16k-}?mjRtq3s8e_p zZaG1fQ+6F;S(H`*Xj|FJ@~f0yrEp0ePY1Q4nTFt6AHY*BNIN}4vjT7E4@3~@T=HkF zRRCwq$94AKy}RvX+1Kf8-s7_*2k^qm89WMlRK;j`BLy_@PiyKJ{=`{*;o!`t` zx!s~HuYYSuj6izHC0)nxC59)G!tEc1AUmw9R*o(n>1A}~0vbIoi&QB{^Rsdy^S7Nq ztF^n}ae_{-z7b8L=R1E-fXs=&a)Q(mFou2%RC%ulXsE4_FAMsD2z_VJ0r&{ez!M$q z9v_b)`n~{7rZT0g*MYR-$%+P@y@Bu%fC`q`VVPZ;-PzG>9SP@BRiO0?s#?XGE|2}c zff=0Y;Gy%QQ4_5a=)KA#N^De(;rnmnvI0xSzDt63Cc%o_6Ip}=&-v`Jrp+8IB z-hm8l{%+$0U(Y3U{({Q;LY;FKIu5$6B^002ovPDHLkV1lXu4UYf- literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png b/assets/dolphin/external/L1_Kaiju_128x64/frame_32.png new file mode 100644 index 0000000000000000000000000000000000000000..39f5db8a0131b28046b4450ce84eda9d9f519ba9 GIT binary patch literal 1618 zcmV-Y2CeytP)d$G==M{J<5M|M zV+}*|tM~rxqHQ%gJMSy__qI3(&}N~8Sv&e(o7u=S6x0bg?-M}yQ&|x?mq&n|@I(QW zo<&oOB=j2XD((`0s~tl3O}%AymjsygLy?)Ot09daNi?;)N9PgU4I7&ckZ4H8=l3l+0RNmph7QtqhZD?rjQ1HQ(B4Z9u{;8B zhP;hOan<1bd+(yMs(?5c)hjn%CIZZ4S`q<45-mH zx*2|DIRF&~Wf*OP&H`&zc@-9k9@X&h=edoP>+rTEIe^z_)&R7yHp_%H{ua%dEL^Ug zkP^a540ETN%MB9897S*}YhI{<*cbXBU8mTr|-`dCoHF)3`ydk_a%2$FK2j2Ab?hbh9&f zWfD~d%@I7ZbDbkLc+bKH59J==x$(4wqh8dj2yEXhUAqvfTBn47u$?(%? zXtHoeogo_mRydER4lwXW1VQ%OXkSgyS;HgI$>QNP$Y{GG3am_G->C4jU4Yg{5`H|B z_WRzV&8%kNsVVSU!Ka0IB#Ec`=)M120%jdEUZ^<%7hW2W1;jwoz-MwIvSo16u{ht7 z-v*+}t%f&bqYguNCDtzFwg?f*0iqox#u~i4hgG;Cte@u3@mfio?&GD4<^g=&UzPMA zOPREF{RBkn0Iz6A&X^|`T5*ShI>PwA170Vn!fms^wW0_>4UHIH>OXUWRfQb1Eys<< zj4b7^Xvbfg2sBwW0{%IdtJ?1t)%ps+CklOFL6QWvD2oAvjhCh}O|7X=ZRkeISBJ<&wWEsRT-4 z{j@T@vu%YFX!KS(n^eZPkj?>UL5CK3w04rP+8_|zxUNdzI;vcrJV`i5ItTFV`>C^3 z%>u5>Y)(;S9DV}Fb#zOGu_OoZV8_yMdimi8NLF#HVj^VdEIAFV7*GYg>mlWzHO!|Z zm1T7R?T}|DZpa6s&Xz@#PC5sW4DLM(km#Y1q7U+Hg)H8_G#5JM1f_oupfUf1P-ww( zD$3V>bfm#RqNn& zfLrj&CV*O7bp5NsO-hw?=~+&zq5y&y&)|7A&vJ;U5s{gI(A5e+7NW(WNPBumlfB3U z)MP+tm~{Ya;8Oh`!KdtxJ>bT*qNyh_+@Y(tWjutr=Gob-HnR_x?v=7>(CBelq@9A) zzeV7*^RE% z{Il0_JZA zxyBgJaNv3VcE7G`JkRsfLR{DNtFQK)0yi&QpOTB4s{-8GU0AXHof%9W(irq%aD6#& z#>K6}Ddl+{))4!2!zv@7*4Jp?vTh3lWXD>JugVCl>%l}1 zek3DU#Q;!tuHD7}E4>pcey+7UBuLJ(iUH==a}B5yZQY}D0E^*K8<%L=vR-zt90kLF zuV4VnzFw09aP&s?(Y(NG3|Q%v;RViL!2lNdX1^^A5HTDsv7zSX8BDDX*FtFiT|*p9+&)i^ruIKvRyEKX8YFJ=Q~mQk>Psc4MXPt?Rbr z0F>}lc)(sC*(B`W#b91wMyk?16%Jub^<9<&a2w_XqUek|Ob@smz+zMydsSgq2%ht` zpwtH{9VdNN6mJ1{<-u!GimsOH)SQFD9q|$l5&Ft1 zi!(Ug&3$@qrJe0zyP#t7AW|6;OcebSgS)6;NN#^VIxqVAR92oA!*w zIl`N0+KNyP5FJP@j8T=`RN-3>K7&ukB3wae$3d(%$ejje3FR0+*T75#kgLQ|iANNb zhjtPxP3&T)k{m$G{-fWA{i+X5Q}}5HV!^f?0Km2cTZ)L^AYl zz~U8rNwq76XY~-t0nBv(2^C7CHlOuk>$!1{E|8_w=?Lz=Jdc zc<#>l-rW&zqbva1jx|@#BcVM9n8vc=@K4AGpmD#!2udq~-SK~}4M8a^szmQN%!Y3W zAD=O5ejWgRb{GQ2uF^8~EGx^(P?=@`>cBG;pP}9r^G0hNZ$at102~T-GC^f1SRu