From c741727b96cfecb067a82313586b8065077e37d8 Mon Sep 17 00:00:00 2001 From: Anna Antonenko Date: Tue, 5 Nov 2024 13:53:24 +0300 Subject: [PATCH 01/27] I am own the JS (#3982) * fix: js app template * i am own the js * farewell --- .github/CODEOWNERS | 10 +++++----- .../system/js_app/packages/create-fz-app/package.json | 2 +- .../packages/create-fz-app/template/tsconfig.json | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aab3ff353..dcf964add 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -36,7 +36,7 @@ /applications/services/power_settings_app/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich /applications/system/storage_move_to_sd/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/applications/system/js_app/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/applications/system/js_app/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov @portasynthinca3 /applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov @gornekich @Astrrra @Skorpionm @@ -49,8 +49,8 @@ /applications/main/infrared/resources/ @skotopes @DrZlo13 @hedger @gsurkov # Documentation -/documentation/ @skotopes @DrZlo13 @hedger @gsurkov @drunkbatya -/scripts/toolchain/ @skotopes @DrZlo13 @hedger @gsurkov @drunkbatya +/documentation/ @skotopes @DrZlo13 @hedger @gsurkov +/scripts/toolchain/ @skotopes @DrZlo13 @hedger @gsurkov # Lib /lib/stm32wb_copro/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich @@ -59,11 +59,11 @@ /lib/lfrfid/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov /lib/libusb_stm32/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov /lib/mbedtls/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov -/lib/mjs/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov +/lib/mjs/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov @portasynthinca3 /lib/nanopb/ @skotopes @DrZlo13 @hedger @gsurkov @nminaylov /lib/nfc/ @skotopes @DrZlo13 @hedger @gsurkov @gornekich @Astrrra /lib/one_wire/ @skotopes @DrZlo13 @hedger @gsurkov /lib/subghz/ @skotopes @DrZlo13 @hedger @gsurkov @Skorpionm # CI/CD -/.github/workflows/ @skotopes @DrZlo13 @hedger @gsurkov @drunkbatya +/.github/workflows/ @skotopes @DrZlo13 @hedger @gsurkov diff --git a/applications/system/js_app/packages/create-fz-app/package.json b/applications/system/js_app/packages/create-fz-app/package.json index 216423396..7778104e2 100644 --- a/applications/system/js_app/packages/create-fz-app/package.json +++ b/applications/system/js_app/packages/create-fz-app/package.json @@ -1,6 +1,6 @@ { "name": "@flipperdevices/create-fz-app", - "version": "0.1.0", + "version": "0.1.1", "description": "Template package for JS apps Flipper Zero", "bin": "index.js", "type": "module", diff --git a/applications/system/js_app/packages/create-fz-app/template/tsconfig.json b/applications/system/js_app/packages/create-fz-app/template/tsconfig.json index c7b83cd5d..1e6fc6018 100644 --- a/applications/system/js_app/packages/create-fz-app/template/tsconfig.json +++ b/applications/system/js_app/packages/create-fz-app/template/tsconfig.json @@ -5,13 +5,14 @@ "module": "CommonJS", "noLib": true, "target": "ES2015", + "types": [], }, "files": [ "./node_modules/@flipperdevices/fz-sdk/global.d.ts", ], "include": [ "./**/*.ts", - "./**/*.js" + "./**/*.js", ], "exclude": [ "./node_modules/**/*", From b86b9b87b8d5977bfd25c7ac344c4db9d63fb53f Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Tue, 5 Nov 2024 11:41:03 +0000 Subject: [PATCH 02/27] JS: Add die() typedoc (#3985) * JS: Add die() to typedocs * JS: Never type for die() --------- Co-authored-by: Anna Antonenko --- applications/system/js_app/packages/fz-sdk/global.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/applications/system/js_app/packages/fz-sdk/global.d.ts b/applications/system/js_app/packages/fz-sdk/global.d.ts index d2e73f7de..ba6996f27 100644 --- a/applications/system/js_app/packages/fz-sdk/global.d.ts +++ b/applications/system/js_app/packages/fz-sdk/global.d.ts @@ -202,6 +202,13 @@ declare function chr(n: number): string | null; */ declare function require(module: string): any; +/** + * @brief Exit JavaScript with given message + * @param message The error message to show to user + * @version Added in JS SDK 0.1 + */ +declare function die(message: string): never; + /** * @brief mJS Foreign Pointer type * From 51aafd1b5ea1023616c88d2294ab411679ad8915 Mon Sep 17 00:00:00 2001 From: Anna Antonenko Date: Tue, 5 Nov 2024 14:58:19 +0300 Subject: [PATCH 03/27] fix: bump fz-sdk to 0.1.2 (#3989) --- applications/system/js_app/packages/fz-sdk/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/system/js_app/packages/fz-sdk/package.json b/applications/system/js_app/packages/fz-sdk/package.json index 4d18f3f20..3c2954c9c 100644 --- a/applications/system/js_app/packages/fz-sdk/package.json +++ b/applications/system/js_app/packages/fz-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@flipperdevices/fz-sdk", - "version": "0.1.1", + "version": "0.1.2", "description": "Type declarations and documentation for native JS modules available on Flipper Zero", "keywords": [ "flipper", From 0ef9f7d308e093f3dc45df79202c73c150756957 Mon Sep 17 00:00:00 2001 From: Ruslan Nadyrshin <110516632+rnadyrshin@users.noreply.github.com> Date: Mon, 2 Dec 2024 10:58:13 +0400 Subject: [PATCH 04/27] JS packages folder included to the doxygen's exclude list (#4021) --- documentation/doxygen/Doxyfile.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/doxygen/Doxyfile.cfg b/documentation/doxygen/Doxyfile.cfg index e01631749..3df12f08f 100644 --- a/documentation/doxygen/Doxyfile.cfg +++ b/documentation/doxygen/Doxyfile.cfg @@ -1042,6 +1042,7 @@ EXCLUDE = $(DOXY_SRC_ROOT)/lib/mlib \ $(DOXY_SRC_ROOT)/applications/plugins/dap_link/lib/free-dap \ $(DOXY_SRC_ROOT)/applications/debug \ $(DOXY_SRC_ROOT)/applications/main \ + $(DOXY_SRC_ROOT)/applications/system/js_app/packages \ $(DOXY_SRC_ROOT)/applications/settings \ $(DOXY_SRC_ROOT)/lib/micro-ecc \ $(DOXY_SRC_ROOT)/lib/ReadMe.md \ From 399bcdcc13765cff4701eb8ddb2dbeddcb5ba59f Mon Sep 17 00:00:00 2001 From: Nicolas Marie-Magdelaine PhD Date: Mon, 2 Dec 2024 08:15:29 +0100 Subject: [PATCH 05/27] Add IR command for NAD DR2 D7050 D3020 (#4020) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../resources/infrared/assets/audio.ir | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/applications/main/infrared/resources/infrared/assets/audio.ir b/applications/main/infrared/resources/infrared/assets/audio.ir index 20070bbe0..efd0382e3 100644 --- a/applications/main/infrared/resources/infrared/assets/audio.ir +++ b/applications/main/infrared/resources/infrared/assets/audio.ir @@ -4650,3 +4650,42 @@ type: parsed protocol: NECext address: 7F 01 00 00 command: 69 96 00 00 +# +# Model : NAD DR2 remote for NAD D7050 and D3020 +# +name: Power +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 25 DA 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 88 77 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 8C 73 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 94 6B 00 00 +# +name: Next +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 1A E5 00 00 +# +name: Prev +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 1D E2 00 00 +# From c3dc9e1ec2aa785f346d979e906ecef82dc2ab74 Mon Sep 17 00:00:00 2001 From: Luu <112649910+luu176@users.noreply.github.com> Date: Mon, 2 Dec 2024 08:27:16 +0100 Subject: [PATCH 06/27] Fix typo for mf_classic_key_cahce_get_next_key() function (#4015) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update mf_classic_key_cache.c * Update mf_classic_key_cache.h * Update mf_classic.c * Update nfc_scene_mf_classic_update_initial.c Co-authored-by: あく --- applications/main/nfc/helpers/mf_classic_key_cache.c | 2 +- applications/main/nfc/helpers/mf_classic_key_cache.h | 2 +- .../main/nfc/helpers/protocol_support/mf_classic/mf_classic.c | 2 +- .../main/nfc/scenes/nfc_scene_mf_classic_update_initial.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.c b/applications/main/nfc/helpers/mf_classic_key_cache.c index 1b945660c..763c4dea7 100644 --- a/applications/main/nfc/helpers/mf_classic_key_cache.c +++ b/applications/main/nfc/helpers/mf_classic_key_cache.c @@ -166,7 +166,7 @@ void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfCl } } -bool mf_classic_key_cahce_get_next_key( +bool mf_classic_key_cache_get_next_key( MfClassicKeyCache* instance, uint8_t* sector_num, MfClassicKey* key, diff --git a/applications/main/nfc/helpers/mf_classic_key_cache.h b/applications/main/nfc/helpers/mf_classic_key_cache.h index b09f4526b..50a1f5c30 100644 --- a/applications/main/nfc/helpers/mf_classic_key_cache.h +++ b/applications/main/nfc/helpers/mf_classic_key_cache.h @@ -16,7 +16,7 @@ bool mf_classic_key_cache_load(MfClassicKeyCache* instance, const uint8_t* uid, void mf_classic_key_cache_load_from_data(MfClassicKeyCache* instance, const MfClassicData* data); -bool mf_classic_key_cahce_get_next_key( +bool mf_classic_key_cache_get_next_key( MfClassicKeyCache* instance, uint8_t* sector_num, MfClassicKey* key, diff --git a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c index 4fece16be..6f7be7f4c 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c +++ b/applications/main/nfc/helpers/protocol_support/mf_classic/mf_classic.c @@ -72,7 +72,7 @@ static NfcCommand nfc_scene_read_poller_callback_mf_classic(NfcGenericEvent even uint8_t sector_num = 0; MfClassicKey key = {}; MfClassicKeyType key_type = MfClassicKeyTypeA; - if(mf_classic_key_cahce_get_next_key( + if(mf_classic_key_cache_get_next_key( instance->mfc_key_cache, §or_num, &key, &key_type)) { mfc_event->data->read_sector_request_data.sector_num = sector_num; mfc_event->data->read_sector_request_data.key = key; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c index 7c76260b4..a477a08b9 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_initial.c @@ -34,7 +34,7 @@ NfcCommand nfc_mf_classic_update_initial_worker_callback(NfcGenericEvent event, uint8_t sector_num = 0; MfClassicKey key = {}; MfClassicKeyType key_type = MfClassicKeyTypeA; - if(mf_classic_key_cahce_get_next_key( + if(mf_classic_key_cache_get_next_key( instance->mfc_key_cache, §or_num, &key, &key_type)) { mfc_event->data->read_sector_request_data.sector_num = sector_num; mfc_event->data->read_sector_request_data.key = key; From 2f30817dc9a052a9d033b53c6f32de6b67d74b0c Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 2 Dec 2024 07:54:50 +0000 Subject: [PATCH 07/27] Settings: More granularity for LCD and Notification (#4010) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../notification_settings_app.c | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/applications/settings/notification_settings/notification_settings_app.c b/applications/settings/notification_settings/notification_settings_app.c index ada2bfdd4..2462b32bd 100644 --- a/applications/settings/notification_settings/notification_settings_app.c +++ b/applications/settings/notification_settings/notification_settings_app.c @@ -20,8 +20,11 @@ static const NotificationSequence sequence_note_c = { NULL, }; -#define CONTRAST_COUNT 11 +#define CONTRAST_COUNT 17 const char* const contrast_text[CONTRAST_COUNT] = { + "-8", + "-7", + "-6", "-5", "-4", "-3", @@ -33,8 +36,14 @@ const char* const contrast_text[CONTRAST_COUNT] = { "+3", "+4", "+5", + "+6", + "+7", + "+8", }; const int32_t contrast_value[CONTRAST_COUNT] = { + -8, + -7, + -6, -5, -4, -3, @@ -46,44 +55,47 @@ const int32_t contrast_value[CONTRAST_COUNT] = { 3, 4, 5, + 6, + 7, + 8, }; -#define BACKLIGHT_COUNT 5 +#define BACKLIGHT_COUNT 21 const char* const backlight_text[BACKLIGHT_COUNT] = { - "0%", - "25%", - "50%", - "75%", - "100%", + "0%", "5%", "10%", "15%", "20%", "25%", "30%", "35%", "40%", "45%", "50%", + "55%", "60%", "65%", "70%", "75%", "80%", "85%", "90%", "95%", "100%", }; const float backlight_value[BACKLIGHT_COUNT] = { - 0.0f, - 0.25f, - 0.5f, - 0.75f, - 1.0f, + 0.00f, 0.05f, 0.10f, 0.15f, 0.20f, 0.25f, 0.30f, 0.35f, 0.40f, 0.45f, 0.50f, + 0.55f, 0.60f, 0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.00f, }; -#define VOLUME_COUNT 5 +#define VOLUME_COUNT 21 const char* const volume_text[VOLUME_COUNT] = { - "0%", - "25%", - "50%", - "75%", - "100%", + "0%", "5%", "10%", "15%", "20%", "25%", "30%", "35%", "40%", "45%", "50%", + "55%", "60%", "65%", "70%", "75%", "80%", "85%", "90%", "95%", "100%", +}; +const float volume_value[VOLUME_COUNT] = { + 0.00f, 0.05f, 0.10f, 0.15f, 0.20f, 0.25f, 0.30f, 0.35f, 0.40f, 0.45f, 0.50f, + 0.55f, 0.60f, 0.65f, 0.70f, 0.75f, 0.80f, 0.85f, 0.90f, 0.95f, 1.00f, }; -const float volume_value[VOLUME_COUNT] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f}; -#define DELAY_COUNT 6 +#define DELAY_COUNT 11 const char* const delay_text[DELAY_COUNT] = { "1s", "5s", + "10s", "15s", "30s", "60s", + "90s", "120s", + "5min", + "10min", + "30min", }; -const uint32_t delay_value[DELAY_COUNT] = {1000, 5000, 15000, 30000, 60000, 120000}; +const uint32_t delay_value[DELAY_COUNT] = + {1000, 5000, 10000, 15000, 30000, 60000, 90000, 120000, 300000, 600000, 1800000}; #define VIBRO_COUNT 2 const char* const vibro_text[VIBRO_COUNT] = { From 8629c61dc91f843577d9214b4a60b9b607fe1914 Mon Sep 17 00:00:00 2001 From: mxcdoam <72457810+mxcdoam@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:08:17 +0300 Subject: [PATCH 08/27] NFC: Plantain parser Last payment amount fix (#3998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../main/nfc/plugins/supported_cards/plantain.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/plantain.c b/applications/main/nfc/plugins/supported_cards/plantain.c index 9f2491691..49bbaebe8 100644 --- a/applications/main/nfc/plugins/supported_cards/plantain.c +++ b/applications/main/nfc/plugins/supported_cards/plantain.c @@ -310,9 +310,11 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { last_payment_date.year, last_payment_date.hour, last_payment_date.minute); - //payment amount. This needs to be investigated more, currently it shows incorrect amount on some cards. - uint16_t last_payment = (data->block[18].data[9] << 8) | data->block[18].data[8]; - furi_string_cat_printf(parsed_data, "Amount: %d rub", last_payment / 100); + //Last payment amount. + uint16_t last_payment = ((data->block[18].data[10] << 16) | + (data->block[18].data[9] << 8) | (data->block[18].data[8])) / + 100; + furi_string_cat_printf(parsed_data, "Amount: %d rub", last_payment); furi_string_free(card_number_s); furi_string_free(tmp_s); //This is for 4K Plantains. @@ -369,9 +371,11 @@ static bool plantain_parse(const NfcDevice* device, FuriString* parsed_data) { last_payment_date.year, last_payment_date.hour, last_payment_date.minute); - //payment amount - uint16_t last_payment = (data->block[18].data[9] << 8) | data->block[18].data[8]; - furi_string_cat_printf(parsed_data, "Amount: %d rub", last_payment / 100); + //Last payment amount + uint16_t last_payment = ((data->block[18].data[10] << 16) | + (data->block[18].data[9] << 8) | (data->block[18].data[8])) / + 100; + furi_string_cat_printf(parsed_data, "Amount: %d rub", last_payment); furi_string_free(card_number_s); furi_string_free(tmp_s); } From 3d6fd9b00d216f744a69472341e1bca89fa7a83c Mon Sep 17 00:00:00 2001 From: ted-logan Date: Mon, 2 Dec 2024 01:51:57 -0800 Subject: [PATCH 09/27] nfc/clipper: (#3991) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nfs/clipper: BART station ids for San Lorenzo, Bay Fair Update the BART station ids for the San Lorenzo and Bay Fair stations in the East Bay of the San Francisco Bay Area. * nfc/clipper: Update MUNI station ids Add MUNI station ids for the T line "Central Subway" project, which opened three new underground stations in 2022. --------- Co-authored-by: あく --- applications/main/nfc/plugins/supported_cards/clipper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/plugins/supported_cards/clipper.c b/applications/main/nfc/plugins/supported_cards/clipper.c index 3eba82425..eb459d419 100644 --- a/applications/main/nfc/plugins/supported_cards/clipper.c +++ b/applications/main/nfc/plugins/supported_cards/clipper.c @@ -101,7 +101,8 @@ static const IdMapping bart_zones[] = { {.id = 0x001d, .name = "Lake Merrit"}, {.id = 0x001e, .name = "Fruitvale"}, {.id = 0x001f, .name = "Coliseum"}, - {.id = 0x0021, .name = "San Leandro"}, + {.id = 0x0020, .name = "San Leandro"}, + {.id = 0x0021, .name = "Bay Fair"}, {.id = 0x0022, .name = "Hayward"}, {.id = 0x0023, .name = "South Hayward"}, {.id = 0x0024, .name = "Union City"}, @@ -131,6 +132,9 @@ static const IdMapping muni_zones[] = { {.id = 0x000b, .name = "Castro"}, {.id = 0x000c, .name = "Forest Hill"}, // Guessed {.id = 0x000d, .name = "West Portal"}, + {.id = 0x0019, .name = "Union Square/Market Street"}, + {.id = 0x001a, .name = "Chinatown - Rose Pak"}, + {.id = 0x001b, .name = "Yerba Buena/Moscone"}, }; static const size_t kNumMUNIZones = COUNT(muni_zones); From 7d5358b9d3b0d6493a8be3d0e76dfd58de913493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Nov=C3=BD?= Date: Tue, 3 Dec 2024 07:30:25 +0100 Subject: [PATCH 10/27] Replace mf_classic_dict.nfc with Proxmark3 version (#4013) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../resources/nfc/assets/mf_classic_dict.nfc | 3712 ++++++++++++----- 1 file changed, 2556 insertions(+), 1156 deletions(-) diff --git a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc index e2b74f847..0a3de18af 100644 --- a/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc +++ b/applications/main/nfc/resources/nfc/assets/mf_classic_dict.nfc @@ -1,126 +1,82 @@ -# Key dictionary from https://github.com/ikarus23/MifareClassicTool.git - -# More well known keys! -# Standard keys +# Key dictionary from https://github.com/RfidResearchGroup/proxmark3/ +# +# Mifare Default Keys +# -- iceman fork version -- +# -- contribute to this list, sharing is caring -- +# +# Default key FFFFFFFFFFFF -A0A1A2A3A4A5 -D3F7D3F7D3F7 +# +# Blank key 000000000000 - -# Keys from mfoc +# +# NFC Forum MADkey +A0A1A2A3A4A5 +# +# MAD access key A (reversed) +A5A4A3A2A1A0 +# +# MAD access key B +89ECA97F8C2A +# +# Mifare 1k EV1 (S50) hidden blocks, Signature data +# 16 A +5C8FF9990DA2 +# +# 17 A +75CCB59C9BED +# +# 16 B +D01AFEEB890A +# +# 17 B +4B791BEA7BCC +# +# QL88 keys +# 17 A/B +2612C6DE84CA +707B11FC1481 +# +# QL88 diversifed +03F9067646AE +2352C5B56D85 +# B0B1B2B3B4B5 +C0C1C2C3C4C5 +D0D1D2D3D4D5 +AABBCCDDEEFF 4D3A99C351DD 1A982C7E459A -AABBCCDDEEFF +# +# key A Wien +D3F7D3F7D3F7 +# +# key B Wien +5A1B85FCE20A +# +# 714C5C886E97 587EE5F9350F A0478CC39091 533CB6C723F6 8FD0A4F256E9 - -# Keys from: -# http://pastebin.com/wcTHXLZZ -A64598A77478 -26940B21FF5D -FC00018778F7 -00000FFE2488 -5C598C9C58B5 -E4D2770A89BE - -# Keys from: -# http://pastebin.com/svGjN30Q -434F4D4D4F41 -434F4D4D4F42 -47524F555041 -47524F555042 -505249564141 -505249564142 - -# Keys from: -# http://pastebin.com/d7sSetef -0297927C0F77 -EE0042F88840 -722BFCC5375F -F1D83F964314 - -# Keys from: -# http://pastebin.com/pvJX0xVS -54726176656C -776974687573 -4AF9D7ADEBE4 -2BA9621E0A36 - -# Keys from: -# http://pastebin.com/y3PDBWR1 -000000000001 -123456789ABC -B127C6F41436 -12F2EE3478C1 -34D1DF9934C5 -55F5A5DD38C9 -F1A97341A9FC -33F974B42769 -14D446E33363 -C934FE34D934 -1999A3554A55 -27DD91F1FCF1 -A94133013401 -99C636334433 -43AB19EF5C31 -A053A292A4AF -505249565441 -505249565442 - -# Keys from: -# http://pastebin.com/TUXj17K3 -FC0001877BF7 - -# Keys from: -# http://0x9000.blogspot.com/2010/12/mifare-classic-default-keys.html -A0B0C0D0E0F0 -A1B1C1D1E1F1 - -# Keys from: -# https://code.google.com/p/mifare-key-cracker/downloads/list -BD493A3962B6 -010203040506 -111111111111 -222222222222 -333333333333 -444444444444 -555555555555 -666666666666 -777777777777 -888888888888 -999999999999 -AAAAAAAAAAAA -BBBBBBBBBBBB -CCCCCCCCCCCC -DDDDDDDDDDDD -EEEEEEEEEEEE -0123456789AB - -# Keys from: -# https://github.com/4ZM/mfterm/blob/master/dictionary.txt -000000000002 -00000000000A -00000000000B -100000000000 -200000000000 -A00000000000 -B00000000000 - -# Key from: -# ladyada.net -ABCDEF123456 - -# Key from: -# http://irq5.io/2013/04/13/decoding-bcard-conference-badges/ -F4A9EF2AFC6D - -# Keys from: -# https://github.com/iceman1001/proxmark -4B0B20107CCB +# +# iCopy-X +E00000000000 +# +# +E7D6064C5860 +B27CCAB30DBD +# +# lib / Nat Bieb +D2ECE8B9395E +# NSCP default key +1494E81663D7 +# +# NFC tools +7c9fb8474242 +# +# Kiev keys 569369C5A0E5 632193BE1C3C 644672BD4AFE @@ -129,410 +85,926 @@ F4A9EF2AFC6D B5FF67CBA951 EFF603E1EFE9 F14EE7CAE863 +# +# ICT S14 A/B +9C28A60F7249 +C9826AF02794 +# +# RKF +# Västtrafiken KeyA, RKF ÖstgötaTrafiken KeyA +FC00018778F7 +# +# Västtrafiken KeyA +0297927C0F77 +54726176656C +# +# Västtrafiken KeyB +00000FFE2488 +776974687573 +EE0042F88840 +# +# RKF SLKeyA +26940B21FF5D +A64598A77478 +# +# RKF SLKeyB +5C598C9C58B5 +E4D2770A89BE +# +# RKF Rejskort Danmark KeyA +722BFCC5375F +# +# RKF Rejskort Danmark KeyB +F1D83F964314 +# +# RKF JOJOPRIVA KeyA +505249564141 +# +# RKF JOJOPRIVA KeyB +505249564142 +# +# RKF JOJOGROUP KeyA +47524F555041 +434F4D4D4F41 +# +# RKF JOJOGROUP KeyB +47524F555042 +434F4D4D4F42 +# +# TNP3xxx +4B0B20107CCB +# +# Access control system +605F5E5D5C5B +# +# NSP Global keys A and B (uk housing access control) +199404281970 +199404281998 +# +# Data from http://www.proxmark.org/forum/viewtopic.php?pid=25925#p25925 +# Tengo Cards Key A +FFF011223358 +FF9F11223358 +# +# Elevator system Kherson, Ukraine +AC37E76385F5 +576DCFFF2F25 +# +# Car wash system +1EE38419EF39 +26578719DCD9 +# +# more Keys from mfc_default_keys.lua +000000000001 +000000000002 +00000000000A +00000000000B +010203040506 +0123456789AB +100000000000 +111111111111 +123456789ABC +12F2EE3478C1 +14D446E33363 +1999A3554A55 +200000000000 +222222222222 +27DD91F1FCF1 +# +# Hotel system +505209016A1F +# +# Directory and eventlog KeyB +2BA9621E0A36 +# +# Directory and eventlog KeyA +4AF9D7ADEBE4 +# +# +333333333333 +33F974B42769 +34D1DF9934C5 +43AB19EF5C31 +444444444444 +505249565441 +505249565442 +555555555555 +55F5A5DD38C9 +666666666666 +777777777777 +888888888888 +999999999999 +99C636334433 +A00000000000 +A053A292A4AF +A94133013401 +AAAAAAAAAAAA +# +# Key from ladyada.net +ABCDEF123456 +# +# +B00000000000 +B127C6F41436 +BBBBBBBBBBBB +BD493A3962B6 +C934FE34D934 +CCCCCCCCCCCC +DDDDDDDDDDDD +EEEEEEEEEEEE +# +# elevator +# data from forum +FFFFFF545846 +# +# +F1A97341A9FC +# +# hotel system 44AB09010845 85FED980EA5A -314B49474956 -564C505F4D41 -0263DE1278F3 -067DB45454A9 -15FC4C7613FE -16F21A82EC84 -16F3D5AB1139 -17758856B182 -1FC235AC1309 -22C1BAE1AACD -243F160918D1 -25094DF6F148 -2A3C347A1200 -324F5DF65310 -32AC3B90AC13 -35C3D2CAEE88 -3A42F33AF429 -3DF14C8000A1 -3E3554AF0E12 -3E65E4FB65B3 -454841585443 -460722122510 -48FFE71294A0 -491CDCFB7752 -4AD1E273EAF1 -4B791BEA7BCC -51284C3686A6 -528C9DFFE28C -5EB8F884C8D1 -5F146716E373 -6338A371C0ED -63F17A449AF0 -643FB6DE2217 -64E3C10394C2 -682D401ABB09 -68D30288910A -693143F10368 -6A470D54127C -740E9A4F9AAF -75CCB59C9BED -75D8690F21B6 -75EDE6A84460 -82F435DEDF01 -85675B200017 -871B8C085997 -937A4FFF3011 -97184D136233 -97D1101F18B0 -9AFC42372AF1 -A27D3804C259 -A3F97428DD01 -A8966C7CC54B +# +# ARD (fr) key A +43454952534E +# ARD (fr) key B +4A2B29111213 +# +# +4143414F5250 +# +# Tehran Railway +A9B43414F585 +1FB235AC1388 +# +# Data from http://irq5.io/2013/04/13/decoding-bcard-conference-badges/ +# BCARD KeyB +F4A9EF2AFC6D +# +# +# S0 B +89EAC97F8C2A +# +# S4 A +43C7600DEE6B +# +# S6 A +0120BF672A64 +# +# S6 B +FB0B20DF1F34 +# +# A9F953DEF0A3 -AAFB06045877 -AC0E24C75527 -AE3FF4EEA0DB -B0C9DD55DD4D -B736412614AF -C4652C54261C -C6AD00254562 -C82EC29E3235 -D39BB83F5297 -D49E2826664F -DF27A8F1CB8E -E2C42591368A -E3429281EFC1 -E444D53D359F +# +# Data from forum +74A386AD0A6D +3F7A5C2DBD81 +21EDF95E7433 +C121FF19F681 +3D5D9996359A +# +# Here be BIP keys... +3A42F33AF429 +1FC235AC1309 +6338A371C0ED +243F160918D1 F124C2578AD0 +9AFC42372AF1 +32AC3B90AC13 +682D401ABB09 +4AD1E273EAF1 +067DB45454A9 +E2C42591368A +15FC4C7613FE +2A3C347A1200 +68D30288910A +16F3D5AB1139 F59A36A2546D -FEE470A4CB58 -0000000018DE -0000014B5C31 -003003003003 -003CC420001A -013889343891 -01FA3FC68349 -021209197591 -050908080008 -0A7932DC7E65 -0C669993C776 -0C71BCFB7E72 -0D258FE90296 +937A4FFF3011 +64E3C10394C2 +35C3D2CAEE88 +B736412614AF +693143F10368 +324F5DF65310 +A3F97428DD01 +643FB6DE2217 +63F17A449AF0 +82F435DEDF01 +C4652C54261C +0263DE1278F3 +D49E2826664F +51284C3686A6 +3DF14C8000A1 +6A470D54127C +# +# Data from http://pastebin.com/AK9Bftpw +# Länstrafiken i Västerbotten +48FFE71294A0 +E3429281EFC1 +16F21A82EC84 +460722122510 +# +# 3dprinter +# EPI Envisionte +AAFB06045877 +# +# Gyms / Fitness Clubs / Health Clubs / Wellness Centres +# +# Fysiken A +3E65E4FB65B3 +# +# Fysiken B +25094DF6F148 +# +# +# https://mattionline.de/fitnessstudio-armband-reverse-engineering/ +# https://mattionline.de/milazycracker/ +# Gym Wristband A - Same as Fysiken A +# Gym Wristband B +81CC25EBBB6A +195DC63DB3A3 +# +# CleverFit +A05DBD98E0FC +# +# GoFit +AA4DDA458EBB +EAB8066C7479 +# +# Nordic Wellness A - Same as Fysiken A +# Nordic Wellness B +E5519E1CC92B +# +# Jett's 24 Hour Fitness S0 KA/B +# 049979614077 +# 829338771705 +# +# Hotel KeyCard +D3B595E9DD63 +AFBECD121004 +# +# SimonsVoss +6471A5EF2D1A +# +# ID06 +4E3552426B32 +22BDACF5A33F +6E7747394E63 +763958704B78 +# +# Onity S1 A/B +8A19D40CF2B5 +# +3961EA82C46D +# +# 24-7 +D21762B2DE3B 0E83A374B513 -0F230695923F +1F1FFE000000 +A10F303FC879 +1322285230B8 +0C71BCFB7E72 +C3C88C6340B8 +F101622750B7 +1F107328DC8D +710732200D34 +7C335FB121B5 +B39AE17435DC +# +# key A +454841585443 +# +# Lift system +190819842023 +# +# Data from http://pastebin.com/gQ6nk38G +D39BB83F5297 +85675B200017 +528C9DFFE28C +C82EC29E3235 +3E3554AF0E12 +491CDCFB7752 +22C1BAE1AACD +5F146716E373 +740E9A4F9AAF +AC0E24C75527 +97184D136233 +E444D53D359F +17758856B182 +A8966C7CC54B +C6AD00254562 +AE3FF4EEA0DB +5EB8F884C8D1 +FEE470A4CB58 +75D8690F21B6 +871B8C085997 +97D1101F18B0 +75EDE6A84460 +DF27A8F1CB8E +B0C9DD55DD4D +# +# Data from http://bit.ly/1bdSbJl +A0B0C0D0E0F0 +A1B1C1D1E1F1 +# +# Data from msk social +2735FC181807 +2ABA9519F574 +84FD7F7A12B6 +186D8C4B93F9 +3A4BBA8ADAF0 +8765B17968A2 +40EAD80721CE +0DB5E6523F7C +51119DAE5216 +83E3549CE42D +136BDB246CAC +7DE02A7F6025 +BF23A53C1F63 +CB9A1F2D7368 +C7C0ADB3284F +9F131D8C2057 +67362D90F973 +6202A38F69E2 +100533B89331 +653A87594079 +D8A274B2E026 +B20B83CB145C +9AFA6CB4FC3D +A229E68AD9E5 +49C2B5296EF4 +# +# Data from http://pastebin.com/RRJUEDCM +0D258FE90296 +E55A3CA71826 +A4F204203F56 +EEB420209D0C +911E52FD7CE4 +752FBB5B7B45 +66B03ACA6EE9 +48734389EDC3 +17193709ADF4 +1ACC3189578C +C2B7EC7D4EB1 +369A4663ACD2 +# +# Data from https://github.com/zhangjingye03/zxcardumper +# zxcard Key A/B +668770666644 +003003003003 +# +# Data from http://phreakerclub.com/forum/showthread.php?p=41266 +26973EA74321 +71F3A315AD26 +51044EFB5AAB +AC70CA327A04 +EB0A8FF88ADE +# +# Transport system Metromoney +2803BCB0C7E1 +9C616585E26D +4FA9EB49F75E +2DADE48942C5 +A160FCD5EC4C +112233445566 +361A62F35BC9 +# +# Transport system Spain +83F3CB98C258 +070D486BC555 +A9B018868CC1 +9DCDB136110C +749934CC8ED3 +506DB955F161 +F088A85E71D7 +72B458D60363 +70C714869DC7 +B32464412EE3 +F253C30568C4 +1C68315674AC +CFE63749080A +C1E6F8AFC9EC +DD0DE3BA08A6 +3D923EB73534 +FF94F86B09A6 +D61707FFDFB1 +8223205047B6 +9951A273DEE7 +C9449301AF93 +66695A45C9FA +89AA9D743812 +C41514DEFC07 +C52876869800 +5353B3AECB53 +2E4169A5C79D +4BB747E48C2A +6285A1C8EB5C +5145C34DBA19 +25352912CD8D +81B20C274C3F +00B70875AF1D +04B787B2F3A5 +05412723F1B6 +05C301C8795A +066F5AF3CCEE +0A1B6C50E04E +0AD0956DF6EE +0AD6B7E37183 +0F3A4D48757B +1417E5671417 +18AB07270506 +18E887D625B4 +1ABC15934F5A +1AF66F83F5BE +260480290483 +2900AAC52BC3 +2910AFE15C99 +374521A38BCC +3A4C47757B07 +3A524B7A7B37 +3C4ABB877EAF +3F3A534B7B7B +4B787B273A50 +4B92DF1BF25D +4F0E4AE8051A +514B797B2F3A +529CF51F05C5 +52B26C199862 +57A18BFEC381 +5A7D87876EA8 +64CBADC7A313 +65B6C3200736 +67B1B3A4E497 +6B0454D5D3C3 +6B3B7AF45777 +6C273F431564 +702C1BF025DD +738385948494 +76E450094393 +777B1F3A4F4A +7B173A4E4976 +81504133B13C +826576A1AB68 +8A55194F6587 +8DFACF11E778 +8FD6D76742DC +9AFEE1F65742 +9D56D83658AC +9FAC23197904 +A1AB3A08712C +A514B797B373 +A58AB5619631 +A5BB18152EF1 +A777B233A4F4 +AB19BC885A29 +AB91BDA25F00 +AE98BA1E6F2C +B133A4D48757 +B3A4C47757B0 +B6803136F5AF +B793ADA6DB0C +B95BFDEBA7E4 +C0AA2BBD27CD +C27F5C1A9C2B +C9BE49675FE4 +CCCE24102003 +CDE668FDCDBA +D23A31A4AAB9 +DEDD7688BC38 +E9AE90885C39 +F0A3C5182007 +F3A524B7A7B3 +# +# Data from mall +# playland balikesir +ABBA1234FCB0 +# +# A trio bowling bahcelievler +314F495254FF +4152414B4E41 +# +# karinca park nigde +4E474434FFFF +# +# hotel system +537930363139 +# +# Data from https://github.com/RadioWar/NFCGUI +44DD5A385AAF +21A600056CB0 +B1ACA33180A5 +DD61EB6BCE22 +1565A172770F +3E84D2612E2A +F23442436765 +79674F96C771 +87DF99D496CB +C5132C8980BC +A21680C27773 +F26E21EDCEE2 +675557ECC92E +F4396E468114 +6DB17C16B35B +4186562A5BB2 +2FEAE851C199 +DB1A3338B2EB +157B10D84C6B +A643F952EA57 +DF37DCB6AFB3 +4C32BAF326E0 +91CE16C07AC5 +3C5D1C2BCD18 +C3F19EC592A2 +F72A29005459 +185FA3438949 +321A695BD266 +D327083A60A7 +45635EF66EF3 +5481986D2D62 +CBA6AE869AD5 +645A166B1EEB +A7ABBC77CC9E +F792C4C76A5C +BFB6796A11DB +# +# Data from Salto A/B +6A1987C40A21 +7F33625BC129 +6BE9314930D8 +# +# Data from forum +2338B4913111 +# +# Data from stoye +CB779C50E1BD +A27D3804C259 +003CC420001A +F9861526130F +381ECE050FBD +A57186BDD2B9 +48C739E21A04 +36ABF5874ED7 +649D2ABBBD20 +BBE8FFFCF363 +AB4E7045E97D +340E40F81CD8 +E4F65C0EF32C +D2A597D76936 +A920F32FE93A +86AFD95200F7 +9B832A9881FF +26643965B16E +0C669993C776 +B468D1991AF9 +D9A37831DCE5 +2FC1F32F51B1 0FFBF65B5A14 +C5CFE06D9EA3 +C0DECE673829 +# +# +A56C2DF9A26D +# +# Data from https://pastebin.com/vbwast74 +68D3F7307C89 +# +# Smart Rider. Western Australian Public Transport Cards +568C9083F71C +117E5C165B10 +24BB421C7973 +3E3A546650EA +41F262D3AB66 +514956AB3142 +863933AE8388 +# +# Bangkok metro key +97F5DA640B18 +# +# Metro Valencia key +A8844B0BCA06 +# +# HTC Eindhoven key +857464D3AAD1 +# +# Vigik Keys +# Various sources : +# * https://github.com/DumpDos/Vigik +# * http://newffr.com/viewtopic.php?&forum=235&topic=11559 +# * Own dumps +# +# French VIGIK +# VIGIK1 A +314B49474956 +# +# VIGIK1 B +564C505F4D41 +BA5B895DA162 +# +# BTCINO UNDETERMINED SPREAKD 0x01->0x13 key +021209197591 +# +# +2EF720F2AF76 +414C41524F4E +424C41524F4E +4A6352684677 +BF1F4424AF76 +536653644C65 +# +# Intratone Cogelec +# Data from http://bouzdeck.com/rfid/32-cloning-a-mifare-classic-1k-tag.html +484558414354 +A22AE129C013 +49FAE4E3849F +38FCF33072E0 +8AD5517B4B18 +509359F131B1 +6C78928E1317 +AA0720018738 +A6CAC2886412 +62D0C424ED8E +E64A986A5D94 +8FA1D601D0A2 +89347350BD36 +66D2B7DC39EF +6BC1E1AE547D +22729A9BD40F +# +# Data from https://dfir.lu/blog/cloning-a-mifare-classic-1k-tag.html +925B158F796F +FAD63ECB5891 +BBA840BA1C57 +CC6B3B3CD263 +6245E47352E6 +8ED41E8B8056 +2DD39A54E1F3 +6D4C5B3658D2 +1877ED29435A +52264716EFDE +961C0DB4A7ED +703140FD6D86 +157C9A513FA5 +E2A5DC8E066F +# +# Data from forum, schlage 9691T fob +EF1232AB18A0 +# +# Data from a oyster card +374BF468607F +BFC8E353AF63 +15CAFD6159F6 +62EFD80AB715 +987A7F7F1A35 +C4104FA3C526 +4C961F23E6BE +67546972BC69 +F4CD5D4C13FF +94414C1A07DC +16551D52FD20 +9CB290282F7D +77A84170B574 +ED646C83A4F3 +E703589DB50B +513C85D06CDE +95093F0B2E22 +543B01B27A95 +C6D375B99972 +EE4CC572B40E +5106CA7E4A69 +C96BD1CE607F +167A1BE102E0 +A8D0D850A606 +A2ABB693CE34 +7B296C40C486 +91F93A5564C9 +E10623E7A016 +B725F9CBF183 +# +# Data from FDi tag +8829DA9DAF76 +# +# Data from GitHub issue +0A7932DC7E65 11428B5BCE06 11428B5BCE07 11428B5BCE08 11428B5BCE09 11428B5BCE0A 11428B5BCE0F -11496F97752A -123F8888F322 -1322285230B8 -1565A172770F -157B10D84C6B -157C9A513FA5 -15CAFD6159F6 -160A91D29A9C -16551D52FD20 -167A1BE102E0 -16DDCB6B3F24 -1717E34A7A8A -17193709ADF4 -185FA3438949 -1877ED29435A 18971D893494 -1AB23CD45EF6 -1ACC3189578C -1F107328DC8D -1F1A0A111B5B -1F1FFE000000 -2031D1E57A3B -# HID Key B -204752454154 -21A600056CB0 -22729A9BD40F -2338B4913111 -2548A443DF28 25D60050BF6E -26643965B16E -26973EA74321 -27FBC86A00D0 -2A2C13CC242A -2A6D9205E7CA -2CB1A90071C8 -2DD39A54E1F3 -2ED3B15E7C0F -2EF720F2AF76 -2FC1F32F51B1 -2FEAE851C199 -3060206F5B0A -31646241686C -321958042333 -321A695BD266 -340E40F81CD8 -345547514B4D -356D46474348 -369A4663ACD2 -36ABF5874ED7 -374BF468607F -381ECE050FBD -386B4D634A65 -38FCF33072E0 -3A09594C8587 -3B7E4FD575AD -3C5D1C2BCD18 -3E84D2612E2A -3FA7217EC575 -410B9B40B872 -414C41524F4E -415A54454B4D -4186562A5BB2 -424C41524F4E -425A73484166 -436A46587552 -447AB7FD5A6B -44DD5A385AAF 44F0B5FBE344 -45635EF66EF3 -476242304C53 -484558414354 -# HID Key A -484944204953 -484A57696F4A -48734389EDC3 -48C739E21A04 -49FAE4E3849F -4A6352684677 -4C32BAF326E0 -4C6B69723461 -4C961F23E6BE -4D3248735131 -4D5076656D58 -4E32336C6E38 -4E4175623670 -4F9F59C9C875 -509359F131B1 -51044EFB5AAB -5106CA7E4A69 -513C85D06CDE -52264716EFDE -536653644C65 -53C11F90822A -543B01B27A95 -5481986D2D62 -5544564E6E67 -564777315276 -568C9083F71C -57734F6F6974 -57784A533069 -584F66326877 -5A1B85FCE20A -5EC39B022F2B -623055724556 -62387B8D250D -6245E47352E6 -62CED42A6D87 -62D0C424ED8E -62EFD80AB715 -645A166B1EEB -649D2ABBBD20 -666E564F4A44 -668770666644 -66B03ACA6EE9 -66D2B7DC39EF -66F3ED00FED7 -67546972BC69 -675557ECC92E -686A736A356E -68D3F7307C89 -69FB7B7CD8EE -6A1987C40A21 -6A676C315142 -6A696B646631 -6B6579737472 -6BC1E1AE547D -6C78928E1317 -6C94E1CED026 -6D44B5AAF464 -6D4C5B3658D2 -6D4E334B6C48 -6DB17C16B35B -6F4B6D644178 -6F506F493353 -703140FD6D86 -70564650584F -710732200D34 -71F3A315AD26 -744E326B3441 -752FBB5B7B45 -756EF55E2507 -77494C526339 -77646B633657 -77A84170B574 -79674F96C771 -7B296C40C486 7B296F353C6B -7C335FB121B5 -7F33625BC129 8553263F4FF0 -8697389ACA26 -86AFD95200F7 -87DF99D496CB -8829DA9DAF76 -89347350BD36 -8AD5517B4B18 8E5D33A6ED51 -8ED41E8B8056 -8FA1D601D0A2 -911E52FD7CE4 -9189449EA24E -91CE16C07AC5 -91F93A5564C9 -925B158F796F -92EE4DC87191 -932B9CB730EF -94414C1A07DC -95093F0B2E22 -961C0DB4A7ED -987A7F7F1A35 -9B832A9881FF -9CB290282F7D -9DC282D46217 9F42971E8322 -A10F303FC879 -A21680C27773 -A22AE129C013 -A2ABB693CE34 -A4F204203F56 -A56C2DF9A26D -A57186BDD2B9 -A643F952EA57 -A6CAC2886412 -A7ABBC77CC9E -A8D0D850A606 -A920F32FE93A -AA0720018738 -AB4E7045E97D -AC70CA327A04 -AD4FB33388BF -AD9E0A1CA2F7 -AFD0BA94D624 -B1ACA33180A5 -B35A0E4ACC09 -B39AE17435DC -B468D1991AF9 -B578F38A5C61 -B725F9CBF183 -B7BF0C13066E -B8A1F613CF3D -BA5B895DA162 -BBA840BA1C57 -BBE8FFFCF363 -BEDB604CC9D1 -BF1F4424AF76 -BFB6796A11DB -BFC8E353AF63 -C0C1C2C3C4C5 -C0DECE673829 -C2B7EC7D4EB1 -C3C88C6340B8 -C3F19EC592A2 -C4104FA3C526 -C5132C8980BC -C5CFE06D9EA3 C620318EF179 -C6D375B99972 -C96BD1CE607F -CB779C50E1BD -CBA6AE869AD5 -CC6B3B3CD263 -D0D1D2D3D4D5 -D21762B2DE3B -D2A597D76936 -D327083A60A7 D4FE03CE5B06 D4FE03CE5B07 D4FE03CE5B08 D4FE03CE5B09 D4FE03CE5B0A D4FE03CE5B0F -D58023BA2BDC -D9A37831DCE5 -DB1A3338B2EB -DD61EB6BCE22 -DF37DCB6AFB3 -E10623E7A016 E241E8AFCBAF -E2A5DC8E066F -E4F65C0EF32C -E55A3CA71826 -E64A986A5D94 -E703589DB50B -EB0A8FF88ADE -EC0A9B1A9E06 -ED646C83A4F3 -EE4CC572B40E -EEB420209D0C -F101622750B7 -F1B9F5669CC8 -F23442436765 -F238D78FF48F -F26E21EDCEE2 -F4396E468114 -F4CD5D4C13FF -F662248E7E89 -F72A29005459 -F792C4C76A5C +# Transport system Argentina - SUBE +# Shared key - sec 3 blk 15 +3FA7217EC575 +# +# Data from forum post +123F8888F322 +050908080008 +# +# Data from hoist +4F9F59C9C875 +# +# Data from pastebin +66F3ED00FED7 F7A39753D018 -F9861526130F -FAD63ECB5891 - -# Some keys of https://w3bsit3-dns.com and https://ikey.ru -BC4580B7F20B -8E26E45E7D65 -A7141147D430 -18E3A02B5EFF -E328A1C7156D -8A8D88151A00 -7A86AA203788 -72F96BDD3714 -C76BF71A2509 -1B61B2E78C75 -045CECA15535 -6B07877E2C5C -0CE7CD2CC72B -EA0FD73CB149 -B81F2B0C2F66 -BB52F8CCE07F -46D78E850A7E -E4821A377B75 -8791B2CCB5C4 -D5524F591EED -BAFF3053B496 -0F318130ED18 -42E9B54E51AB -7413B599C4EA -9EA3387A63C1 -B27ADDFB64B0 -E56AC127DD45 -0BE5FAC8B06A -FD8705E721B0 -7259FA0197C6 -22052B480D11 -9D993C5D4EF4 -C65D4EAA645B -0EB23CC8110B -3A8A139C20B4 -19FC84A3784B -0F01CEFF2742 -A3FAA6DAFF67 -BC2D1791DEC1 -7A396F0D633D -ACFFFFFFFFFF -77DABC9825E1 -518DC6EEA089 -044CE1872BC3 -114D6BE9440C -AFCEF64C9913 - +# +# Data from https://pastebin.com/Z7pEeZif +386B4D634A65 +666E564F4A44 +564777315276 +476242304C53 +6A696B646631 +4D3248735131 +425A73484166 +57784A533069 +345547514B4D +4C6B69723461 +4E4175623670 +4D5076656D58 +686A736A356E +484A57696F4A +6F4B6D644178 +744E326B3441 +70564650584F +584F66326877 +6D4E334B6C48 +6A676C315142 +77494C526339 +623055724556 +356D46474348 +4E32336C6E38 +57734F6F6974 +436A46587552 +5544564E6E67 +6F506F493353 +31646241686C +77646B633657 +# +# Data from TransPert +2031D1E57A3B +53C11F90822A +9189449EA24E +# +# data from Github +410B9B40B872 +2CB1A90071C8 +# +# +8697389ACA26 +1AB23CD45EF6 +013889343891 +# +# +0000000018DE +16DDCB6B3F24 +# +# Data from https://pastebin.com/vwDRZW7d +# Vingcard Mifare 4k Staff card +EC0A9B1A9E06 +6C94E1CED026 +0F230695923F +0000014B5C31 +# +# +BEDB604CC9D1 +B8A1F613CF3D +B578F38A5C61 +B66AC040203A +6D0B6A2A0003 +2E641D99AD5B +AD4FB33388BF +69FB7B7CD8EE +# +# Hotel +2A6D9205E7CA +13B91C226E56 +# +# KABA Hotel Locks +2A2C13CC242A +# +# +27FBC86A00D0 +01FA3FC68349 +# +# Smart Rider. Western Australian Public Transport Cards +6D44B5AAF464 +1717E34A7A8A +# +# RFIDeas +6B6579737472 +# +# HID MIFARE Classic 1k Key +484944204953 +204752454154 +# HID MIFARE SO +3B7E4FD575AD +11496F97752A +# +# Luxeo/Aztek cashless vending +415A54454B4D +# +# BQT +321958042333 +# +# Aperio KEY_A Sector 1, 12, 13, 14, 15 Data Start 0 Length 48 +160A91D29A9C +# +# Gallagher +B7BF0C13066E +# +# PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) +009FB42D98ED +002E626E2820 +# +# Boston, MA, USA Transit - MBTA Charlie Card +3060206F5B0A +5EC39B022F2B +3A09594C8587 +F1B9F5669CC8 +F662248E7E89 +62387B8D250D +F238D78FF48F +9DC282D46217 +AFD0BA94D624 +92EE4DC87191 +B35A0E4ACC09 +756EF55E2507 +447AB7FD5A6B +932B9CB730EF +1F1A0A111B5B +AD9E0A1CA2F7 +D58023BA2BDC +62CED42A6D87 +2548A443DF28 +2ED3B15E7C0F +F66224EE1E89 +# +# +60012E9BA3FA +# +# +DE1FCBEC764B +81BFBE8CACBA +BFF123126C9B +2F47741062A0 +B4166B0A27EA +A170D9B59F95 +400BC9BE8976 +D80511FC2AB4 +1FCEF3005BCF +BB467463ACD6 +E67C8010502D +FF58BA1B4478 +# +# Data from https://pastebin.com/Kz8xp4ev +FBF225DC5D58 +# +# Data https://pastebin.com/BEm6bdAE +# vingcard.txt +# Note: most likely diversified +96A301BCE267 +4708111C8604 +3D50D902EA48 +6700F10FEC09 +7A09CC1DB70A +560F7CFF2D81 +66B31E64CA4B +9E53491F685B +3A09911D860C +8A036920AC0C +361F69D2C462 +D9BCDE7FC489 +0C03A720F208 +6018522FAC02 +# +# Data from https://pastebin.com/4t2yFMgt +# Mifare technische Universität Graz TUG +D58660D1ACDE +50A11381502C +C01FC822C6E5 +0854BF31111E +# +# More keys - Found 8A at Sebel Hotel in Canberra, Australia +AE8587108640 +# +# SafLock standalone door locks +135B88A94B8B +# # Russian Troika card +EC29806D9738 08B386463229 0E8F64340BA4 0F1C63013DBA @@ -587,545 +1059,284 @@ EAAC88E5DC99 F8493407799D 6B8BD9860763 D3A297DC2698 -FBF225DC5D58 -# Strelka extension +# +# Data from reddit +34635A313344 +593367486137 +# +# Keys from Mifare Classic Tool project +044CE1872BC3 +045CECA15535 +0BE5FAC8B06A +0CE7CD2CC72B +0EB23CC8110B +0F01CEFF2742 +0F318130ED18 +114D6BE9440C +18E3A02B5EFF +19FC84A3784B +1B61B2E78C75 +22052B480D11 3367BFAA91DB +3A8A139C20B4 +42E9B54E51AB +46D78E850A7E 4B609876BBA3 -5C83859F2224 -66B504430416 -70D1CF2C6843 +518DC6EEA089 +6B07877E2C5C +7259FA0197C6 +72F96BDD3714 +7413B599C4EA +77DABC9825E1 +7A396F0D633D +7A86AA203788 +8791B2CCB5C4 +8A8D88151A00 8C97CD7A0E56 +8E26E45E7D65 +9D993C5D4EF4 +9EA3387A63C1 +A3FAA6DAFF67 +A7141147D430 +ACFFFFFFFFFF +AFCEF64C9913 +B27ADDFB64B0 +B81F2B0C2F66 B9F8A7D83978 -C4B3BD0ED5F1 -C4D3911AD1B3 -CAD7D4A6A996 -DA898ACBB854 -FEA1295774F9 - -# Moscow public toilets card -807119F81418 -22C8BCD10AAA -0AAABA420191 -E51B4C22C8BC -DBF9F79AB7A2 -34EDE51B4C22 -C8BCD10AAABA -BCD10AAABA42 - -# Moscow social card -2735FC181807 -2ABA9519F574 -84FD7F7A12B6 -186D8C4B93F9 -3A4BBA8ADAF0 -8765B17968A2 -40EAD80721CE -0DB5E6523F7C -51119DAE5216 -83E3549CE42D -136BDB246CAC -2F87F74090D1 -E53EAEFE478F -CE2797E73070 -328A034B93DB -81E1529AE22B -FC55C50E579F -1A72E2337BC3 -5DB52676BE07 -F64FBF085098 -8FE758A8F039 -BB1484CC155D -41990A529AE2 -CD2E9EE62F77 -69C1327AC20B -3C9C0D559DE5 -67BF3880C811 -48A01159A1E9 -2B83FB448CD4 -F24BBB044C94 -7DE02A7F6025 -BF23A53C1F63 -CB9A1F2D7368 -C7C0ADB3284F -9F131D8C2057 -67362D90F973 -6202A38F69E2 -100533B89331 -653A87594079 -D8A274B2E026 -B20B83CB145C -9AFA6CB4FC3D -94F46DB5FD46 -C31C8CD41D65 -BB1684CC155D -CA2393DB246C -1D75E52E76BE -81D9529AE223 -0159C9125AA2 -52AA1B6BB3FB -97EF60A8F031 -6FC73888D011 -3A92FA438BD3 -74CC3D85CD0E -025ACA1B63A3 -AF0878C81151 -9BFB6CB4FC45 -F750C0095199 -075FCF1860A8 -2686EE3F87C7 -277FEF3880C0 -82DA4B93DB1C -9CF46DB5FD46 -93EB64ACF43D - -# Keys from RfidResearchGroup proxmark3 project -# https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_default_keys.dic -0854BF31111E -0C03A720F208 -135B88A94B8B -1FCEF3005BCF -2F47741062A0 -361F69D2C462 -3A09911D860C -3D50D902EA48 -400BC9BE8976 -4708111C8604 -50A11381502C -560F7CFF2D81 -60012E9BA3FA -6018522FAC02 -66B31E64CA4B -6700F10FEC09 -7A09CC1DB70A -81BFBE8CACBA -8A036920AC0C -8A19D40CF2B5 -96A301BCE267 -9E53491F685B -A170D9B59F95 -AE8587108640 -B4166B0A27EA -BB467463ACD6 -BFF123126C9B -C01FC822C6E5 -D58660D1ACDE -D80511FC2AB4 -D9BCDE7FC489 -DE1FCBEC764B -E67C8010502D -FF58BA1B4478 - -C1E51C63B8F5 -4143414F5250 -474249437569 -B0699AD03D17 -B18CDCDE52B7 -3864FCBA5937 -85A438F72A8A -C342F825B01B -C6A76CB2F3B5 -323334353637 -43C7600DEE6B -D01AFEEB890A -26396F2042E7 -F3F0172066B2 -BB320A757099 -00F0BD116D70 -25094DF2C1BD -E41E6199318F -F00DFEEDD0D0 -6D9B485A4845 -71171A82D951 -62711F1A83E9 -1711B1A82E96 -F3864FCCA693 -7B7E752B6A2D -2012053082AD -9AEDF9931EC1 -B9C874AE63D0 -83BAB5ACAD62 -A541538F1416 -4A2B29111213 -D31463A7AB6D -AD5645062534 -B069D0D03D17 -30FFB6B056F5 -D7744A1A0C44 -D1991E71E2C5 -1795902DBAF9 -4243414F5250 -C554EF6A6015 -A5524645CD91 -200306202033 -A00003000084 -CEE3632EEFF5 -F0F0172066B2 -B021669B44BB -3F1A87298691 -A2B2C9D187FB -4B511F4D28DD -E3AD9E9BA5D4 -B3630C9F11C8 -F83466888612 -857464D3AAD1 -A2A3CCA2A3CC -35D850D10A24 -B2F170172066 -0D8CA561BDF3 -05F89678CFCF -850984712F1A -21EDF95E7433 -172066B2F2F0 -6B2F1B017206 -363119000001 -23D4CDFF8DA3 -EF4C5A7AC6FC -123456ABCDEF -8F9B229047AC -E96246531342 -5D293AFC8D7E -AABBCC660429 -63FCA9492F38 -354A787087F1 -40E5EA1EFC00 -675A32413770 -12FD3A94DF0E -C4F271F5F0B3 -4A306E62E9B6 -4CFF128FA3EF -0000085F0000 -0F385FFB6529 -FA38F70215AD -904735F00F9E -B66AC040203A -BCFE01BCFE01 -2FCA8492F386 -237A4D0D9119 -E6849FCC324B -0ED7846C2BC9 -1FB235AC1388 -EA1B88DF0A76 -C9739233861F -89EAC97F8C2A -D964406E67B4 -563A22C01FC8 -DB5181C92CBE -1E352F9E19E5 -6291B3860FC8 -A9A4045DCE77 -434456495243 -B5F454568271 -491CDC863104 -4D8B8B95FDEE -AFBECD121004 -4D48414C5648 -E1DD284379D4 -AFBECD120454 -CFC738403AB0 -0AF7DB99AEE4 -772219470B38 -EF61A3D48E2A -4A4C474F524D -0172066B2F03 -3D5D9996359A -66A163BA82B4 -2011092119F1 -BB2C0007D022 -1494E81663D7 -590BD659CDD2 -A6C028A12FBB -BCF5A6B5E13F -A2F63A485632 -39CF885474DD -D2ECE8B9395E -17505586EF02 -70172066B2F0 -B385EFA64290 -B7C344A36D88 -010000000000 -2F130172066B -DFED39FFBB76 -3F7A5C2DBD81 -DFE73BE48AC6 -FB0B20DF1F34 -913385FFB752 -2066B2F27017 -91FF18E63887 -0734BFB93DAB -97F5DA640B18 -B42C4DFD7A90 -C121FF19F681 -BE7C4F6C7A9A -FE04ECFE5577 -18F34C92A56E -0BB31DC123E5 -0E0E8C6D8EB6 -1CFA22DBDFC3 -0F1A81C95071 -AA4D051954AC -9F9D8EEDDCCE -863FCB959373 -98631ED2B229 -A23456789123 -833FBD3CFE51 -AB28A44AD5F5 -74A386AD0A6D -4C60F4B15BA8 -022FE48B3072 -97271231A83F -385EFA542907 -066B2F230172 -BA729428E808 -BDF837787A71 -A05DBD98E0FC -395244733978 -9B1DD7C030A1 -2C9F3D45BA13 -1DB710648A65 -E65B66089AFC -A85198481331 -5A12F83326E7 -A7D71AC06DC2 -64E2283FCF5E -FFFFAE82366C -10F3BEBC01DF +BAFF3053B496 +BB52F8CCE07F +BC2D1791DEC1 +BC4580B7F20B +C65D4EAA645B +C76BF71A2509 +D5524F591EED +E328A1C7156D +E4821A377B75 +E56AC127DD45 +EA0FD73CB149 +FC0001877BF7 +FD8705E721B0 00ADA2CD516D -1D28C58BBE8A -6936C035AE1B -AC45AD2D620D -64A2EE93B12B -A9F95891F0A4 -E45230E7A9E8 -A7FB4824ACBF -223C3427108A -58AC17BF3629 -535F47D35E39 -10F2BBAA4D1C -A0004A000036 -3F3865FCCB69 -0B0172066B2F -4098653289D3 -BA28CFD15EE8 -A22647F422AE -99858A49C119 -29173860FC76 -1A80B93F7107 -1A2B3C4D5E6F -2C60E904539C -6C9EC046C1A4 -FC9839273862 -1C1532A6F1BC -09800FF94AAF +518108E061E2 +558AAD64EB5B +001122334455 +6CA761AB6CA7 +B1C4A8F7F6E3 +FF75AFDA5A3C +FCDDF7767C10 +A6B3F6C8F1D4 +# +# +237A4D0D9119 +0ED7846C2BC9 FFFFD06F83E3 -0B83797A9C64 -13B91C226E56 -A4EF6C3BB692 -000000270000 -A00002000021 -872B71F9D15A -A37A30004AC9 -B1A862985913 -A4CDFF3B1848 -2E641D99AD5B -827ED62B31A7 -CD212889C3ED -B1A80C94F710 -CB911A1A1929 -A844F4F52385 -C0BEEFEC850B -5C8FF9990DA2 -160F4B7AB806 -B8937130B6BA -66B2F1F01720 -82D58AA49CCB -A9B43414F585 -C38197C36420 -0172066B2F33 -434143445649 -34B16CD59FF8 -5A7A52D5E20D -6471A5EF2D1A -F57F410E18FF -3B0172066B2F -E907470D31CC -B8457ACC5F5D -67CC03B7D577 -A8844B0BCA06 -435330666666 -B47058139187 -46868F6D5677 -C27D999912EA -37FC71221B46 -F97371271A84 -AE43F36C1A9A -5EC7938F140A -AA734D2F40E0 -A5A4A3A2A1A0 -70172066B2F3 -03EA4053C6ED -06FF5F03AA1A -5554AAA96321 -0120BF672A64 -87291F3861FC -9EBC3EB37130 -B4C36C79DA8D -43CA22C13091 -6D0B6A2A0003 -FB6C88B7E279 -013940233313 -7DD399D4E897 -ED3A7EFBFF56 -68F9A1F0B424 -6476FA0746E7 -1A8619858137 -1131A81D9507 -8268046CD154 -4857DD68ECD9 -B62307B62307 -C6C866AA421E -F66224EE1E89 -4ECCA6236400 -72066B2F2B01 -34D3C568B348 -000131B93F28 -419A13811554 -B6323F550F54 +FFFFAE82366C F89C86B2A961 -B268F7C9CA63 -B8B1CFA646A8 -4D57414C5648 -6A0E215D1EEB -70758FDD31E0 +F83466888612 +ED3A7EFBFF56 +E96246531342 +E1DD284379D4 +DFED39FFBB76 +DB5181C92CBE +CFC738403AB0 +BCFE01BCFE01 +BA28CFD15EE8 +B0699AD03D17 +AABBCC660429 +A4EF6C3BB692 +A2B2C9D187FB +9B1DD7C030A1 +9AEDF9931EC1 +8F9B229047AC +872B71F9D15A +833FBD3CFE51 +5D293AFC8D7E +5554AAA96321 +474249437569 +435330666666 +1A2B3C4D5E6F +123456ABCDEF +83BAB5ACAD62 +64E2283FCF5E +64A2EE93B12B +46868F6D5677 +40E5EA1EFC00 37D4DCA92451 -444156494442 -B210CFA436D2 -207FFED492FD -7578BF2C66A9 -50983712B1A8 -5A85536395B3 -B81846F06EDF -842146108088 +2012053082AD +2011092119F1 +200306202033 +1795902DBAF9 +17505586EF02 +022FE48B3072 +013940233313 +# +# Hotel Adina +9EBC3EB37130 +# +# Misc. keys from hotels & library cards in Germany +914f57280ce3 +324a82200018 +370aee95cd69 +2e032ad6850d +1feda39d38ec +288b7a34dbf8 +0965e3193497 +18c628493f7f +064d9423938a +995fd2a2351e +7c7d672bc62e +217250fb7014 +ae7478ccaee7 +abbf6d116eaf +05862c58edfb +e43b7f185460 +6a59aa9a959b +b79e5b175227 +7bc9ebb8274b +b2afbf2331d4 +223e5847dd79 +640524d2a39b +aee297cb2fd6 +3da5dfa54604 +0cf1a2aa1f8d +# +# most likely diversifed individual keys. +# data from https://github.com/korsehindi/proxmark3/commit/24fdbfa9a1d5c996aaa5c192bc07e4ab28db4c5c +491CDC863104 +A2F63A485632 +98631ED2B229 19F1FFE02563 -D3B595E9DD63 +# +# Argentina +563A22C01FC8 +43CA22C13091 +25094DF2C1BD +# +# OMNITEC.ES HOTEL TIMECARD / MAINTENANCECARD +AFBECD120454 +# +# OMNITEC.ES HOTEL EMERGENCYCARD +842146108088 +# +# TAPCARD PUBLIC TRANSPORT LA +EA1B88DF0A76 +D1991E71E2C5 +05F89678CFCF +D31463A7AB6D +C38197C36420 +772219470B38 +1C1532A6F1BC +FA38F70215AD +E907470D31CC +160F4B7AB806 +1D28C58BBE8A B3830B95CA34 -A506370E7C0F +6A0E215D1EEB +E41E6199318F +C4F271F5F0B3 +1E352F9E19E5 +0E0E8C6D8EB6 +C342F825B01B +CB911A1A1929 +E65B66089AFC +B81846F06EDF +37FC71221B46 880C09CFA23C -43454952534E -39AD2963D3D1 +6476FA0746E7 +419A13811554 +2C60E904539C +4ECCA6236400 +10F2BBAA4D1C +4857DD68ECD9 +C6A76CB2F3B5 +E3AD9E9BA5D4 +6C9EC046C1A4 +# +# ROC HIGHSCHOOL ACCESSCARD +B021669B44BB +B18CDCDE52B7 +A22647F422AE +B268F7C9CA63 +A37A30004AC9 +B3630C9F11C8 +A4CDFF3B1848 +B42C4DFD7A90 +A541538F1416 +B5F454568271 +A6C028A12FBB +B6323F550F54 +A7D71AC06DC2 +B7C344A36D88 +A844F4F52385 +B8457ACC5F5D +A9A4045DCE77 B9B8B7B6B5B3 -82908B57EF4F - -C67BEB41FFBF -2AFFD6F88B97 -E77952748484 -988ACDECDFB0 -605F5E5D5C5B -42EF7BF572AB -4087C6A75A96 -AADE86B1F9C1 -5EA088C824C9 -120D00FFFFFF -CA679D6291B0 -E2A9E88BFE16 -0A4600FF00FF -43B04995D234 -0602721E8F06 -5F31F6FCD3A0 -4AE23A562A80 -A0974382C4C5 -91C2376005A1 -FEE2A3FBC5B6 -2602FFFFFFFF -CA3A24669D45 -A9F3F289B70C -B84D52971107 -274E6101FC5E -00DD300F4F10 -F7BA51A9434E -4A832584637D -B16B2E573235 -A82045A10949 -FC0B50AF8700 -403F09848B87 -DFF293979FA7 -4118D7EF0902 -30B7680B2BC9 -52B0D3F6116E -5296C26109D4 -DB6819558A25 -4D6F62692E45 -0406080A0C0E -6130DFA578A0 -30D9690FC5BC -D73438698EEA -005078565703 - -7C87013A648A -9E7168064993 -45FEE09C1D06 -734EBE504CE8 -E592ED478E59 -C229CE5123D5 -240F0BB84681 -D8BA1AA9ABA0 -865B6472B1C0 -974A36E2B1BA -57D83754711D -C9CD8D7C65E5 -C197AE6D6990 -AABAFFCC7612 -C0AD1B72921A -AFAAFCC40DEC -E902395C1744 -DAC7E0CBA8FD -755D49191A78 -68D3263A8CD6 -2F8A867B06B4 -7357EBD483CC -ABCC1276FCB0 -26BF1A68B00F -704A81DDACED -E8794FB14C63 -EC070A52E539 -037F64F470AD -76939DDD9E97 -4D80A10649DF -89E00BC444EF -26107E7006A0 -B1D3BC5A7CCA -ECC58C5D34CA -9F97C182585B -B2FE3B2875A6 -B70B1957FE71 -E495D6E69D9C -0860318A3A89 -4051A85E7F2D -17D071403C20 -3BF391815A8D -1927A45A83D3 -CE7712C5071D -F3C1F1DB1D83 -D0DDDF2933EC - -# Iron Logic -A3A26EF4C6B0 -2C3FEAAE99FC -E85B73382E1F -F4ED24C2B998 -CB574C6D3B19 -E092081D724B -B38D82CF7B6C -8228D2AA6EFA -2C7E983588A3 -CF7A7B77E232 -32A7F5EAF87D -7453A687B5F0 -01A0C008A5B9 -DEC0CEB0CE24 -413BED2AE45B -D6261A9A4B3F -CB9D507CE56D - +AA4D051954AC +BA729428E808 +AB28A44AD5F5 +BB320A757099 +AC45AD2D620D +BCF5A6B5E13F +AD5645062534 +BDF837787A71 +AE43F36C1A9A +BE7C4F6C7A9A +5EC7938F140A +82D58AA49CCB +# +# MELON CARD +323334353637 +# +# +CEE3632EEFF5 +827ED62B31A7 +03EA4053C6ED +C0BEEFEC850B +F57F410E18FF +0AF7DB99AEE4 +A7FB4824ACBF +207FFED492FD +1CFA22DBDFC3 +30FFB6B056F5 +39CF885474DD +00F0BD116D70 +4CFF128FA3EF +10F3BEBC01DF +# +# Transportes Insular La Palma +0172066B2F03 +0000085F0000 +1A80B93F7107 +70172066B2F0 +B1A80C94F710 +0B0172066B2F +0F1A81C95071 +F0F0172066B2 +1131A81D9507 +2F130172066B +71171A82D951 +B2F170172066 +1711B1A82E96 +6B2F1B017206 +62711F1A83E9 +66B2F1F01720 +97271231A83F +066B2F230172 +F97371271A84 +2066B2F27017 +50983712B1A8 +72066B2F2B01 +850984712F1A +172066B2F2F0 +A85198481331 +0172066B2F33 +1A8619858137 +70172066B2F3 +B1A862985913 +3B0172066B2F +3F1A87298691 +F3F0172066B2 +# # Tehran ezpay 38A88AEC1C43 CBD2568BC7C6 @@ -1142,138 +1353,404 @@ D3B1C7EA5C53 604AC8D87C7E 8E7B29460F12 BB3D7B11D224 - -# More keys from the PM3 repo -DC018FC1D126 -C428C4550A75 -0C4233587119 -5B0C7EC83645 -540D5E6355CC -35C649004000 -CFE63749080A -6307417353C1 -411053C05273 -749934CC8ED3 -1C68315674AC -35D152154017 -D1417E431949 -26B85DCA4321 -D973D917A4C7 -3A471B2192BF -534F4C303232 -730956C72BC2 -C9449301AF93 -F678905568C3 -4578ABFEDC12 -075D1A4DD323 -43E69C28F08C -0F35D5660653 -F7FA2F629BB1 -5145C34DBA19 -124578ABFEDC -E2F14D0A0E28 -C8AACD7CF3D1 -9C616585E26D -4927C97F1D57 -6F30126EE7E4 -155332417E00 -5353B3AECB53 -361A62F35BC9 -00460740D722 -A9B018868CC1 -2E71D3BD262A -4F75030AD12B -42454C4C4147 -D75971531042 -25352912CD8D -51E97FFF51E9 -1170553E4304 -D1F71E05AD9D -541C417E57C0 -AE76242931F1 -6039ABB101BB -0E620691B9FE -4BF6DE347FB6 -10510049D725 -1F0128447C00 -D14E615E0545 -94B6A644DFF6 -81B20C274C3F -66695A45C9FA -130662240200 -DD0DE3BA08A6 -05F5EC05133C -4FA9EB49F75E -C1E6F8AFC9EC -28D70900734C -32CA52054416 -703265497350 -3D923EB73534 -C151D998C669 +# +# Chaco +B210CFA436D2 +B8B1CFA646A8 +A9F95891F0A4 +# +# Keys from APK application "Scan Badge" +4A4C474F524D +444156494442 +434143445649 +434456495243 +A00002000021 +EF61A3D48E2A +A23456789123 +010000000000 +363119000001 +A00003000084 +675A32413770 +395244733978 +A0004A000036 +2C9F3D45BA13 +4243414F5250 +DFE73BE48AC6 +# +# +B069D0D03D17 +000131B93F28 +# +# From the DFW Area, TX, USA +A506370E7C0F +26396F2042E7 +70758FDD31E0 +9F9D8EEDDCCE +06FF5F03AA1A +4098653289D3 +904735F00F9E +B4C36C79DA8D +68F9A1F0B424 +5A85536395B3 +7DD399D4E897 +EF4C5A7AC6FC +B47058139187 +8268046CD154 +67CC03B7D577 +# +# From the HTL Mödling, NÖ, AT +A5524645CD91 +D964406E67B4 +99858A49C119 +7B7E752B6A2D +C27D999912EA +66A163BA82B4 +4C60F4B15BA8 +# +# CAFE + CO, AT +35D850D10A24 +4B511F4D28DD +E45230E7A9E8 +535F47D35E39 +FB6C88B7E279 +# +# Metro Card, AT +223C3427108A +# +# Unknown, AT +23D4CDFF8DA3 +E6849FCC324B +12FD3A94DF0E +# +# Unknown, AT +0B83797A9C64 +39AD2963D3D1 +# +# Hotel Berlin Classic room A KEY +34B16CD59FF8 +# +# Hotel Berlin Classic room B KEY +BB2C0007D022 +# +# Coinmatic laundry Smart card +# data from: https://pastebin.com/XZQiLtUf +0734BFB93DAB +85A438F72A8A +# +# Data from forum, Chinese hotel +58AC17BF3629 +B62307B62307 +# +# +A2A3CCA2A3CC +# +# Granada, ES Transport Card +000000270000 +0F385FFB6529 +29173860FC76 +2FCA8492F386 +385EFA542907 +3864FCBA5937 +3F3865FCCB69 +6291B3860FC8 +63FCA9492F38 +863FCB959373 +87291F3861FC +913385FFB752 +B385EFA64290 +C9739233861F +F3864FCCA693 +FC9839273862 +# +# various hotel keys +34D3C568B348 +91FF18E63887 +4D8B8B95FDEE +354A787087F1 +4A306E62E9B6 +B9C874AE63D0 +# +# Data from official repo +F00DFEEDD0D0 +0BB31DC123E5 +7578BF2C66A9 +CD212889C3ED +6936C035AE1B +C6C866AA421E +590BD659CDD2 +AA734D2F40E0 +09800FF94AAF +5A12F83326E7 +C554EF6A6015 +0D8CA561BDF3 +B8937130B6BA +D7744A1A0C44 +82908B57EF4F +FE04ECFE5577 +# +# comfort inn hotel +4D57414C5648 +4D48414C5648 +# +# unknown hotel key +6D9B485A4845 +# +# Bosch Solution 6000 +5A7A52D5E20D +# +# Found in TagInfo app +# RATB key +C1E51C63B8F5 +1DB710648A65 +# E-GO card key +18F34C92A56E +# +# Library Card MFP - SL1 +4A832584637D +CA679D6291B0 +30D9690FC5BC +5296C26109D4 +E77952748484 +91C2376005A1 +30B7680B2BC9 +E2A9E88BFE16 +43B04995D234 +AADE86B1F9C1 +5EA088C824C9 +C67BEB41FFBF +B84D52971107 +52B0D3F6116E +# +# Data from https://pastebin.com/cLSQQ9xN +CA3A24669D45 +4087C6A75A96 +403F09848B87 +D73438698EEA +5F31F6FCD3A0 +A0974382C4C5 +A82045A10949 +# +# Data from https://pastebin.com/2iV8h93h +# +# funnivarium +# forum ankara +2602FFFFFFFF +# +# macera adasi +# ankara kentpark +# INACTIVE +0A4600FF00FF +DFF293979FA7 +4D6F62692E45 +4118D7EF0902 +# +# petrol ofisi +# positive card +# ode-gec +0406080A0C0E +# +# konya elkart +988ACDECDFB0 +120D00FFFFFF +# +# bowlingo +# serdivan avym +4AE23A562A80 +# +# kart 54 +2AFFD6F88B97 +A9F3F289B70C +DB6819558A25 +6130DFA578A0 +B16B2E573235 +42EF7BF572AB +274E6101FC5E +# +# crazy park +# kizilay avm +00DD300F4F10 +# +# kartsistem B +FEE2A3FBC5B6 +# +# toru ent +# taurus avm +005078565703 +# +# Ving? +0602721E8F06 +FC0B50AF8700 +F7BA51A9434E +# +# eskart +# eskisehir transport card +E902395C1744 +4051A85E7F2D +7357EBD483CC +D8BA1AA9ABA0 +76939DDD9E97 +3BF391815A8D +# +# muzekart +# museum card for turkey +7C87013A648A +E8794FB14C63 +9F97C182585B +EC070A52E539 +C229CE5123D5 +E495D6E69D9C +26BF1A68B00F +B1D3BC5A7CCA +734EBE504CE8 +974A36E2B1BA +C197AE6D6990 +4D80A10649DF +037F64F470AD +C9CD8D7C65E5 +B70B1957FE71 +CE7712C5071D +C0AD1B72921A +45FEE09C1D06 +E592ED478E59 +F3C1F1DB1D83 +704A81DDACED +89E00BC444EF +AFAAFCC40DEC +ECC58C5D34CA +57D83754711D +D0DDDF2933EC +240F0BB84681 +9E7168064993 +2F8A867B06B4 +# +# bursakart +# bursa transport card +755D49191A78 +DAC7E0CBA8FD +68D3263A8CD6 +865B6472B1C0 +0860318A3A89 +1927A45A83D3 +B2FE3B2875A6 +# +# playland +# maltepe park +ABCC1276FCB0 +AABAFFCC7612 +# +# lunasan +# kocaeli fair +26107E7006A0 +# +# gamefactory +# ozdilek +17D071403C20 +# +# 534F4C415249 -70C714869DC7 -A7395CCB42A0 -89AA9D743812 -A160FCD5EC4C -9DCDB136110C -9951A273DEE7 -AA0857C641A3 -F1A1239A4487 -B882FD4A9F78 -9386E2A48280 -460661C93045 -EF1232AB18A0 -6285A1C8EB5C -C41514DEFC07 -ABFEDC124578 -046154274C11 -5429D67E1F57 -# SMARTair Key B -E7316853E731 -CD7FFFF81C4A -F253C30568C4 -E7D6064C5860 -506DB955F161 -8223205047B6 -070D486BC555 -D4B2D140CB2D -0C734F230E13 -2E4169A5C79D -69D92108C8B5 -A297CEB7D34B +534F4C303232 +# +# Nespresso, smart card +# key-gen algo, these keys are for one card (keys diversified) FF9A84635BD2 -735175696421 -5D0762D13401 -D61707FFDFB1 -2803BCB0C7E1 -C52876869800 -424C0FFBF657 -AF9E38D36582 -B32464412EE3 -50240A68D1D8 -6B13935CD550 -83F3CB98C258 -521B517352C7 -4BB747E48C2A -5E594208EF02 -FFFFFF545846 -D65561530174 -52750A0E592A -112233445566 -2DADE48942C5 -A7765C952DDF -2CA4A4D68B8E -72B458D60363 -F088A85E71D7 -FF94F86B09A6 -B27CCAB30DBD -89ECA97F8C2A -E00000000000 +6F30126EE7E4 +6039ABB101BB +F1A1239A4487 +# +# +B882FD4A9F78 +CD7FFFF81C4A +AA0857C641A3 +C8AACD7CF3D1 9FFDA233B496 +26B85DCA4321 +D4B2D140CB2D +A7395CCB42A0 +541C417E57C0 +D14E615E0545 +69D92108C8B5 +703265497350 +D75971531042 +10510049D725 +35C649004000 +5B0C7EC83645 +05F5EC05133C +521B517352C7 +94B6A644DFF6 +2CA4A4D68B8E +A7765C952DDF +E2F14D0A0E28 +DC018FC1D126 +4927C97F1D57 +046154274C11 +155332417E00 +6B13935CD550 +C151D998C669 +D973D917A4C7 +130662240200 +9386E2A48280 +52750A0E592A +075D1A4DD323 +32CA52054416 +460661C93045 +5429D67E1F57 +0C734F230E13 +1F0128447C00 +411053C05273 +42454C4C4147 +C428C4550A75 +730956C72BC2 +28D70900734C +4F75030AD12B +6307417353C1 +D65561530174 +D1F71E05AD9D +F7FA2F629BB1 +0E620691B9FE +43E69C28F08C +735175696421 +424C0FFBF657 +51E97FFF51E9 +E7316853E731 +00460740D722 +35D152154017 +5D0762D13401 +0F35D5660653 +1170553E4304 +0C4233587119 +F678905568C3 +50240A68D1D8 +2E71D3BD262A +540D5E6355CC +D1417E431949 +4BF6DE347FB6 +# +# +3A471B2192BF +A297CEB7D34B +AE76242931F1 +# +# +124578ABFEDC +ABFEDC124578 +4578ABFEDC12 +# +# Data from +# premier inn hotel chain +5E594208EF02 +AF9E38D36582 +# +# Norwegian building site identication card. (HMS KORT) +# Key a 10DF4D1859C8 +# +# Key B B5244E79B0C8 +# +# Ukraine hotel F5C1C4C5DE34 - +# +# Data from Mifare Classic Tool repo # Rotterdam University of applied sciences campus card BB7923232725 A95BD5BB4FC5 @@ -1297,27 +1774,406 @@ B5ADEFCA46C4 BF3FE47637EC B290401B0CAD AD11006B0601 - -# Keys of Armenian underground ticket -A0A1A2A8A4A5 +# +# Data from Mifare Classic Tool repo +# Armenian Metro +E4410EF8ED2D +6A68A7D83E11 0D6057E8133B D3F3B958B8A3 -6A68A7D83E11 -7C469FE86855 -E4410EF8ED2D 3E120568A35C -CE99FBC8BD26 2196FAD8115B - -# PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) -009FB42D98ED -002E626E2820 - -# Volgograd (Russia) Volna transport cards keys -2B787A063D5D -D37C8F1793F7 - +7C469FE86855 +CE99FBC8BD26 +# +# keys from Eurothermes group (Switzerland) +D66D91829013 +75B691829013 +83E391829013 +A23C91829013 +E46A91829013 +D9E091829013 +FED791829013 +155F91829013 +06CC91829013 +8DDC91829013 +54AF91829013 +29A791829013 +668091829013 +00008627C10A +# +# easycard +310D51E539CA +2CCDA1358323 +03E0094CEDFE +562E6EF73DB6 +F53E9F4114A9 +AD38C17DE7D2 +# +# SUBE cards keys (new) +2DEB57A3EA8F +32C1BB023F87 +70E3AD3F2D29 +202ECDCCC642 +3686192D813F +24501C422387 +2C7813A721C3 +FFE04BE3D995 +D28F090677A1 +DE2D83E2DCCC +A66A478712EA +643232ADB2D5 +C7F4A4478415 +95C013B70D99 +3C383889362A +3C6D9C4A90FA +51BEDBA005E5 +74BF7363F354 +53B09DB89111 +E98075318085 +2F904641D75F +7F60AEF68136 +F5C1B3F62FDA +3E6E5713BA10 +8B75A29D4AB2 +7E6545076619 +# +# SUBE cards keys (old) +4C5A766DFE3A +32C6768847F5 +F68930789631 +8B42B6D64B02 +B627A3CB13F8 +562A4FB8260B +88DDC24E1671 +91CB7802A559 +7A3E0F5B63FC +8CA2C9DC8292 +5CCC6D50EAAC +DE4F5AA9A7F3 +52D0145E1AF5 +C10F92A4E57E +7D6E7AF43C97 +DE1E7D5F6DF1 +F4CB751B031A +C54474936B59 +2A1F900D4533 +6303CDCBB233 +F115E91357B3 +BFE25035B0C8 +62FF943EB069 +7C82EF592001 +D5C172325DD3 +992B152E834A +CE75D7EADEAF +# +# Russian Podorozhnik card (Saint-Petersburg transport) +# may be combined with Troika +038B5F9B5A2A +04DC35277635 +0C420A20E056 +152FD0C420A7 +296FC317A513 +29C35FA068FB +31BEC3D9E510 +462225CD34CF +4B7CB25354D3 +5583698DF085 +578A9ADA41E3 +6F95887A4FD3 +7600E889ADF9 +86120E488ABF +8818A9C5D406 +8C90C70CFF4A +8E65B3AF7D22 +9764FEC3154A +9BA241DB3F56 +AD2BDC097023 +B0A2AAF3A1BA +B69D40D1A439 +C956C3B80DA3 +CA96A487DE0B +D0A4131FB290 +D27058C6E2C7 +E19504C39461 +FA1FBB3F0F1F +FF16014FEFC7 +# +# Food GEM +6686FADE5566 +# +# Samsung Data Systems (SDS) — Electronic Locks +# Gen 1 S10 KA/KB is FFFFFFFFFFFF, incompatible with Gen 2 locks +# +# SDS Gen 2 S10 KB +C22E04247D9A +# +# Data from Discord, French pool +# SDS Gen 2 S10 KA +9B7C25052FC3 +494446555455 +# +# Data from Discord, seems to be related to ASSA +427553754D47 +# Keys found on Edith Cowan University Smart Riders +9A677289564D +186C59E6AFC9 +DDDAA35A9749 +9D0D0A829F49 +# Mercator Pika Card, Slovenia +97D77FAE77D3 +5AF445D2B87A +# +# Vilniečio/JUDU kortelė, Lithuania +# A +16901CB400BC +F0FE56621A42 +8C187E78EE9C +FE2A42E85CA8 +# B +6A6C80423226 +F4CE4AF888AE +307448829EBC +C2A0105EB028 +# +# Keys from Flipper Zero Community +# Last update: Aug 13, 2022 +# +# unknown if keys are diversified or static default +# +# Strelka Extension +5C83859F2224 +66B504430416 +70D1CF2C6843 +C4B3BD0ED5F1 +C4D3911AD1B3 +CAD7D4A6A996 +DA898ACBB854 +FEA1295774F9 +# +# Moscow Public Toilets Card +807119F81418 +22C8BCD10AAA +0AAABA420191 +E51B4C22C8BC +DBF9F79AB7A2 +34EDE51B4C22 +C8BCD10AAABA +BCD10AAABA42 +# +# Moscow Social Card +2F87F74090D1 +E53EAEFE478F +CE2797E73070 +328A034B93DB +81E1529AE22B +FC55C50E579F +1A72E2337BC3 +5DB52676BE07 +F64FBF085098 +8FE758A8F039 +BB1484CC155D +41990A529AE2 +CD2E9EE62F77 +69C1327AC20B +3C9C0D559DE5 +67BF3880C811 +48A01159A1E9 +2B83FB448CD4 +F24BBB044C94 +94F46DB5FD46 +C31C8CD41D65 +BB1684CC155D +CA2393DB246C +1D75E52E76BE +81D9529AE223 +0159C9125AA2 +52AA1B6BB3FB +97EF60A8F031 +6FC73888D011 +3A92FA438BD3 +74CC3D85CD0E +025ACA1B63A3 +AF0878C81151 +9BFB6CB4FC45 +F750C0095199 +075FCF1860A8 +2686EE3F87C7 +277FEF3880C0 +82DA4B93DB1C +9CF46DB5FD46 +93EB64ACF43D +# +# Iron Logic RU +A3A26EF4C6B0 +2C3FEAAE99FC +E85B73382E1F +F4ED24C2B998 +CB574C6D3B19 +E092081D724B +B38D82CF7B6C +8228D2AA6EFA +2C7E983588A3 +CF7A7B77E232 +32A7F5EAF87D +7453A687B5F0 +01A0C008A5B9 +DEC0CEB0CE24 +413BED2AE45B +D6261A9A4B3F +CB9D507CE56D +# +# Armenian Underground Ticket +A0A1A2A8A4A5 +# +# Badge Maker Leaked from https://github.com/UberGuidoZ +1A1B1C1D1E1F +1665FE2AE945 +158B51947A8E +E167EC67C7FF +D537320FF90E +5E56BFA9E2C9 +F81CED821B63 +C81584EF5EDF +9551F8F9259D +36E1765CE3E8 +509052C8E42E +776C9B03BE71 +C608E13ADD50 +BEE8B345B949 +ED0EC56EEFDD +9716D5241E28 +05D1FC14DC31 +3321FB75A356 +F22A78E29880 +EC211D12C98D +8CCA8F62A551 +B637E46AD674 +39605B3C8917 +3882719778A1 +9F27D36C4230 +DB32A6811327 +8AA8544A2207 +8C5819E780A4 +7549E90353A2 +2E52ABE0CE95 +E46210ED98AB +61D030C0D7A8 +18E20102821E +DA59354DFB88 +040047C12B75 +D10008074A6F +686E736F6E20 +446176696453 +6F6674776172 +6520446F7665 +# +# Apartment keyfobs (USA) (Corvette830) +E60F8387F0B9 +FFD46FF6C5EE +4F9661ED2E70 +576A798C9904 +1C5179C4A8A1 +16CA203B811B +11AC8C8F3AF2 +# +# The Westin Jakarta Indonesia (D4DB0D) +# Peppers Hotel Unknown location (D4D0D) +6E0DD4136B0A +141940E9B71B +3B1D3AAC866E +95E9EE4CCF8F +FEA6B332F04A +BE0EC5155806 +0500D6BFCC4F +FC5AC7678BE3 +F09BB8DD142D +B4B3FFEDBE0A +540E0D2D1D08 +# +# Schlage 9691T Keyfob (seasnaill) +7579B671051A +4F4553746B41 +# +# Vigik ScanBadge App (fr.badgevigik.scanbadge) +# Website https://badge-vigik.fr/ (Alex) +0000A2B3C86F +021200C20307 +021209197507 +1E34B127AF9C +303041534956 +4143532D494E +41454E521985 +43412D627400 +455249524345 +456666456666 +45B722C63319 +484585414354 +4D414C414741 +536563644C65 +57D27B730760 +593DD8FE167A +6472616E7265 +65626F726369 +680E95F3C287 +709BA7D4F920 +8829DAD9AF76 +92D0A0999CBA +948EE7CFC9DB +9EB7C8A6D4E3 +A22AE12C9013 +AFC984A3576E +# +# Vigik verified by quantum-x +# https://github.com/RfidResearchGroup/proxmark3/pull/1742#issuecomment-1206113976 +A00027000099 +A00016000028 +A00003000028 +A0000F000345 +A00001000030 +A00002000086 +A00002000036 +A00002000088 +A00000000058 +A00000000096 +A00000000008 +A00000043D79 +A00000000064 +A00025000030 +A00003000057 +# +# BH USA 2013 conference +012279BAD3E5 +# +# iGuard Simple (and reverse) keys +AAAAAAFFFFFF +FFFFFFAAAAAA +# +# Random Hotel A Key Sec 0 Blk 3 - KABA Lock (VideoMan) +3111A3A303EB +# Transport system Uruguay - STM +# Shared key - sec 0 blk 3 +D144BD193063 +# +# Data from http://www.proxmark.org/forum/viewtopic.php?pid=45659#p45659 +3515AE068CAD +# +# Keys Catering +6A0D531DA1A7 +4BB29463DC29 +# +# Keys Swim +8627C10A7014 +453857395635 +# +# Unknown hotel system Sec 0 / A +353038383134 +# +# Brazil transport Sec 8 / A +50d4c54fcdf5 +# # Bandai Namco Passport [fka Banapassport] / Sega Aime Card +# Dumped on the Flipper Devices Discord Server 6090D00632F5 019761AA8082 574343467632 @@ -1351,7 +2207,551 @@ E69DD9015A43 C8382A233993 7B304F2A12A6 FC9418BF788B +# +# Guest Cashless Prepaid Arcade Payment Cards +168168168168 +198407157610 +4E4F584D2101 +4E4F584D2105 +686B35333376 +861861861861 +# +# Transport System Cracow / Polen +B071A76BA2E9 +B3A181BCA5F2 +3225942F7717 +80D00703C5FB +6DD510E080B1 +87529F30FC58 +B75C4FA614AE +42DC568C64F4 +# Data from "the more the marriott" mifare project (colonelborkmundus) +# aka The Horde +# +# These keys seem to be from Vingcard / Saflok system which means they are diversified +# and not static default keys. To verify this, the UID from such a card is needed. +# +# 20230125-01, Elite Member Marriott Rewards +43012BD9EB87 +# 20230125-02, Elite Member Marriott Rewards +3119A70628EB +# 20230125-03, Elite Member Marriott Rewards +23C9FDD9A366 +# 20230125-04, Elite Member Marriott Rewards +7B4DFC6D6525 +# 20230125-05, Elite Member Marriott Rewards +1330824CD356 +# 20230125-06, Elite Member Marriott Rewards +30AAD6A711EF +# 20230125-07, Fairfield Inn & Suites Marriott +7B3B589A5525 +# 20230125-08, Moxy Hotels +20C166C00ADB +# 20230125-09, Westin Hotels & Resorts +7D0A1C277C05 +2058580A941F +8C29F8320617 +# 20230125-10, Westin Hotels & Resorts +C40964215509 +D44CFC178460 +5697519A8F02 +# 20230125-12, AC Hotels Marriott +7B56B2B38725 +# 20230125-13, AC Hotels Marriott +8EA8EC3F2320 +# 20230125-14, Waldorf Astoria Chicago +011C6CF459E8 +# 20230125-24, Aria Resort & Casino +A18D9F4E75AF +# 20230125-25, Aria Resort & Casino +316B8FAA12EF +# 20230125-26, Residence Inn Mariott +3122AE5341EB +# 20230125-27, Residence Inn Mariott +F72CD208FDF9 +# 20230125-28, Marriott +035C70558D7B +# 20230125-29, Marriott +12AB4C37BB8B +# 20230125-30, Marriott +9966588CB9A0 +# 20230125-31, Sheraton +42FC522DE987 +# 20230125-32, The Industrialist +2158E314C3DF +# 20230125-39, The Ritz-Carlton Balharbour +30FB20D0EFEF +# 20230125-40, The Ritz-Carlton Balharbour +66A3B064CC4B +# 20230125-41, The Ritz-Carlton Balharbour +D18296CD9E6E +# 20230125-42, The Ritz-Carlton Balharbour +D20289CD9E6E +# 20230125-44, Graduate Hotels +209A2B910545 +C49DAE1C6049 +# 20230125-46, AmericInn +8AC04C1A4A25 +# 20230129-53, Marriott Bonvoy +6E029927600D +3E173F64C01C +C670A9AD6066 +# 20230413-69, Westin +487339FA02E0 +# 20230413-70, Marriott Bonvoy +DBD5CA4EE467 +A0B1F234006C +180DE12B700E +# 20230413-71, Westin +1352C68F7A56 +# 20230413-76, Ritz Carlton +318BD98C1CEF +# 20230413-77, Marriott +D23C1CB1216E +# 20230413-78, Caesars +A1D92F808CAF +# 20230413-79, The Cosmopolitan, Vegas +# 20230413-80, Aria +1153C319B4F8 +# 20230413-81, Aria +110C819BBEF8 +# 20230413-82, Aria +1332117E8756 +# 20230413-83, Kimpton +500AE915F50A +5032E362B484 +8B63AB712753 +# 20230413-85, Kimpton +06106E187106 +2E45C23DC541 +D9FF8BEE7550 +# 20230413-87, Marriott +42F7A186BF87 +# 20230413-88, Meritage Resort +D213B093B79A +# 20230413-89, Meritage Resort +216024C49EDF +# 20230413-90, Gaylord Palms +D201DBB6AB6E +# 20230413-91, Residence Inn +9F4AD875BB30 +# 20230413-92, Marriott +3352DB1E8777 +# 20230413-94, Marriott +09074A146605 +151F3E85EC46 +# +# Travelodge by Wyndham Berkeley +0000FFFFFFFF +4663ACD2FFFF +EDC317193709 +# Hotel Santa Cruz +75FAB77E2E5B +# saflok brand HOTEL key +32F093536677 +# A WaterFront Hotel in Oakland +3351916B5A77 +# Ballys (2018) +336E34CC2177 +# Random Hawaiian Hotel +A1670589B2AF +# SF Hotel (SoMa area) +2E0F00700000 +# +# Unknown PACS from Western Australia +CA80E51FA52B +A71E80EA35E1 +05597810D63D +# +# Hotel Key from Las Vegas +EA0CA627FD06 +80BB8436024C +5044068C5183 +# +# Key from Hotel M Montreal (probably diversified) +7E5E05866ED6 +661ABF99AFAD +# +# Key from evo Montreal (probably diversified) +1064BA5D6DF8 +# Hotel key +CE0F4F15E909 +D60DE9436219 +# +# ATM Area de Girona, spanish transport card +A01000000000 +A02000000000 +A03000000000 +A04000000000 +A05000000000 +A06000000000 +A07000000000 +A08000000000 +A09000000000 +A10000000000 +A11000000000 +A12000000000 +A13000000000 +A14000000000 +A15000000000 +B01000000000 +B02000000000 +B03000000000 +B04000000000 +B05000000000 +B06000000000 +B07000000000 +B08000000000 +B09000000000 +B10000000000 +B11000000000 +B12000000000 +B13000000000 +B14000000000 +B15000000000 +# +# Pittsburgh, PA, USA - Pittsburgh Regional Transit ConnectCard +A7AE4A5A33DC +6B857B568C10 +E2CE9A674CBE +A4896B2EBA4E +0724DF9AEDE8 +0E368FB140C1 +874EB25C8721 +5C313F4539CD +C5498606E0A8 +79C69F7EC7C0 +DA7DD0044DA2 +1B8189BD966B +765584147990 +4B7C7C315E6E +46CAAD12C524 +53BD03DEA5C9 +D2D72CB60F59 +14D258786538 +E2E89A375B36 +B3FA87DB0C45 +44D3B1561B34 +2817C6E02F97 +A513FF1232E9 +BD454BD52792 +391771654DC8 +5162797F8E1C +F700BD8E042D +3973ABFD8B66 +CE8BFF3728EE +09938D05DA78 +EACDA4DBE420 +EC2B9FD483CA +# +# Hotel Intelier Orange - Benicasim, Spain +# block 1 - key A +04256CFE0425 +# +# InsideWash Membership Card - Portugal +C18063858BB9 +# +# An apartment building in Sydney Olympic Park +13254608D0AB +24A2971BC0B2 +14264709D1AC +25A3981CC1B3 +1527480AD2AD +26A4991DC2B4 +1628490BD3AE +27A59A1EC3B5 +17294A0CD4AF +28A69B1FC4B6 +182A4B0DD5B0 +29A79C20C5B7 +192B4C0ED6B1 +2AA89D21C6B8 +1A2C4D0FD7B2 +2BA99E22C7B9 +1B2D4E10D8B3 +2CAA9F23C8BA +1C2E4F11D9B4 +2DABA024C9BB +1D2F5012DAB5 +2EACA125CABC +1E305113DBB6 +2FADA226CBBD +1F315214DCB7 +30AEA327CCBE +20325315DDB8 +31AFA428CDBF +21335416DEB9 +32B0A529CEC0 +22345517DFBA +33B1A62ACFC1 +# +# Universidade de São Paulo (USP) student card +17B50E38F1B0 +24E311F594CE +3794FBFB1A54 +43B229069F6A +4531952F765F +4943F2F35E0A +4985E681EF88 +4F56C88E0337 +710070E92C79 +8A036C5C35D4 +A027BD830A06 +D33673C19243 +D89A506542F2 +E5813CD228F1 +FAB943906E9C +# +# R.A.T.T transport card key A/B +AA034F342A55 +456776908C48 +# BusFacil - Brazilian public transport card for some cities +7b296f353c6b +3fa7217ec575 +fae9b14365a9 +c567dd4a6004 +c567dd4a6005 +c567dd4a6006 +c567dd4a6007 +c567dd4a6008 +c567dd4a6009 +c567dd4a600a +c567dd4a600d +c567dd4a600e +c567dd4a600f +5ef014ec5d7f +5086052022ac +bd6af9754c18 +5d67d4732a7d +17fe45604a04 +17fe45604a05 +17fe45604a06 +17fe45604a07 +17fe45604a08 +17fe45604a09 +17fe45604a0a +17fe45604a0d +17fe45604a0e +17fe45604a0f +# keys for swimming pool cards in Reykjavík Iceland +28220F14BEF0 +# key for Orkan keyfobs +300724070486 +# key for Atlantsolía keyfobs +60FCB3C42ABF +# key for hotel in greece +722F24F0722F +# STS Hotel 2A +535453535453 +# Public transport in Sofia, Bulgaria (SKGT) +# upgraded to DESFire since January 2024 +# SKGT common +# Sector 15, key A +f618b3d7855a +# Sector 15, key B +f1afa4da949f +# SKGT multi-use ticket +# Sector 0 +67362dace527 +633a010fa3c3 +# Sector 1 +f93c98655b9c +67ec0a47b0fb +# Sector 2 +54a028818ac7 +b2e87e53c5a0 +# Sector 3 +3e93cf0644b6 +79e12280e219 +# Sector 4 +2204b9fbf033 +4537fd238c8e +# Sector 5 +d1b44a9df05f +cfa526835a1f +# Sector 6 +21cc007ad81c +c097d0a85446 +# Sector 7 +d2268262710f +730bb7b8b3de +# Sector 8 +9fe7c5be7dff +61ae2d920c79 +# Sector 9 +78fcd4470c50 +b638caf7357b +# Sector 10 +0dc1dd7c8ea2 +4c6a6866b934 +# Sector 11 +03de2ceb2ea1 +93e0118b21ed +# Sector 12 +8fbced387bf4 +f57ca95c6edd +# Sector 13 +ef24fe3b4cf7 +8b44d303d62f +# Sector 14 +b1ea40b2caa6 +3abf8431003b +# Sector 15 - see above +# SKGT personalised subscription card +# Sector 0, 2, 16, key A +a0a1a2a3a4a5 +# Sector 8-14, 17-39, key A +ffffffffffff +# Sector 1, key A +# blue +f1df0ca8948b +# yellow +7747b4912984 +# Sector 3, key A +# blue +09d556d57a4b +# yellow +3ed158c6934e +# Sector 4-7, key A +# blue +839dedbfec0d +# yellow +c694a9ed2f9e +# Sector 15 - see above +# Sector 0, 16, key B (blue) +81d55f4551b9 +# Sector 0, key B (yellow) +6e9a040c3c91 +# Sector 1, key B +# blue +5b72c63fb416 +# yellow +a3cdced46371 +# Sector 2, key B +# blue +87a61433d026 +# yellow +9cd3a81f11ab +# Sector 3, key B +# blue +7070d331360c +# yellow +836c790f6e2c +# Sector 4-7, key B +# blue +7fe057787c4f +# yellow +ff59c6d13f88 +# Sector 8-14, 17-39, key B +536f6669614d +# Sector 15 - see above +# End of SKGT +# Hanoi Bus Rapid Transit - 1/2/3 A +AAAFBA10FC37 +C61F2C28DADF +23AACA30CBF2 +# +# Keys dumped from student ID (AGH Cracow - Poland), may be diversified +# Need to be verified +833E4F32589E +432D02DA59F3 +5C161CA2716F +F60B5F9666B8 +98EAC5321D2F +CC945E3FE5C4 +70783C436CF4 +2D186C7149A9 +5D60AC0939FC +93A5CE63C873 +87174550E900 +45675B25A3DA +F91750E629D5 +A3E662ABCDC8 +33D99E9FFA6A +FF7AABA39C61 +A8248C049BEA +C2AF731771C4 +9263B2E0DD80 +CE7FCCBBA5D8 +F8E385E5A2A0 +B27678B5C4AE +D68D7EBB9551 +7AB63F082328 +# +# Payment cards used by Eurest on certain campuses +7E2BC58168EB +# +# Shower cards provided by Seijsener +291A65CBEA7B +344A359BBAD9 +476572726974 +4D696368656C +4F3748E6C826 +69D40AF8B353 +72DEA10F21DF +74845AA8E3F1 +8C3C43EDCC55 +ACD30DFFB434 +D1A27C8EC5DF +F14D329CBDBE +# +# Hotel cards from Austria +AB287B3B4903 +7B0DEDA7E162 +# +#Metro Q transit cards from Huston, Texas +373B72D34B80 +BF3FFC245C9b +7D1D9E7CF8A7 +6A917BF357E6 +B1D461EC62CA +C6BECABEBE8A +66026782D435 +4547E34E40D9 +753897b99AAE +1C36761E8ABD +6D8FBD8CC524 +5A3274779706 +23F13602CC1A +511C1C2C9804 +F8B2B926555E +2593E37D9B2E +41A1F17EE990 +64DD48AEDE88 +7915ED4D9903 +D139DD71DB92 +216D97D46E88 +D9D1C447E427 +911E789433CB +93B43D689F85 +525A869053F1 +69B25667E0B4 +6AACA2D97645 +# UK London Office +435DF6296EC4 +2338B4913222 +# Acces card of students, and more in Occitanie, France +E9A553102EA5 +F982E971CFED +1F42AB9159EE +BBFB836A48B8 +B5D170B2E8F5 +E76978A05F10 +0B1A995DD007 +650DB9CEDB6B +13E54B4448B7 +3E3540C2C273 +A76152840117 +066CCC7666BC +3C0B3AC3AFA3 +CCB541598D72 +1988B5D48EC3 +892EEF0D30FB +0FE5CE5CC640 +# Volgograd (Russia) Volna transport cards keys +2B787A063D5D +D37C8F1793F7 # H World Hotel Chain Room Keys 543071543071 5F01015F0101 From 256c1a114013ff4a36487240b187c98b320ecbb1 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Tue, 17 Dec 2024 22:12:55 +0900 Subject: [PATCH 11/27] [FL-3917] Add the ability to send a signal once via RPC (#4000) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add the ability to send a signal once * Update protobuf * Fix sending infrared signals * Review changes * Update protobuf * Separate sending an IR signal once into a function * Update protobuf module --------- Co-authored-by: あく Co-authored-by: Georgii Surkov --- applications/main/infrared/infrared_app.c | 33 ++++++++++++++ applications/main/infrared/infrared_app_i.h | 14 ++++++ .../main/infrared/infrared_custom_event.h | 2 + .../main/infrared/scenes/infrared_scene_rpc.c | 43 +++++++++++++++++++ .../main/subghz/helpers/subghz_custom_event.h | 1 + .../main/subghz/scenes/subghz_scene_rpc.c | 37 ++++++++++++++++ applications/main/subghz/subghz.c | 3 ++ applications/services/rpc/rpc_app.c | 39 +++++++++++++++++ applications/services/rpc/rpc_app.h | 8 ++++ assets/protobuf | 2 +- 10 files changed, 181 insertions(+), 1 deletion(-) diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index a93fd766d..c50039760 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -88,6 +88,19 @@ static void infrared_rpc_command_callback(const RpcAppSystemEvent* event, void* view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressIndex); } + } else if(event->type == RpcAppEventTypeButtonPressRelease) { + furi_assert( + event->data.type == RpcAppSystemEventDataTypeString || + event->data.type == RpcAppSystemEventDataTypeInt32); + if(event->data.type == RpcAppSystemEventDataTypeString) { + furi_string_set(infrared->button_name, event->data.string); + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressReleaseName); + } else { + infrared->app_state.current_button_index = event->data.i32; + view_dispatcher_send_custom_event( + infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPressReleaseIndex); + } } else if(event->type == RpcAppEventTypeButtonRelease) { view_dispatcher_send_custom_event( infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease); @@ -411,6 +424,26 @@ void infrared_tx_stop(InfraredApp* infrared) { infrared->app_state.last_transmit_time = furi_get_tick(); } +void infrared_tx_send_once(InfraredApp* infrared) { + if(infrared->app_state.is_transmitting) { + return; + } + + dolphin_deed(DolphinDeedIrSend); + infrared_signal_transmit(infrared->current_signal); +} + +InfraredErrorCode infrared_tx_send_once_button_index(InfraredApp* infrared, size_t button_index) { + furi_assert(button_index < infrared_remote_get_signal_count(infrared->remote)); + + InfraredErrorCode error = infrared_remote_load_signal( + infrared->remote, infrared->current_signal, infrared->app_state.current_button_index); + if(!INFRARED_ERROR_PRESENT(error)) { + infrared_tx_send_once(infrared); + } + + return error; +} void infrared_blocking_task_start(InfraredApp* infrared, FuriThreadCallback callback) { view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewLoading); furi_thread_set_callback(infrared->task_thread, callback); diff --git a/applications/main/infrared/infrared_app_i.h b/applications/main/infrared/infrared_app_i.h index 692cc9671..75d8502f2 100644 --- a/applications/main/infrared/infrared_app_i.h +++ b/applications/main/infrared/infrared_app_i.h @@ -218,6 +218,20 @@ InfraredErrorCode infrared_tx_start_button_index(InfraredApp* infrared, size_t b */ void infrared_tx_stop(InfraredApp* infrared); +/** + * @brief Transmit the currently loaded signal once. + * + * @param[in,out] infrared pointer to the application instance. + */ +void infrared_tx_send_once(InfraredApp* infrared); + +/** + * @brief Load the signal under the given index and transmit it once. + * + * @param[in,out] infrared pointer to the application instance. + */ +InfraredErrorCode infrared_tx_send_once_button_index(InfraredApp* infrared, size_t button_index); + /** * @brief Start a blocking task in a separate thread. * diff --git a/applications/main/infrared/infrared_custom_event.h b/applications/main/infrared/infrared_custom_event.h index 02d9a276f..2efc99f4b 100644 --- a/applications/main/infrared/infrared_custom_event.h +++ b/applications/main/infrared/infrared_custom_event.h @@ -21,6 +21,8 @@ enum InfraredCustomEventType { InfraredCustomEventTypeRpcButtonPressName, InfraredCustomEventTypeRpcButtonPressIndex, InfraredCustomEventTypeRpcButtonRelease, + InfraredCustomEventTypeRpcButtonPressReleaseName, + InfraredCustomEventTypeRpcButtonPressReleaseIndex, InfraredCustomEventTypeRpcSessionClose, InfraredCustomEventTypeGpioTxPinChanged, diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c index 8f9dc4338..35cd971d8 100644 --- a/applications/main/infrared/scenes/infrared_scene_rpc.c +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -124,6 +124,49 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { rpc_system_app_confirm(infrared->rpc_ctx, result); + } else if( + event.event == InfraredCustomEventTypeRpcButtonPressReleaseName || + event.event == InfraredCustomEventTypeRpcButtonPressReleaseIndex) { + bool result = false; + + // Send the signal once and stop + if(rpc_state == InfraredRpcStateLoaded) { + if(event.event == InfraredCustomEventTypeRpcButtonPressReleaseName) { + const char* button_name = furi_string_get_cstr(infrared->button_name); + size_t index; + const bool index_found = + infrared_remote_get_signal_index(infrared->remote, button_name, &index); + app_state->current_button_index = index_found ? (signed)index : + InfraredButtonIndexNone; + FURI_LOG_D(TAG, "Sending signal with name \"%s\"", button_name); + } else { + FURI_LOG_D( + TAG, "Sending signal with index \"%ld\"", app_state->current_button_index); + } + if(infrared->app_state.current_button_index != InfraredButtonIndexNone) { + InfraredErrorCode error = infrared_tx_send_once_button_index( + infrared, app_state->current_button_index); + if(!INFRARED_ERROR_PRESENT(error)) { + const char* remote_name = infrared_remote_get_name(infrared->remote); + infrared_text_store_set(infrared, 0, "emulating\n%s", remote_name); + + infrared_scene_rpc_show(infrared); + result = true; + } else { + rpc_system_app_set_error_code( + infrared->rpc_ctx, RpcAppSystemErrorCodeInternalParse); + rpc_system_app_set_error_text( + infrared->rpc_ctx, "Cannot load button data"); + result = false; + } + } + } + + if(result) { + scene_manager_set_scene_state( + infrared->scene_manager, InfraredSceneRpc, InfraredRpcStateLoaded); + } + rpc_system_app_confirm(infrared->rpc_ctx, result); } else if( event.event == InfraredCustomEventTypeRpcExit || event.event == InfraredCustomEventTypeRpcSessionClose || diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index fe2c08fc6..571f3feb9 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -49,6 +49,7 @@ typedef enum { SubGhzCustomEventSceneRpcLoad, SubGhzCustomEventSceneRpcButtonPress, SubGhzCustomEventSceneRpcButtonRelease, + SubGhzCustomEventSceneRpcButtonPressRelease, SubGhzCustomEventSceneRpcSessionClose, SubGhzCustomEventViewReceiverOK, diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index c1476746d..040a15114 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -85,6 +85,43 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle); rpc_system_app_confirm(subghz->rpc_ctx, result); + } else if(event.event == SubGhzCustomEventSceneRpcButtonPressRelease) { + bool result = false; + if(state == SubGhzRpcStateLoaded) { + switch( + subghz_txrx_tx_start(subghz->txrx, subghz_txrx_get_fff_data(subghz->txrx))) { + case SubGhzTxRxStartTxStateErrorOnlyRx: + rpc_system_app_set_error_code( + subghz->rpc_ctx, RpcAppSystemErrorCodeRegionLock); + rpc_system_app_set_error_text( + subghz->rpc_ctx, + "Transmission on this frequency is restricted in your region"); + break; + case SubGhzTxRxStartTxStateErrorParserOthers: + rpc_system_app_set_error_code( + subghz->rpc_ctx, RpcAppSystemErrorCodeInternalParse); + rpc_system_app_set_error_text( + subghz->rpc_ctx, "Error in protocol parameters description"); + break; + + default: //if(SubGhzTxRxStartTxStateOk) + result = true; + subghz_blink_start(subghz); + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateTx); + break; + } + } + + // Stop transmission + if(state == SubGhzRpcStateTx) { + subghz_txrx_stop(subghz->txrx); + subghz_blink_stop(subghz); + result = true; + } + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneRpc, SubGhzRpcStateIdle); + rpc_system_app_confirm(subghz->rpc_ctx, result); } else if(event.event == SubGhzCustomEventSceneRpcLoad) { bool result = false; if(state == SubGhzRpcStateIdle) { diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 22e81f2eb..b17a232a8 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -43,6 +43,9 @@ static void subghz_rpc_command_callback(const RpcAppSystemEvent* event, void* co } else if(event->type == RpcAppEventTypeButtonRelease) { view_dispatcher_send_custom_event( subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease); + } else if(event->type == RpcAppEventTypeButtonPressRelease) { + view_dispatcher_send_custom_event( + subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPressRelease); } else { rpc_system_app_confirm(subghz->rpc_ctx, false); } diff --git a/applications/services/rpc/rpc_app.c b/applications/services/rpc/rpc_app.c index aa2a3f64f..2b9a6542d 100644 --- a/applications/services/rpc/rpc_app.c +++ b/applications/services/rpc/rpc_app.c @@ -258,6 +258,41 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context) } } +static void rpc_system_app_button_press_release(const PB_Main* request, void* context) { + furi_assert(request); + furi_assert(request->which_content == PB_Main_app_button_press_release_request_tag); + + RpcAppSystem* rpc_app = context; + furi_assert(rpc_app); + + if(rpc_app->callback) { + FURI_LOG_D(TAG, "ButtonPressRelease"); + + RpcAppSystemEvent event; + event.type = RpcAppEventTypeButtonPressRelease; + + if(strlen(request->content.app_button_press_release_request.args) != 0) { + event.data.type = RpcAppSystemEventDataTypeString; + event.data.string = request->content.app_button_press_release_request.args; + } else { + event.data.type = RpcAppSystemEventDataTypeInt32; + event.data.i32 = request->content.app_button_press_release_request.index; + } + + rpc_system_app_error_reset(rpc_app); + rpc_system_app_set_last_command(rpc_app, request->command_id, &event); + + rpc_app->callback(&event, rpc_app->callback_context); + + } else { + rpc_system_app_send_error_response( + rpc_app, + request->command_id, + PB_CommandStatus_ERROR_APP_NOT_RUNNING, + "ButtonPressRelease"); + } +} + static void rpc_system_app_get_error_process(const PB_Main* request, void* context) { furi_assert(request); furi_assert(request->which_content == PB_Main_app_get_error_request_tag); @@ -332,6 +367,7 @@ void rpc_system_app_confirm(RpcAppSystem* rpc_app, bool result) { rpc_app->last_event_type == RpcAppEventTypeLoadFile || rpc_app->last_event_type == RpcAppEventTypeButtonPress || rpc_app->last_event_type == RpcAppEventTypeButtonRelease || + rpc_app->last_event_type == RpcAppEventTypeButtonPressRelease || rpc_app->last_event_type == RpcAppEventTypeDataExchange); const uint32_t last_command_id = rpc_app->last_command_id; @@ -432,6 +468,9 @@ void* rpc_system_app_alloc(RpcSession* session) { rpc_handler.message_handler = rpc_system_app_button_release; rpc_add_handler(session, PB_Main_app_button_release_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_app_button_press_release; + rpc_add_handler(session, PB_Main_app_button_press_release_request_tag, &rpc_handler); + rpc_handler.message_handler = rpc_system_app_get_error_process; rpc_add_handler(session, PB_Main_app_get_error_request_tag, &rpc_handler); diff --git a/applications/services/rpc/rpc_app.h b/applications/services/rpc/rpc_app.h index aa6fd81cc..377d9ccb3 100644 --- a/applications/services/rpc/rpc_app.h +++ b/applications/services/rpc/rpc_app.h @@ -90,6 +90,13 @@ typedef enum { * all activities to be conducted while a button is being pressed. */ RpcAppEventTypeButtonRelease, + /** + * @brief The client has informed the application that a button has been pressed and released. + * + * This command's meaning is application-specific, e.g. to perform an action + * once without repeating it. + */ + RpcAppEventTypeButtonPressRelease, /** * @brief The client has sent a byte array of arbitrary size. * @@ -162,6 +169,7 @@ void rpc_system_app_send_exited(RpcAppSystem* rpc_app); * - RpcAppEventTypeLoadFile * - RpcAppEventTypeButtonPress * - RpcAppEventTypeButtonRelease + * - RpcAppEventTypeButtonPressRelease * - RpcAppEventTypeDataExchange * * Not confirming these events will result in a client-side timeout. diff --git a/assets/protobuf b/assets/protobuf index 6c7c0d55e..1c84fa489 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 6c7c0d55e82cb89223cf4890a540af4cff837fa7 +Subproject commit 1c84fa48919cbb71d1cc65236fc0ee36740e24c6 From 99175796199ef4e0d6a166d3beb7db18c8fc071c Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 18 Dec 2024 21:23:29 +0200 Subject: [PATCH 12/27] Increase system stack's reserved memory size (#4025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- targets/f7/stm32wb55xx_flash.ld | 2 +- targets/f7/stm32wb55xx_ram_fw.ld | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/targets/f7/stm32wb55xx_flash.ld b/targets/f7/stm32wb55xx_flash.ld index 524da6fc3..ef61bb238 100644 --- a/targets/f7/stm32wb55xx_flash.ld +++ b/targets/f7/stm32wb55xx_flash.ld @@ -3,7 +3,7 @@ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ _stack_end = 0x20030000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ -_stack_size = 0x200; /* required amount of stack */ +_stack_size = 0x400; /* required amount of stack */ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K diff --git a/targets/f7/stm32wb55xx_ram_fw.ld b/targets/f7/stm32wb55xx_ram_fw.ld index f0e8ad678..93579788d 100644 --- a/targets/f7/stm32wb55xx_ram_fw.ld +++ b/targets/f7/stm32wb55xx_ram_fw.ld @@ -3,7 +3,7 @@ ENTRY(Reset_Handler) /* Highest address of the user mode stack */ _stack_end = 0x20030000; /* end of RAM */ /* Generate a link error if heap and stack don't fit into RAM */ -_stack_size = 0x200; /* required amount of stack */ +_stack_size = 0x400; /* required amount of stack */ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K From 8d078e4b8cc7a3e2eb3aafc5f0a0055618511d26 Mon Sep 17 00:00:00 2001 From: Anna Antonenko Date: Thu, 19 Dec 2024 00:38:43 +0400 Subject: [PATCH 13/27] [FL-3927] FuriThread stdin (#3979) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: FuriThread stdin * ci: fix f18 * feat: stdio callback context Co-authored-by: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Co-authored-by: あく --- .../unit_tests/tests/furi/furi_stdio_test.c | 108 ++++++++++++++++++ .../debug/unit_tests/tests/furi/furi_test.c | 8 ++ applications/services/cli/cli.c | 10 +- applications/services/cli/cli_i.h | 3 +- applications/services/cli/cli_vcp.c | 9 +- furi/core/string.h | 8 +- furi/core/thread.c | 80 +++++++++++-- furi/core/thread.h | 59 +++++++++- lib/print/SConscript | 20 ++-- lib/print/wrappers.c | 47 +++++++- lib/print/wrappers.h | 6 + targets/f18/api_symbols.csv | 13 ++- targets/f7/api_symbols.csv | 13 ++- 13 files changed, 346 insertions(+), 38 deletions(-) create mode 100644 applications/debug/unit_tests/tests/furi/furi_stdio_test.c diff --git a/applications/debug/unit_tests/tests/furi/furi_stdio_test.c b/applications/debug/unit_tests/tests/furi/furi_stdio_test.c new file mode 100644 index 000000000..94e2f613b --- /dev/null +++ b/applications/debug/unit_tests/tests/furi/furi_stdio_test.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include "../test.h" // IWYU pragma: keep + +#define TAG "StdioTest" + +#define CONTEXT_MAGIC ((void*)0xDEADBEEF) + +// stdin + +static char mock_in[256]; +static size_t mock_in_len, mock_in_pos; + +static void set_mock_in(const char* str) { + size_t len = strlen(str); + strcpy(mock_in, str); + mock_in_len = len; + mock_in_pos = 0; +} + +static size_t mock_in_cb(char* buffer, size_t size, FuriWait wait, void* context) { + UNUSED(wait); + furi_check(context == CONTEXT_MAGIC); + size_t remaining = mock_in_len - mock_in_pos; + size = MIN(remaining, size); + memcpy(buffer, mock_in + mock_in_pos, size); + mock_in_pos += size; + return size; +} + +void test_stdin(void) { + FuriThreadStdinReadCallback in_cb = furi_thread_get_stdin_callback(); + furi_thread_set_stdin_callback(mock_in_cb, CONTEXT_MAGIC); + char buf[256]; + + // plain in + set_mock_in("Hello, World!\n"); + fgets(buf, sizeof(buf), stdin); + mu_assert_string_eq("Hello, World!\n", buf); + mu_assert_int_eq(EOF, getchar()); + + // ungetc + ungetc('i', stdin); + ungetc('H', stdin); + fgets(buf, sizeof(buf), stdin); + mu_assert_string_eq("Hi", buf); + mu_assert_int_eq(EOF, getchar()); + + // ungetc + plain in + set_mock_in(" World"); + ungetc('i', stdin); + ungetc('H', stdin); + fgets(buf, sizeof(buf), stdin); + mu_assert_string_eq("Hi World", buf); + mu_assert_int_eq(EOF, getchar()); + + // partial plain in + set_mock_in("Hello, World!\n"); + fgets(buf, strlen("Hello") + 1, stdin); + mu_assert_string_eq("Hello", buf); + mu_assert_int_eq(',', getchar()); + fgets(buf, sizeof(buf), stdin); + mu_assert_string_eq(" World!\n", buf); + + furi_thread_set_stdin_callback(in_cb, CONTEXT_MAGIC); +} + +// stdout + +static FuriString* mock_out; +FuriThreadStdoutWriteCallback original_out_cb; + +static void mock_out_cb(const char* data, size_t size, void* context) { + furi_check(context == CONTEXT_MAGIC); + // there's no furi_string_cat_strn :( + for(size_t i = 0; i < size; i++) { + furi_string_push_back(mock_out, data[i]); + } +} + +static void assert_and_clear_mock_out(const char* expected) { + // return the original stdout callback for the duration of the check + // if the check fails, we don't want the error to end up in our buffer, + // we want to be able to see it! + furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC); + mu_assert_string_eq(expected, furi_string_get_cstr(mock_out)); + furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC); + + furi_string_reset(mock_out); +} + +void test_stdout(void) { + original_out_cb = furi_thread_get_stdout_callback(); + furi_thread_set_stdout_callback(mock_out_cb, CONTEXT_MAGIC); + mock_out = furi_string_alloc(); + + puts("Hello, World!"); + assert_and_clear_mock_out("Hello, World!\n"); + + printf("He"); + printf("llo!"); + fflush(stdout); + assert_and_clear_mock_out("Hello!"); + + furi_string_free(mock_out); + furi_thread_set_stdout_callback(original_out_cb, CONTEXT_MAGIC); +} diff --git a/applications/debug/unit_tests/tests/furi/furi_test.c b/applications/debug/unit_tests/tests/furi/furi_test.c index 193a8124d..f23be37a9 100644 --- a/applications/debug/unit_tests/tests/furi/furi_test.c +++ b/applications/debug/unit_tests/tests/furi/furi_test.c @@ -10,6 +10,8 @@ void test_furi_memmgr(void); void test_furi_event_loop(void); void test_errno_saving(void); void test_furi_primitives(void); +void test_stdin(void); +void test_stdout(void); static int foo = 0; @@ -52,6 +54,11 @@ MU_TEST(mu_test_furi_primitives) { test_furi_primitives(); } +MU_TEST(mu_test_stdio) { + test_stdin(); + test_stdout(); +} + MU_TEST_SUITE(test_suite) { MU_SUITE_CONFIGURE(&test_setup, &test_teardown); MU_RUN_TEST(test_check); @@ -61,6 +68,7 @@ MU_TEST_SUITE(test_suite) { MU_RUN_TEST(mu_test_furi_pubsub); MU_RUN_TEST(mu_test_furi_memmgr); MU_RUN_TEST(mu_test_furi_event_loop); + MU_RUN_TEST(mu_test_stdio); MU_RUN_TEST(mu_test_errno_saving); MU_RUN_TEST(mu_test_furi_primitives); } diff --git a/applications/services/cli/cli.c b/applications/services/cli/cli.c index 0d8f52c04..28ba417c2 100644 --- a/applications/services/cli/cli.c +++ b/applications/services/cli/cli.c @@ -431,9 +431,9 @@ void cli_session_open(Cli* cli, void* session) { cli->session = session; if(cli->session != NULL) { cli->session->init(); - furi_thread_set_stdout_callback(cli->session->tx_stdout); + furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL); } else { - furi_thread_set_stdout_callback(NULL); + furi_thread_set_stdout_callback(NULL, NULL); } furi_semaphore_release(cli->idle_sem); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); @@ -447,7 +447,7 @@ void cli_session_close(Cli* cli) { cli->session->deinit(); } cli->session = NULL; - furi_thread_set_stdout_callback(NULL); + furi_thread_set_stdout_callback(NULL, NULL); furi_check(furi_mutex_release(cli->mutex) == FuriStatusOk); } @@ -461,9 +461,9 @@ int32_t cli_srv(void* p) { furi_record_create(RECORD_CLI, cli); if(cli->session != NULL) { - furi_thread_set_stdout_callback(cli->session->tx_stdout); + furi_thread_set_stdout_callback(cli->session->tx_stdout, NULL); } else { - furi_thread_set_stdout_callback(NULL); + furi_thread_set_stdout_callback(NULL, NULL); } if(furi_hal_rtc_get_boot_mode() == FuriHalRtcBootModeNormal) { diff --git a/applications/services/cli/cli_i.h b/applications/services/cli/cli_i.h index d4cac6e7d..d7351b9ff 100644 --- a/applications/services/cli/cli_i.h +++ b/applications/services/cli/cli_i.h @@ -28,8 +28,9 @@ struct CliSession { void (*init)(void); void (*deinit)(void); size_t (*rx)(uint8_t* buffer, size_t size, uint32_t timeout); + size_t (*rx_stdin)(uint8_t* buffer, size_t size, uint32_t timeout, void* context); void (*tx)(const uint8_t* buffer, size_t size); - void (*tx_stdout)(const char* data, size_t size); + void (*tx_stdout)(const char* data, size_t size, void* context); bool (*is_connected)(void); }; diff --git a/applications/services/cli/cli_vcp.c b/applications/services/cli/cli_vcp.c index cdabaaa05..aa399e78a 100644 --- a/applications/services/cli/cli_vcp.c +++ b/applications/services/cli/cli_vcp.c @@ -242,6 +242,11 @@ static size_t cli_vcp_rx(uint8_t* buffer, size_t size, uint32_t timeout) { return rx_cnt; } +static size_t cli_vcp_rx_stdin(uint8_t* data, size_t size, uint32_t timeout, void* context) { + UNUSED(context); + return cli_vcp_rx(data, size, timeout); +} + static void cli_vcp_tx(const uint8_t* buffer, size_t size) { furi_assert(vcp); furi_assert(buffer); @@ -267,7 +272,8 @@ static void cli_vcp_tx(const uint8_t* buffer, size_t size) { VCP_DEBUG("tx %u end", size); } -static void cli_vcp_tx_stdout(const char* data, size_t size) { +static void cli_vcp_tx_stdout(const char* data, size_t size, void* context) { + UNUSED(context); cli_vcp_tx((const uint8_t*)data, size); } @@ -310,6 +316,7 @@ CliSession cli_vcp = { cli_vcp_init, cli_vcp_deinit, cli_vcp_rx, + cli_vcp_rx_stdin, cli_vcp_tx, cli_vcp_tx_stdout, cli_vcp_is_connected, diff --git a/furi/core/string.h b/furi/core/string.h index 84b8c6a24..0d407356b 100644 --- a/furi/core/string.h +++ b/furi/core/string.h @@ -129,12 +129,12 @@ void furi_string_swap(FuriString* string_1, FuriString* string_2); /** Move string_2 content to string_1. * - * Set the string to the other one, and destroy the other one. + * Copy data from one string to another and destroy the source. * - * @param string_1 The FuriString instance 1 - * @param string_2 The FuriString instance 2 + * @param destination The destination FuriString + * @param source The source FuriString */ -void furi_string_move(FuriString* string_1, FuriString* string_2); +void furi_string_move(FuriString* destination, FuriString* source); /** Compute a hash for the string. * diff --git a/furi/core/thread.c b/furi/core/thread.c index fd576ea72..6e5157957 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -23,12 +23,17 @@ #define THREAD_MAX_STACK_SIZE (UINT16_MAX * sizeof(StackType_t)) -typedef struct FuriThreadStdout FuriThreadStdout; - -struct FuriThreadStdout { +typedef struct { FuriThreadStdoutWriteCallback write_callback; FuriString* buffer; -}; + void* context; +} FuriThreadStdout; + +typedef struct { + FuriThreadStdinReadCallback read_callback; + FuriString* unread_buffer; // output.buffer = furi_string_alloc(); + thread->input.unread_buffer = furi_string_alloc(); FuriThread* parent = NULL; if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) { @@ -245,6 +252,7 @@ void furi_thread_free(FuriThread* thread) { } furi_string_free(thread->output.buffer); + furi_string_free(thread->input.unread_buffer); free(thread); } @@ -710,13 +718,22 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) { static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) { if(thread->output.write_callback != NULL) { - thread->output.write_callback(data, size); + thread->output.write_callback(data, size, thread->output.context); } else { furi_log_tx((const uint8_t*)data, size); } return size; } +static size_t + __furi_thread_stdin_read(FuriThread* thread, char* data, size_t size, FuriWait timeout) { + if(thread->input.read_callback != NULL) { + return thread->input.read_callback(data, size, timeout, thread->input.context); + } else { + return 0; + } +} + static int32_t __furi_thread_stdout_flush(FuriThread* thread) { FuriString* buffer = thread->output.buffer; size_t size = furi_string_size(buffer); @@ -727,19 +744,33 @@ static int32_t __furi_thread_stdout_flush(FuriThread* thread) { return 0; } -void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { - FuriThread* thread = furi_thread_get_current(); - furi_check(thread); - __furi_thread_stdout_flush(thread); - thread->output.write_callback = callback; -} - FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void) { FuriThread* thread = furi_thread_get_current(); furi_check(thread); return thread->output.write_callback; } +FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void) { + FuriThread* thread = furi_thread_get_current(); + furi_check(thread); + return thread->input.read_callback; +} + +void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context) { + FuriThread* thread = furi_thread_get_current(); + furi_check(thread); + __furi_thread_stdout_flush(thread); + thread->output.write_callback = callback; + thread->output.context = context; +} + +void furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context) { + FuriThread* thread = furi_thread_get_current(); + furi_check(thread); + thread->input.read_callback = callback; + thread->input.context = context; +} + size_t furi_thread_stdout_write(const char* data, size_t size) { FuriThread* thread = furi_thread_get_current(); furi_check(thread); @@ -772,6 +803,31 @@ int32_t furi_thread_stdout_flush(void) { return __furi_thread_stdout_flush(thread); } +size_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout) { + FuriThread* thread = furi_thread_get_current(); + furi_check(thread); + + size_t from_buffer = MIN(furi_string_size(thread->input.unread_buffer), size); + size_t from_input = size - from_buffer; + size_t from_input_actual = + __furi_thread_stdin_read(thread, buffer + from_buffer, from_input, timeout); + memcpy(buffer, furi_string_get_cstr(thread->input.unread_buffer), from_buffer); + furi_string_right(thread->input.unread_buffer, from_buffer); + + return from_buffer + from_input_actual; +} + +void furi_thread_stdin_unread(char* buffer, size_t size) { + FuriThread* thread = furi_thread_get_current(); + furi_check(thread); + + FuriString* new_buf = furi_string_alloc(); // there's no furi_string_alloc_set_strn :( + furi_string_set_strn(new_buf, buffer, size); + furi_string_cat(new_buf, thread->input.unread_buffer); + furi_string_free(thread->input.unread_buffer); + thread->input.unread_buffer = new_buf; +} + void furi_thread_suspend(FuriThreadId thread_id) { furi_check(thread_id); diff --git a/furi/core/thread.h b/furi/core/thread.h index ed7aa4553..9abfde5cd 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -74,8 +74,23 @@ typedef int32_t (*FuriThreadCallback)(void* context); * * @param[in] data pointer to the data to be written to the standard out * @param[in] size size of the data in bytes + * @param[in] context optional context */ -typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size); +typedef void (*FuriThreadStdoutWriteCallback)(const char* data, size_t size, void* context); + +/** + * @brief Standard input callback function pointer type + * + * The function to be used as a standard input callback MUST follow this signature. + * + * @param[out] buffer buffer to read data into + * @param[in] size maximum number of bytes to read into the buffer + * @param[in] timeout how long to wait for (in ticks) before giving up + * @param[in] context optional context + * @returns number of bytes that was actually read into the buffer + */ +typedef size_t ( + *FuriThreadStdinReadCallback)(char* buffer, size_t size, FuriWait timeout, void* context); /** * @brief State change callback function pointer type. @@ -468,13 +483,30 @@ uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); */ FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(void); +/** + * @brief Get the standard input callback for the current thead. + * + * @return pointer to the standard in callback function + */ +FuriThreadStdinReadCallback furi_thread_get_stdin_callback(void); + /** Set standard output callback for the current thread. * * @param[in] callback pointer to the callback function or NULL to clear + * @param[in] context context to be passed to the callback */ -void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback); +void furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback, void* context); + +/** Set standard input callback for the current thread. + * + * @param[in] callback pointer to the callback function or NULL to clear + * @param[in] context context to be passed to the callback + */ +void furi_thread_set_stdin_callback(FuriThreadStdinReadCallback callback, void* context); /** Write data to buffered standard output. + * + * @note You can also use the standard C `putc`, `puts`, `printf` and friends. * * @param[in] data pointer to the data to be written * @param[in] size data size in bytes @@ -489,6 +521,29 @@ size_t furi_thread_stdout_write(const char* data, size_t size); */ int32_t furi_thread_stdout_flush(void); +/** Read data from the standard input + * + * @note You can also use the standard C `getc`, `gets` and friends. + * + * @param[in] buffer pointer to the buffer to read data into + * @param[in] size how many bytes to read into the buffer + * @param[in] timeout how long to wait for (in ticks) before giving up + * @return number of bytes that was actually read + */ +size_t furi_thread_stdin_read(char* buffer, size_t size, FuriWait timeout); + +/** Puts data back into the standard input buffer + * + * `furi_thread_stdin_read` will return the bytes in the same order that they + * were supplied to this function. + * + * @note You can also use the standard C `ungetc`. + * + * @param[in] buffer pointer to the buffer to get data from + * @param[in] size how many bytes to read from the buffer + */ +void furi_thread_stdin_unread(char* buffer, size_t size); + /** * @brief Suspend a thread. * diff --git a/lib/print/SConscript b/lib/print/SConscript index 07be8d890..90028cf06 100644 --- a/lib/print/SConscript +++ b/lib/print/SConscript @@ -44,18 +44,24 @@ wrapped_fn_list = [ "vsiprintf", "vsniprintf", # - # Scanf is not implemented 4 now + # standard input + # + "fgetc", + "getc", + "getchar", + "fgets", + "ungetc", + # + # standard input, but unimplemented + # + "gets", + # + # scanf, not implemented for now # # "fscanf", # "scanf", # "sscanf", # "vsprintf", - # "fgetc", - # "fgets", - # "getc", - # "getchar", - # "gets", - # "ungetc", # "vfscanf", # "vscanf", # "vsscanf", diff --git a/lib/print/wrappers.c b/lib/print/wrappers.c index c8d72d192..18df92def 100644 --- a/lib/print/wrappers.c +++ b/lib/print/wrappers.c @@ -51,11 +51,54 @@ int __wrap_snprintf(char* str, size_t size, const char* format, ...) { } int __wrap_fflush(FILE* stream) { - UNUSED(stream); - furi_thread_stdout_flush(); + if(stream == stdout) furi_thread_stdout_flush(); return 0; } +int __wrap_fgetc(FILE* stream) { + if(stream != stdin) return EOF; + char c; + if(furi_thread_stdin_read(&c, 1, FuriWaitForever) == 0) return EOF; + return c; +} + +int __wrap_getc(FILE* stream) { + return __wrap_fgetc(stream); +} + +int __wrap_getchar(void) { + return __wrap_fgetc(stdin); +} + +char* __wrap_fgets(char* str, size_t n, FILE* stream) { + // leave space for the zero terminator + furi_check(n >= 1); + n--; + + if(stream != stdin) { + *str = '\0'; + return str; + } + + // read characters + int c; + do { + c = __wrap_fgetc(stdin); + if(c > 0) *(str++) = c; + } while(c != EOF && c != '\n' && --n); + + // place zero terminator + *str = '\0'; + return str; +} + +int __wrap_ungetc(int ch, FILE* stream) { + char c = ch; + if(stream != stdin) return EOF; + furi_thread_stdin_unread(&c, 1); + return ch; +} + __attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e) { UNUSED(file); UNUSED(line); diff --git a/lib/print/wrappers.h b/lib/print/wrappers.h index 3cec88249..8a4599b41 100644 --- a/lib/print/wrappers.h +++ b/lib/print/wrappers.h @@ -16,6 +16,12 @@ int __wrap_putc(int ch, FILE* stream); int __wrap_snprintf(char* str, size_t size, const char* format, ...); int __wrap_fflush(FILE* stream); +int __wrap_fgetc(FILE* stream); +int __wrap_getc(FILE* stream); +int __wrap_getchar(void); +char* __wrap_fgets(char* str, size_t n, FILE* stream); +int __wrap_ungetc(int ch, FILE* stream); + __attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e); __attribute__((__noreturn__)) void diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index b5d51a0dd..23421712d 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,78.1,, +Version,+,79.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, @@ -371,11 +371,16 @@ Function,-,__utoa,char*,"unsigned, char*, int" Function,+,__wrap___assert,void,"const char*, int, const char*" Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*" Function,+,__wrap_fflush,int,FILE* +Function,+,__wrap_fgetc,int,FILE* +Function,+,__wrap_fgets,char*,"char*, size_t, FILE*" +Function,+,__wrap_getc,int,FILE* +Function,+,__wrap_getchar,int, Function,+,__wrap_printf,int,"const char*, ..." Function,+,__wrap_putc,int,"int, FILE*" Function,+,__wrap_putchar,int,int Function,+,__wrap_puts,int,const char* Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." +Function,+,__wrap_ungetc,int,"int, FILE*" Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." @@ -1654,6 +1659,7 @@ Function,+,furi_thread_get_return_code,int32_t,FuriThread* Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread* Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId Function,+,furi_thread_get_state,FuriThreadState,FuriThread* +Function,+,furi_thread_get_stdin_callback,FuriThreadStdinReadCallback, Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_join,_Bool,FuriThread* @@ -1674,9 +1680,12 @@ Function,+,furi_thread_set_signal_callback,void,"FuriThread*, FuriThreadSignalCa Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" -Function,+,furi_thread_set_stdout_callback,void,FuriThreadStdoutWriteCallback +Function,+,furi_thread_set_stdin_callback,void,"FuriThreadStdinReadCallback, void*" +Function,+,furi_thread_set_stdout_callback,void,"FuriThreadStdoutWriteCallback, void*" Function,+,furi_thread_signal,_Bool,"const FuriThread*, uint32_t, void*" Function,+,furi_thread_start,void,FuriThread* +Function,+,furi_thread_stdin_read,size_t,"char*, size_t, FuriWait" +Function,+,furi_thread_stdin_unread,void,"char*, size_t" Function,+,furi_thread_stdout_flush,int32_t, Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" Function,+,furi_thread_suspend,void,FuriThreadId diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index ee81f76a9..6f9fc5466 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,78.1,, +Version,+,79.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -448,11 +448,16 @@ Function,-,__utoa,char*,"unsigned, char*, int" Function,+,__wrap___assert,void,"const char*, int, const char*" Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*" Function,+,__wrap_fflush,int,FILE* +Function,+,__wrap_fgetc,int,FILE* +Function,+,__wrap_fgets,char*,"char*, size_t, FILE*" +Function,+,__wrap_getc,int,FILE* +Function,+,__wrap_getchar,int, Function,+,__wrap_printf,int,"const char*, ..." Function,+,__wrap_putc,int,"int, FILE*" Function,+,__wrap_putchar,int,int Function,+,__wrap_puts,int,const char* Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." +Function,+,__wrap_ungetc,int,"int, FILE*" Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." @@ -1873,6 +1878,7 @@ Function,+,furi_thread_get_return_code,int32_t,FuriThread* Function,+,furi_thread_get_signal_callback,FuriThreadSignalCallback,const FuriThread* Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId Function,+,furi_thread_get_state,FuriThreadState,FuriThread* +Function,+,furi_thread_get_stdin_callback,FuriThreadStdinReadCallback, Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_join,_Bool,FuriThread* @@ -1893,9 +1899,12 @@ Function,+,furi_thread_set_signal_callback,void,"FuriThread*, FuriThreadSignalCa Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" -Function,+,furi_thread_set_stdout_callback,void,FuriThreadStdoutWriteCallback +Function,+,furi_thread_set_stdin_callback,void,"FuriThreadStdinReadCallback, void*" +Function,+,furi_thread_set_stdout_callback,void,"FuriThreadStdoutWriteCallback, void*" Function,+,furi_thread_signal,_Bool,"const FuriThread*, uint32_t, void*" Function,+,furi_thread_start,void,FuriThread* +Function,+,furi_thread_stdin_read,size_t,"char*, size_t, FuriWait" +Function,+,furi_thread_stdin_unread,void,"char*, size_t" Function,+,furi_thread_stdout_flush,int32_t, Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" Function,+,furi_thread_suspend,void,FuriThreadId From 8c4922a32212a47b25fde268365cd0068003fbdc Mon Sep 17 00:00:00 2001 From: Emmanuel Ferdman Date: Wed, 18 Dec 2024 22:55:21 +0200 Subject: [PATCH 14/27] Update `infrared_test.c` reference (#3983) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Emmanuel Ferdman Co-authored-by: あく --- documentation/UnitTests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/UnitTests.md b/documentation/UnitTests.md index 9711c6ae1..5d04c8f67 100644 --- a/documentation/UnitTests.md +++ b/documentation/UnitTests.md @@ -43,7 +43,7 @@ To add unit tests for your protocol, follow these steps: 1. Create a file named `test_.irtest` in the [assets](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/debug/unit_tests/resources/unit_tests/infrared) directory. 2. Fill it with the test data (more on it below). -3. Add the test code to [infrared_test.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/infrared/infrared_test.c). +3. Add the test code to [infrared_test.c](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/applications/debug/unit_tests/tests/infrared/infrared_test.c). 4. Build and install firmware with resources, install it on your Flipper and run the tests to see if they pass. ##### Test data format From 9c96bbfc540b0719cb58095e88fb9dc06b5c61d2 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Thu, 19 Dec 2024 00:01:20 +0300 Subject: [PATCH 15/27] [NFC] Fix ISO15693 stucking in wrong mode. (#3988) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- lib/signal_reader/parsers/iso15693/iso15693_parser.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/signal_reader/parsers/iso15693/iso15693_parser.c b/lib/signal_reader/parsers/iso15693/iso15693_parser.c index a2c6912e6..e47c734a2 100644 --- a/lib/signal_reader/parsers/iso15693/iso15693_parser.c +++ b/lib/signal_reader/parsers/iso15693/iso15693_parser.c @@ -243,6 +243,8 @@ static Iso15693ParserCommand iso15693_parser_parse_1_out_of_256(Iso15693Parser* instance->parsed_frame, instance->next_byte_part * 4 + j / 2); } } + } else { + instance->zero_found = true; } } instance->next_byte_part = (instance->next_byte_part + 1) % 64; From a7b3a135815f403006b52ba55d7e82234378a596 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:33:59 +0100 Subject: [PATCH 16/27] Loader: Fix BusFault in handling of OOM (#3992) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Loader: Fix BusFault in handling of OOM * Furi: fix crash in aligned_alloc, cleanup aligned_alloc use Co-authored-by: あく --- furi/core/memmgr.c | 4 +++- lib/flipper_application/elf/elf_file.c | 12 +++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/furi/core/memmgr.c b/furi/core/memmgr.c index d3ff873ae..8ee0d1723 100644 --- a/furi/core/memmgr.c +++ b/furi/core/memmgr.c @@ -106,5 +106,7 @@ void* aligned_malloc(size_t size, size_t alignment) { } void aligned_free(void* p) { - free(((void**)p)[-1]); + if(p) { + free(((void**)p)[-1]); + } } diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index d0c4f52fb..bd8ecdf7e 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -8,11 +8,11 @@ #define TAG "Elf" -#define ELF_NAME_BUFFER_LEN 32 -#define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr)) -#define IS_FLAGS_SET(v, m) (((v) & (m)) == (m)) +#define ELF_NAME_BUFFER_LEN 32 +#define SECTION_OFFSET(e, n) ((e)->section_table + (n) * sizeof(Elf32_Shdr)) +#define IS_FLAGS_SET(v, m) (((v) & (m)) == (m)) #define RESOLVER_THREAD_YIELD_STEP 30 -#define FAST_RELOCATION_VERSION 1 +#define FAST_RELOCATION_VERSION 1 // #define ELF_DEBUG_LOG 1 @@ -830,9 +830,7 @@ void elf_file_free(ELFFile* elf) { for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) { const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it); - if(itref->value.data) { - aligned_free(itref->value.data); - } + aligned_free(itref->value.data); if(itref->value.fast_rel) { aligned_free(itref->value.fast_rel->data); free(itref->value.fast_rel); From 8dd5e64c0384edf93f33eaf01fce412c38a3858d Mon Sep 17 00:00:00 2001 From: Evgeny E <10674163+ssecsd@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:52:37 +0000 Subject: [PATCH 17/27] Move updater and unit tests to dockerized runner (#4028) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * extended unit_tests and changed to dockerized runner * added branch to run units * fixing unit-tests-output * online output * command not found fix * added stm logging * cleaned output * Updated updater test to work on dockerized runner * Test run for changed actions * small refactor of run_unit_tests * Final test of jobs * Checked * On-failure actions runs only on fail * Set action trigger to pull request * Bumped timeout a little * Removed extra steps * Removed stm monitor, as it's now part of docker-runner * fix: testops without stm_monitoring * fix: timeout extended Co-authored-by: あく --- .github/workflows/unit_tests.yml | 48 ++++------ .github/workflows/updater_test.yml | 54 ++--------- scripts/testops.py | 148 +++++++++++++++++++---------- 3 files changed, 127 insertions(+), 123 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index d37337452..309dd7ebd 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -5,54 +5,39 @@ on: env: TARGETS: f7 DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /opt + FBT_TOOLCHAIN_PATH: /opt/ FBT_GIT_SUBMODULE_SHALLOW: 1 jobs: run_units_on_bench: - runs-on: [self-hosted, FlipperZeroUnitTest] + runs-on: [ self-hosted, FlipperZeroTest ] steps: - - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 1 ref: ${{ github.event.pull_request.head.sha }} - - name: 'Get flipper from device manager (mock)' - id: device - run: | - echo "flipper=auto" >> $GITHUB_OUTPUT - - name: 'Flash unit tests firmware' id: flashing if: success() - timeout-minutes: 10 - run: | - ./fbt resources firmware_latest flash SWD_TRANSPORT_SERIAL=2A0906016415303030303032 LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1 - - - name: 'Wait for flipper and format ext' - id: format_ext - if: steps.flashing.outcome == 'success' - timeout-minutes: 5 + timeout-minutes: 20 run: | source scripts/toolchain/fbtenv.sh - python3 scripts/testops.py -p=${{steps.device.outputs.flipper}} -t=120 await_flipper - python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext + ./fbt resources firmware_latest flash LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1 + - name: 'Copy assets and unit data, reboot and wait for flipper' id: copy - if: steps.format_ext.outcome == 'success' + if: steps.flashing.outcome == 'success' timeout-minutes: 7 run: | source scripts/toolchain/fbtenv.sh - python3 scripts/testops.py -p=${{steps.device.outputs.flipper}} -t=15 await_flipper - rm -rf build/latest/resources/dolphin - python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send build/latest/resources /ext - python3 scripts/power.py -p ${{steps.device.outputs.flipper}} reboot - python3 scripts/testops.py -p=${{steps.device.outputs.flipper}} -t=15 await_flipper + python3 scripts/testops.py -t=15 await_flipper + python3 scripts/storage.py -f send build/latest/resources /ext + python3 scripts/storage.py -f send /region_data /ext/.int/.region_data + python3 scripts/power.py reboot + python3 scripts/testops.py -t=30 await_flipper - name: 'Run units and validate results' id: run_units @@ -60,9 +45,16 @@ jobs: timeout-minutes: 7 run: | source scripts/toolchain/fbtenv.sh - python3 scripts/testops.py run_units -p ${{steps.device.outputs.flipper}} + python3 scripts/testops.py run_units + + - name: 'Upload test results' + if: failure() && steps.flashing.outcome == 'success' && steps.run_units.outcome != 'skipped' + uses: actions/upload-artifact@v4 + with: + name: unit-tests_output + path: unit_tests*.txt - name: 'Check GDB output' if: failure() && steps.flashing.outcome == 'success' run: | - ./fbt gdb_trace_all SWD_TRANSPORT_SERIAL=2A0906016415303030303032 LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1 + ./fbt gdb_trace_all LIB_DEBUG=1 FIRMWARE_APP_SET=unit_tests FORCE=1 diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index dbe5df883..59cc6e716 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -1,39 +1,31 @@ name: 'Updater test' on: pull_request: + env: TARGETS: f7 DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /opt + FBT_TOOLCHAIN_PATH: /opt/ FBT_GIT_SUBMODULE_SHALLOW: 1 jobs: test_updater_on_bench: - runs-on: [self-hosted, FlipperZeroUpdaterTest] + runs-on: [self-hosted, FlipperZeroTest ] steps: - - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 1 - submodules: false ref: ${{ github.event.pull_request.head.sha }} - - name: 'Get flipper from device manager (mock)' - id: device - run: | - echo "flipper=auto" >> $GITHUB_OUTPUT - echo "stlink=0F020D026415303030303032" >> $GITHUB_OUTPUT - - name: 'Flashing target firmware' id: first_full_flash - timeout-minutes: 10 + timeout-minutes: 20 run: | source scripts/toolchain/fbtenv.sh - ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 - python3 scripts/testops.py -p=${{steps.device.outputs.flipper}} -t=180 await_flipper + python3 scripts/testops.py -t=180 await_flipper + ./fbt flash_usb_full FORCE=1 + - name: 'Validating updater' id: second_full_flash @@ -41,33 +33,7 @@ jobs: if: success() run: | source scripts/toolchain/fbtenv.sh - ./fbt flash_usb PORT=${{steps.device.outputs.flipper}} FORCE=1 - python3 scripts/testops.py -p=${{steps.device.outputs.flipper}} -t=180 await_flipper + python3 scripts/testops.py -t=180 await_flipper + ./fbt flash_usb FORCE=1 + python3 scripts/testops.py -t=180 await_flipper - - name: 'Get last release tag' - id: release_tag - if: failure() - run: | - echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT - - - name: 'Wipe workspace' - run: find ./ -mount -maxdepth 1 -exec rm -rf {} \; - - - name: 'Checkout latest release' - uses: actions/checkout@v4 - if: failure() - with: - fetch-depth: 1 - ref: ${{ steps.release_tag.outputs.tag }} - - - name: 'Flash last release' - if: failure() - run: | - ./fbt flash SWD_TRANSPORT_SERIAL=${{steps.device.outputs.stlink}} FORCE=1 - - - name: 'Wait for flipper and format ext' - if: failure() - run: | - source scripts/toolchain/fbtenv.sh - python3 scripts/testops.py -p=${{steps.device.outputs.flipper}} -t=180 await_flipper - python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext diff --git a/scripts/testops.py b/scripts/testops.py index 4ae10c7f4..3100a9b7f 100644 --- a/scripts/testops.py +++ b/scripts/testops.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 - import re -import sys import time +from datetime import datetime from typing import Optional from flipper.app import App @@ -11,7 +10,10 @@ from flipper.utils.cdc import resolve_port class Main(App): - # this is basic use without sub-commands, simply to reboot flipper / power it off, not meant as a full CLI wrapper + def __init__(self, no_exit=False): + super().__init__(no_exit) + self.test_results = None + def init(self): self.parser.add_argument("-p", "--port", help="CDC Port", default="auto") self.parser.add_argument( @@ -67,64 +69,108 @@ class Main(App): self.logger.info("Running unit tests") flipper.send("unit_tests" + "\r") self.logger.info("Waiting for unit tests to complete") - data = flipper.read.until(">: ") - self.logger.info("Parsing result") - - lines = data.decode().split("\r\n") - - tests_re = r"Failed tests: \d{0,}" - time_re = r"Consumed: \d{0,}" - leak_re = r"Leaked: \d{0,}" - status_re = r"Status: \w{3,}" - - tests_pattern = re.compile(tests_re) - time_pattern = re.compile(time_re) - leak_pattern = re.compile(leak_re) - status_pattern = re.compile(status_re) tests, elapsed_time, leak, status = None, None, None, None total = 0 + all_required_found = False - for line in lines: - self.logger.info(line) - if "()" in line: - total += 1 + full_output = [] - if not tests: - tests = re.match(tests_pattern, line) - if not elapsed_time: - elapsed_time = re.match(time_pattern, line) - if not leak: - leak = re.match(leak_pattern, line) - if not status: - status = re.match(status_pattern, line) + tests_pattern = re.compile(r"Failed tests: \d{0,}") + time_pattern = re.compile(r"Consumed: \d{0,}") + leak_pattern = re.compile(r"Leaked: \d{0,}") + status_pattern = re.compile(r"Status: \w{3,}") - if None in (tests, elapsed_time, leak, status): - self.logger.error( - f"Failed to parse output: {tests} {elapsed_time} {leak} {status}" + try: + while not all_required_found: + try: + line = flipper.read.until("\r\n", cut_eol=True).decode() + self.logger.info(line) + if "command not found," in line: + self.logger.error(f"Command not found: {line}") + return 1 + + if "()" in line: + total += 1 + self.logger.debug(f"Test completed: {line}") + + if not tests: + tests = tests_pattern.match(line) + if not elapsed_time: + elapsed_time = time_pattern.match(line) + if not leak: + leak = leak_pattern.match(line) + if not status: + status = status_pattern.match(line) + + pattern = re.compile( + r"(\[-]|\[\\]|\[\|]|\[/-]|\[[^\]]*\]|\x1b\[\d+D)" + ) + line_to_append = pattern.sub("", line) + pattern = re.compile(r"\[3D[^\]]*") + line_to_append = pattern.sub("", line_to_append) + line_to_append = f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S,%f')} {line_to_append}" + + full_output.append(line_to_append) + + if tests and elapsed_time and leak and status: + all_required_found = True + try: + remaining = flipper.read.until(">: ", cut_eol=True).decode() + if remaining.strip(): + full_output.append(remaining) + except: + pass + break + + except Exception as e: + self.logger.error(f"Error reading output: {e}") + raise + + if None in (tests, elapsed_time, leak, status): + raise RuntimeError( + f"Failed to parse output: {tests} {elapsed_time} {leak} {status}" + ) + + leak = int(re.findall(r"[- ]\d+", leak.group(0))[0]) + status = re.findall(r"\w+", status.group(0))[1] + tests = int(re.findall(r"\d+", tests.group(0))[0]) + elapsed_time = int(re.findall(r"\d+", elapsed_time.group(0))[0]) + + test_results = { + "full_output": "\n".join(full_output), + "total_tests": total, + "failed_tests": tests, + "elapsed_time_ms": elapsed_time, + "memory_leak_bytes": leak, + "status": status, + } + + self.test_results = test_results + + output_file = "unit_tests_output.txt" + with open(output_file, "w") as f: + f.write(test_results["full_output"]) + + print( + f"::notice:: Total tests: {total} Failed tests: {tests} Status: {status} Elapsed time: {elapsed_time / 1000} s Memory leak: {leak} bytes" ) - sys.exit(1) - leak = int(re.findall(r"[- ]\d+", leak.group(0))[0]) - status = re.findall(r"\w+", status.group(0))[1] - tests = int(re.findall(r"\d+", tests.group(0))[0]) - elapsed_time = int(re.findall(r"\d+", elapsed_time.group(0))[0]) + if tests > 0 or status != "PASSED": + self.logger.error(f"Got {tests} failed tests.") + self.logger.error(f"Leaked (not failing on this stat): {leak}") + self.logger.error(f"Status: {status}") + self.logger.error(f"Time: {elapsed_time / 1000} seconds") + return 1 - if tests > 0 or status != "PASSED": - self.logger.error(f"Got {tests} failed tests.") - self.logger.error(f"Leaked (not failing on this stat): {leak}") - self.logger.error(f"Status: {status}") - self.logger.error(f"Time: {elapsed_time/1000} seconds") + self.logger.info(f"Leaked (not failing on this stat): {leak}") + self.logger.info( + f"Tests ran successfully! Time elapsed {elapsed_time / 1000} seconds. Passed {total} tests." + ) + return 0 + + finally: flipper.stop() - return 1 - - self.logger.info(f"Leaked (not failing on this stat): {leak}") - self.logger.info( - f"Tests ran successfully! Time elapsed {elapsed_time/1000} seconds. Passed {total} tests." - ) - - flipper.stop() - return 0 if __name__ == "__main__": From a02781b93687086f546df1a48f7f0ca3f7724c5a Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:18:14 +0900 Subject: [PATCH 18/27] [FL-3920] Fix lost BadBLE keystrokes (#3993) * WIP: fix lost BadBLE keystrokes * Switch to semaphores for synchronization * Move checking to the gap level * Remove leftovers from hid_service * Remove more leftovers from hid_service * De-allocate the semaphore after use * Change the timeout to account for unforeseen situation * Update F18 API * Fix naming and unbump api version * Move away from semaphores * Remove the left over include * Ble: cleanup error handling in ble_gatt_characteristic_update * Fix PVS warning Co-authored-by: Aleksandr Kutuzov --- lib/ble_profile/extra_services/hid_service.c | 14 ++++---- lib/nfc/protocols/mf_plus/mf_plus_poller.c | 4 +-- lib/subghz/protocols/bin_raw.c | 2 +- targets/f7/ble_glue/furi_ble/gatt.c | 34 +++++++++++++++++--- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/lib/ble_profile/extra_services/hid_service.c b/lib/ble_profile/extra_services/hid_service.c index e46d2010c..9f9a0f752 100644 --- a/lib/ble_profile/extra_services/hid_service.c +++ b/lib/ble_profile/extra_services/hid_service.c @@ -10,13 +10,13 @@ #define TAG "BleHid" #define BLE_SVC_HID_REPORT_MAP_MAX_LEN (255) -#define BLE_SVC_HID_REPORT_MAX_LEN (255) -#define BLE_SVC_HID_REPORT_REF_LEN (2) -#define BLE_SVC_HID_INFO_LEN (4) -#define BLE_SVC_HID_CONTROL_POINT_LEN (1) +#define BLE_SVC_HID_REPORT_MAX_LEN (255) +#define BLE_SVC_HID_REPORT_REF_LEN (2) +#define BLE_SVC_HID_INFO_LEN (4) +#define BLE_SVC_HID_CONTROL_POINT_LEN (1) -#define BLE_SVC_HID_INPUT_REPORT_COUNT (3) -#define BLE_SVC_HID_OUTPUT_REPORT_COUNT (0) +#define BLE_SVC_HID_INPUT_REPORT_COUNT (3) +#define BLE_SVC_HID_OUTPUT_REPORT_COUNT (0) #define BLE_SVC_HID_FEATURE_REPORT_COUNT (0) #define BLE_SVC_HID_REPORT_COUNT \ (BLE_SVC_HID_INPUT_REPORT_COUNT + BLE_SVC_HID_OUTPUT_REPORT_COUNT + \ @@ -157,6 +157,7 @@ static BleEventAckStatus ble_svc_hid_event_handler(void* event, void* context) { hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); evt_blecore_aci* blecore_evt = (evt_blecore_aci*)event_pckt->data; // aci_gatt_attribute_modified_event_rp0* attribute_modified; + if(event_pckt->evt == HCI_VENDOR_SPECIFIC_DEBUG_EVT_CODE) { if(blecore_evt->ecode == ACI_GATT_ATTRIBUTE_MODIFIED_VSEVT_CODE) { // Process modification events @@ -274,6 +275,7 @@ bool ble_svc_hid_update_input_report( .data_ptr = data, .data_len = len, }; + return ble_gatt_characteristic_update( hid_svc->svc_handle, &hid_svc->input_report_chars[input_report_num], &report_data); } diff --git a/lib/nfc/protocols/mf_plus/mf_plus_poller.c b/lib/nfc/protocols/mf_plus/mf_plus_poller.c index 8d1cc1c99..57a26a9cf 100644 --- a/lib/nfc/protocols/mf_plus/mf_plus_poller.c +++ b/lib/nfc/protocols/mf_plus/mf_plus_poller.c @@ -146,7 +146,7 @@ static void mf_plus_poller_set_callback( static NfcCommand mf_plus_poller_run(NfcGenericEvent event, void* context) { furi_assert(context); - furi_assert(event.protocol = NfcProtocolIso14443_4a); + furi_assert(event.protocol == NfcProtocolIso14443_4a); furi_assert(event.event_data); MfPlusPoller* instance = context; @@ -178,7 +178,7 @@ void mf_plus_poller_free(MfPlusPoller* instance) { static bool mf_plus_poller_detect(NfcGenericEvent event, void* context) { furi_assert(context); - furi_assert(event.protocol = NfcProtocolIso14443_4a); + furi_assert(event.protocol == NfcProtocolIso14443_4a); furi_assert(event.event_data); MfPlusPoller* instance = context; diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 8298bce6b..c2aebb6ab 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -314,8 +314,8 @@ SubGhzProtocolStatus flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) { - break; res = SubGhzProtocolStatusErrorEncoderGetUpload; + break; } instance->encoder.is_running = true; diff --git a/targets/f7/ble_glue/furi_ble/gatt.c b/targets/f7/ble_glue/furi_ble/gatt.c index e40786583..b8ab094f9 100644 --- a/targets/f7/ble_glue/furi_ble/gatt.c +++ b/targets/f7/ble_glue/furi_ble/gatt.c @@ -7,6 +7,12 @@ #define GATT_MIN_READ_KEY_SIZE (10) +#ifdef BLE_GATT_STRICT +#define ble_gatt_strict_crash(message) furi_crash(message) +#else +#define ble_gatt_strict_crash(message) +#endif + void ble_gatt_characteristic_init( uint16_t svc_handle, const BleGattCharacteristicParams* char_descriptor, @@ -42,6 +48,7 @@ void ble_gatt_characteristic_init( &char_instance->handle); if(status) { FURI_LOG_E(TAG, "Failed to add %s char: %d", char_descriptor->name, status); + ble_gatt_strict_crash("Failed to add characteristic"); } char_instance->descriptor_handle = 0; @@ -68,6 +75,7 @@ void ble_gatt_characteristic_init( &char_instance->descriptor_handle); if(status) { FURI_LOG_E(TAG, "Failed to add %s char descriptor: %d", char_descriptor->name, status); + ble_gatt_strict_crash("Failed to add characteristic descriptor"); } if(release_data) { free((void*)char_data); @@ -82,6 +90,7 @@ void ble_gatt_characteristic_delete( if(status) { FURI_LOG_E( TAG, "Failed to delete %s char: %d", char_instance->characteristic->name, status); + ble_gatt_strict_crash("Failed to delete characteristic"); } free((void*)char_instance->characteristic); } @@ -111,14 +120,27 @@ bool ble_gatt_characteristic_update( release_data = char_descriptor->data.callback.fn(context, &char_data, &char_data_size); } - tBleStatus result = aci_gatt_update_char_value( - svc_handle, char_instance->handle, 0, char_data_size, char_data); - if(result) { - FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); - } + tBleStatus result; + size_t retries_left = 1000; + do { + retries_left--; + result = aci_gatt_update_char_value( + svc_handle, char_instance->handle, 0, char_data_size, char_data); + if(result == BLE_STATUS_INSUFFICIENT_RESOURCES) { + FURI_LOG_W(TAG, "Insufficient resources for %s characteristic", char_descriptor->name); + furi_delay_ms(1); + } + } while(result == BLE_STATUS_INSUFFICIENT_RESOURCES && retries_left); + if(release_data) { free((void*)char_data); } + + if(result != BLE_STATUS_SUCCESS) { + FURI_LOG_E(TAG, "Failed updating %s characteristic: %d", char_descriptor->name, result); + ble_gatt_strict_crash("Failed to update characteristic"); + } + return result != BLE_STATUS_SUCCESS; } @@ -132,6 +154,7 @@ bool ble_gatt_service_add( Service_UUID_Type, Service_UUID, Service_Type, Max_Attribute_Records, Service_Handle); if(result) { FURI_LOG_E(TAG, "Failed to add service: %x", result); + ble_gatt_strict_crash("Failed to add service"); } return result == BLE_STATUS_SUCCESS; @@ -141,6 +164,7 @@ bool ble_gatt_service_delete(uint16_t svc_handle) { tBleStatus result = aci_gatt_del_service(svc_handle); if(result) { FURI_LOG_E(TAG, "Failed to delete service: %x", result); + ble_gatt_strict_crash("Failed to delete service"); } return result == BLE_STATUS_SUCCESS; From 33f1a1609455ed78f19178367056fd63d77d1951 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 23 Dec 2024 01:52:37 +0100 Subject: [PATCH 19/27] FBT: Don't lint JS packages (#4030) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- firmware.scons | 2 ++ scripts/lint.py | 26 ++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/firmware.scons b/firmware.scons index 4c5e05873..e7378f957 100644 --- a/firmware.scons +++ b/firmware.scons @@ -29,6 +29,8 @@ env = ENV.Clone( TARGETS_ROOT=Dir("#/targets"), LINT_SOURCES=[ Dir("applications"), + # Not C code + Dir("!applications/system/js_app/packages"), ], LIBPATH=[ "${LIB_DIST_DIR}", diff --git a/scripts/lint.py b/scripts/lint.py index 8530209be..896cd3812 100755 --- a/scripts/lint.py +++ b/scripts/lint.py @@ -35,21 +35,25 @@ class Main(App): self.parser_format.set_defaults(func=self.format) @staticmethod - def _filter_lint_directories(dirnames: list[str]): + def _filter_lint_directories( + dirpath: str, dirnames: list[str], excludes: tuple[str] + ): # Skipping 3rd-party code - usually resides in subfolder "lib" if "lib" in dirnames: dirnames.remove("lib") - # Skipping hidden folders + # Skipping hidden and excluded folders for dirname in dirnames.copy(): if dirname.startswith("."): dirnames.remove(dirname) + if os.path.join(dirpath, dirname).startswith(excludes): + dirnames.remove(dirname) - def _check_folders(self, folders: list): + def _check_folders(self, folders: list, excludes: tuple[str]): show_message = False pattern = re.compile(SOURCE_CODE_DIR_PATTERN) for folder in folders: for dirpath, dirnames, filenames in os.walk(folder): - self._filter_lint_directories(dirnames) + self._filter_lint_directories(dirpath, dirnames, excludes) for dirname in dirnames: if not pattern.match(dirname): @@ -61,11 +65,11 @@ class Main(App): "Folders are not renamed automatically, please fix it by yourself" ) - def _find_sources(self, folders: list): + def _find_sources(self, folders: list, excludes: tuple[str]): output = [] for folder in folders: for dirpath, dirnames, filenames in os.walk(folder): - self._filter_lint_directories(dirnames) + self._filter_lint_directories(dirpath, dirnames, excludes) for filename in filenames: ext = os.path.splitext(filename.lower())[1] @@ -168,14 +172,20 @@ class Main(App): def _perform(self, dry_run: bool): result = 0 - sources = self._find_sources(self.args.input) + excludes = [] + for folder in self.args.input.copy(): + if folder.startswith("!"): + excludes.append(folder.removeprefix("!")) + self.args.input.remove(folder) + excludes = tuple(excludes) + sources = self._find_sources(self.args.input, excludes) if not self._format_sources(sources, dry_run=dry_run): result |= 0b001 if not self._apply_file_naming_convention(sources, dry_run=dry_run): result |= 0b010 if not self._apply_file_permissions(sources, dry_run=dry_run): result |= 0b100 - self._check_folders(self.args.input) + self._check_folders(self.args.input, excludes) return result def check(self): From 6d20bc7e50df4384d8713dd3f9f17042a097f30f Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:09:40 +0900 Subject: [PATCH 20/27] [FL-3938] Add winter animations (#4032) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add winter animations * Format images Co-authored-by: あく --- .../L1_Happy_holidays_128x64/frame_0.png | Bin 0 -> 858 bytes .../L1_Happy_holidays_128x64/frame_1.png | Bin 0 -> 855 bytes .../L1_Happy_holidays_128x64/frame_10.png | Bin 0 -> 872 bytes .../L1_Happy_holidays_128x64/frame_11.png | Bin 0 -> 861 bytes .../L1_Happy_holidays_128x64/frame_12.png | Bin 0 -> 853 bytes .../L1_Happy_holidays_128x64/frame_2.png | Bin 0 -> 851 bytes .../L1_Happy_holidays_128x64/frame_3.png | Bin 0 -> 852 bytes .../L1_Happy_holidays_128x64/frame_4.png | Bin 0 -> 856 bytes .../L1_Happy_holidays_128x64/frame_5.png | Bin 0 -> 850 bytes .../L1_Happy_holidays_128x64/frame_6.png | Bin 0 -> 851 bytes .../L1_Happy_holidays_128x64/frame_7.png | Bin 0 -> 860 bytes .../L1_Happy_holidays_128x64/frame_8.png | Bin 0 -> 857 bytes .../L1_Happy_holidays_128x64/frame_9.png | Bin 0 -> 863 bytes .../L1_Happy_holidays_128x64/meta.txt | 23 ++++++++++++++++++ .../L1_Sleigh_ride_128x64/frame_0.png | Bin 0 -> 820 bytes .../L1_Sleigh_ride_128x64/frame_1.png | Bin 0 -> 881 bytes .../L1_Sleigh_ride_128x64/frame_10.png | Bin 0 -> 788 bytes .../L1_Sleigh_ride_128x64/frame_11.png | Bin 0 -> 816 bytes .../L1_Sleigh_ride_128x64/frame_12.png | Bin 0 -> 864 bytes .../L1_Sleigh_ride_128x64/frame_13.png | Bin 0 -> 798 bytes .../L1_Sleigh_ride_128x64/frame_14.png | Bin 0 -> 813 bytes .../L1_Sleigh_ride_128x64/frame_15.png | Bin 0 -> 879 bytes .../L1_Sleigh_ride_128x64/frame_16.png | Bin 0 -> 855 bytes .../L1_Sleigh_ride_128x64/frame_17.png | Bin 0 -> 772 bytes .../L1_Sleigh_ride_128x64/frame_18.png | Bin 0 -> 817 bytes .../L1_Sleigh_ride_128x64/frame_19.png | Bin 0 -> 867 bytes .../L1_Sleigh_ride_128x64/frame_2.png | Bin 0 -> 866 bytes .../L1_Sleigh_ride_128x64/frame_20.png | Bin 0 -> 809 bytes .../L1_Sleigh_ride_128x64/frame_21.png | Bin 0 -> 795 bytes .../L1_Sleigh_ride_128x64/frame_22.png | Bin 0 -> 870 bytes .../L1_Sleigh_ride_128x64/frame_23.png | Bin 0 -> 852 bytes .../L1_Sleigh_ride_128x64/frame_24.png | Bin 0 -> 805 bytes .../L1_Sleigh_ride_128x64/frame_25.png | Bin 0 -> 858 bytes .../L1_Sleigh_ride_128x64/frame_26.png | Bin 0 -> 830 bytes .../L1_Sleigh_ride_128x64/frame_27.png | Bin 0 -> 828 bytes .../L1_Sleigh_ride_128x64/frame_28.png | Bin 0 -> 585 bytes .../L1_Sleigh_ride_128x64/frame_29.png | Bin 0 -> 431 bytes .../L1_Sleigh_ride_128x64/frame_3.png | Bin 0 -> 812 bytes .../L1_Sleigh_ride_128x64/frame_30.png | Bin 0 -> 281 bytes .../L1_Sleigh_ride_128x64/frame_31.png | Bin 0 -> 270 bytes .../L1_Sleigh_ride_128x64/frame_32.png | Bin 0 -> 236 bytes .../L1_Sleigh_ride_128x64/frame_33.png | Bin 0 -> 485 bytes .../L1_Sleigh_ride_128x64/frame_34.png | Bin 0 -> 771 bytes .../L1_Sleigh_ride_128x64/frame_35.png | Bin 0 -> 887 bytes .../L1_Sleigh_ride_128x64/frame_36.png | Bin 0 -> 809 bytes .../L1_Sleigh_ride_128x64/frame_4.png | Bin 0 -> 890 bytes .../L1_Sleigh_ride_128x64/frame_5.png | Bin 0 -> 819 bytes .../L1_Sleigh_ride_128x64/frame_6.png | Bin 0 -> 799 bytes .../L1_Sleigh_ride_128x64/frame_7.png | Bin 0 -> 817 bytes .../L1_Sleigh_ride_128x64/frame_8.png | Bin 0 -> 875 bytes .../L1_Sleigh_ride_128x64/frame_9.png | Bin 0 -> 823 bytes .../external/L1_Sleigh_ride_128x64/meta.txt | 23 ++++++++++++++++++ assets/dolphin/external/manifest.txt | 20 ++++++++++++--- 53 files changed, 63 insertions(+), 3 deletions(-) create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png create mode 100755 assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png create mode 100755 assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png new file mode 100755 index 0000000000000000000000000000000000000000..f1207ed14b35cd01eafa00342f2801d74b46c622 GIT binary patch literal 858 zcmV-g1Eu_lP)b3G}_zPLJOisK}_wXw<>5vl)fnyEcmLJ)0YM`-qsd9LG<*g zg3uHHfWB#8LN=zM6$A}^iEZj_uEAVvNj6Dq_mbTmAFd!cFTa@&W`4{t&64fs z1O6+|kM;zum_y0;LR;%E{4|RT5jh|65*n~`w!6HK#2L%5CukdP2q1}?sRvZR12EsS zP6BKKYE)f>e;Pi(R|nZq8>DGO9+1VXzqs;Y z>zzX{C5FwW0Jpr#S{j^JpU8;?=-uOueU6(!BS9RndoqvP1aOo&p}tH{j*$Y>F2CbY zJTRxpb}`rb=#NmV8-pBq^GnU*vUM}Lrs&3DVX2NWQRPbV9Eu_gHSp)RdG_xa2L$n& zAzo{&Ly7kt@}aaYyloeLFiehuLtK&CfvZv9i~0aRdxU@_`$?=4uDcSHyHB@B=3sjv z1Yp-C*-y?RIqd_Gb4_y*`NdA5GkU`(19o4%V_h1`c<^#Zb!*T(J_a>H!@b?Pw{we% z1dHI&$2X)22_qD0fIX;@)4QwO+?wU}hx4OTV?~Yn&m|Y9uV0s?!lVme6X@3g$i9tJ zAw<+xNi6@`UU{o9>1u#x3Q(N{@}pCiFtmK`&hbpo$P?pJF1bH9`LkVT#6U2cFbE**&wk(4~aXcues_UO>rxTx;jl=<04a40ce_veR#+-lBGlmLLRKtj8 k04biGE0u;J2K9h{0X->d_eOezlK=n!07*qoM6N<$g2W|{hX4Qo literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png new file mode 100755 index 0000000000000000000000000000000000000000..9d9012281f0193620bc458455f6a93538fad2e96 GIT binary patch literal 855 zcmV-d1E~CoP);NOWB)|bSkT1aRXRBA7yY|TxF{FSfXu8*lUk&&qCXhO8F)FkoA>H2*0;2g2fv& z(d#Xs;?yt-?E~*0`D{sLX^yAU0@$ekWb2zI3N8X{Fs83orr0sQ#X|guwcpNP1=Z{G zSdat3yA1rXr`2nki#Nt*x!NSyKj4u+UwHlRdp$A`_S7#~2^ znalc2P!kjpqG|x})i68iBGTxNe8ACYahNl>a$BAkJMa8EWISSW9x^|7!7He$DqzaK zHH~UJtLg*3q0pI4GT^O->aCwBq?&T_iw|3e_BWDe59<_EmFTb&R`!3M<{$oTix6M< z>)gGM21gU)@>GCLe`>uG{-^#CUE~ZdT5WwA&`g90koSOKH57+|(uvFv@ zu5jrw5_rGj>oQ@wX!V1Y;}RF$?qIcGbR!}y4R^HsbN*(0SXHAg%c)Z)Sl#Bty=5=L zsgMqRmyS4iNFy-1b&V$Edv0=qawEuV>%O>0>(QCF8Rm}903b;}$!G~1tcE58ti4NG z#Li1EDY$>yq#tK|B{A92+mN-R_2;-oJ?~InHFlt@zLj6-(yP6H#HwvaCccFQEMv5L zf=vPm?`HFc&NPYoHp(3VcND9qw+G8F!`Z&vP|e+|N$-{9)>3?5mJ2g}w){{ph6J4J z%MI1syar5eO=xJuFUo-&*Dz1D2A`uKvLp&GC&Xeh15D$Dcrr0Le)4jNZU3t?tW+aRjFfo7> hF5f7XLU{2m{sTJ-XIlu4y4L^z002ovPDHLkV1fh!ld%8* literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png new file mode 100755 index 0000000000000000000000000000000000000000..cb8f173b0bdd8ae661a0e3c000fa88fe256b3a30 GIT binary patch literal 872 zcmV-u1DE`XP)?*-2wV(L$SR8{9F5vK7gWF*K9z%sdYhFTTrr?}P9A_;@_Xs~B$& zdb=C`2i$J}PvkF<5Lip!C9yJmJk zyD)1^<~b8;r*9}UQQZePJA_5QLN`m13s@3OkFb+2eNEr3q|e{3Do1W(`LOHQ*{Ccc zB5$Q4jUTjXj@h}Yt9&X@PQ4@to~kt#+CNgxBxB&i*V>Ql>6XdOn;M1U`%k`JdYzb# zxvXjd@yA2gSI!^4_f;b4ZVha>V{2(}QKcwlYoN5(9sVe;;QMGaV0UFMDS=xLDQI_k zl`c30L1j1u&AC6(zViz%&P+gL(A6Pd>t%<3qVh2Bofy|h5cY3#mMO5li|Y^PWBb5vv#*6Jr_FPc<~!% zzI?osA0I4#%i;aLLUqKAW@~uXa`(Ts#~MgIadhi(T6M8oL-0000;RtW zYC;GgHWEVi?&N>ACW2t*m9`GWO9_C1mL9w88*tuE7R2n%(<4e*f~ghW9``l2w{(Idzmy zutB!vuh7(q6FumKEzuwW3j>H)yTC$Q)<$cl-5z|y-D(1|M{-+S4~zn(Om^kCIMPHS9NRn-wo zb-G{73WfIdhPR*xoUE3%!LeNDq5+&-b6)u9wbmmCRLA3PDHsgjUH*FU4P5pLwkgFV zmxg|AeEVE-xl4Bs3UJRKUC+WFO%Y=k1n4^CS3kxxCIG~RLNWKb&+NDt@uDzso1(=) ziO{b`1j1wkskuLG`gr?8T!vo%MDt|Wxs$H6RMRzvDm|*B)6Vsz{HJQVCiK<`7I%66 zpD+s`5q~AZZ&Y`vK%F#^#?`Zh(igxfh#;bQczHYO!l%F7Qcd2Iw2#!O6k`4kFx=;dmqLL2aI=r`kQ=1B2Is`ynS&V z<-m2T+}iw1!W-ao64<|YDyCoJj>kKX`oA%;cSfI$NThnHV{pp1@th#<3}a7jmBu`P zDqsa5kKKJg0*?!;XMmeM@@8qw(-6&dL7+AUbPP_-V{~{se>K-hd3t2Z!}?=`PrJ;# zQnh39NRQzpt(~lq3*y4nYpOBlET<#Dr92W2XR56aRW)_yVsXJ!W4uiuWfyz#sBd~p naeM(t04ZO&R;l#2#RBjz<}zf`VzpON00000NkvXXu0mjf*Yuj_ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png new file mode 100755 index 0000000000000000000000000000000000000000..5d4c7e7c55fb7012fa223c9084bfe754c5b50e4f GIT binary patch literal 853 zcmV-b1FHOqP)24#t z)$%{!Q45Ty-Up3=wft=c7FG4Oco@|*;BzJ3rR*z7I#_C0{0L^xB_+HdKGwmDt*hX6 zDH20Ibn!pi0Fyv8^LjZu2{ML(&ZIO+ZL(v#u~+Zi{QU%ZB`c)=A=;6(xnT!)w=IGt z=rYN(4d4fF8i~Na_14g#ID(kx5x^!L#~NS7XoQad>-6bk!-Etiv{*>*vHHuoDNw!k z6Bgu&=ryXNZat%G8DUk&U%K4Vh#e5;u5{tcO?gnuU;Vpo0uJJxMn=zn8dOzP6(9`7 zEDrjmYFK79AIVDKNJDk`d#Z&N9J_G3ad7`tJ->0As;ZJa<+8$t^j++69Ulc2wFZz}~l6 z2i~c<%n15&MiH}mc>V#t(I9v-Q%ybCJHJ&K+pAM;f5?b#dvfM$7{k&>F~d5MxP|iG zuzq5)*a~gZ@(lPVvHC{youmzh+FR<={yxpxF6ZCPB+GWTdbU|UpW2_RHsYTJ;tjyJJ$FNDVQx?~LS0I5z~ f@9uVx0t);KGZb(kKhPw)00000NkvXXu0mjfXi1u-?7W>nA=QF^N=Sn#Tt)k_1K?$nA#5M6H- z3SIFJs3+S?$SlbLxQwjlT}@4XMa_vL+g&bDiVrlO%h|I11tkAJR*h4?o`ocsAzjOk`v+05A z6S0AbLLD1$CbL%`nE^E%K>+(g<*^MDqF^JyR=fZHhu$#kT7e)w$r|myavG?9auyq; zKYZndksi}ZIGTPdw6*@?p1uO!w znz&u}0w4q@tEFA=Y*Y2x6`WjihCh3K<3L|C-CkX!q^iUR3udD4T<)wc*+BBe^$%O` z9DO;pT($(b6I9nTb45d9FA<=3UoiGLeh!Tk@xblL1OCRq3Fd|R3OxmeO3e8Dibr{1 zUeld&q4m*kk=8c%bL`D8HBUyJ+v!b3H;0AgI@Uyu>*@0-ig2hwxUj=>f6jU!NYIQ3 zT4Nn5ydO}Eq;>UexAYxw0t`u2rUq`rdtV~p$Dq)4{V=Ihabs3ZrTcVC0t^1*bx8)pZ36wySQ=&?|`G43`_qCmSb|uFJ9h)-AGPdfGG$gIuCG%s+c7RI1Xef zDMOT|0C&IEz1}^e&Fwi(K3p7`9xLnjeV5W}Gq-NZa%s{B7{ln-2ISGra}oGVt&uw6 z&4cn*Y0_7N<_b`o1d1cmKVj+UrGHN4dS0F!pY|#IzQs2U8)eLn$s>IRC)sFNgSj9c zT%A{qnQ&zVJj&x{G*jL9R6CvI^js_+_-a}HHpK^%`VRKOQ-O7;6vtCo$t)n{i}RJr dAS9px@GqP{Wg0BXhk5`2002ovPDHLkV1gwEk39eY literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png new file mode 100755 index 0000000000000000000000000000000000000000..3a47dfc170224127ad04528accd7b47104c33ae9 GIT binary patch literal 852 zcmV-a1FQUrP)`u%7fF~_5srm=#DzMRcpHmH0U6ukyH3_+-tnN~JbCOQv8kQi2h4Vg#S0unXINGxg zZkHmf(E0H4f3_jULp=X_eme(@K&?>6D75yyarmQomH8?DIxB$n{l}YMtfCMgz$WARRBeK`i7Xb9N38#N{wk>6 zn8t$ai(jKW?KTqz)31beoqg$Au^EG>5F@0_K|5OoAV!jm2e98^DtSPu+D-&p6T7j? z22=T#GHT)qf}e{4E{@RF0Fg$IkAQ;u*%3|?$ZdIC>Ad>eun9>hhRF2Pg|Muus(`6R z)->i?xoVVmxigvOz>5vlTi;O5G~@UuZ#56>ZKQucT&Aq5_(KI(+xu}=dbnaMkX%?k zbML*uvD5@?3a}MTY;@e$8so!6fS&!)_@_xFgSiv|g03v0gM*XQC7OLe7Yx9Bg*ybo z<=>Gw{DDXpaM`TYH+n_1%^eCrrGy!?Ak{p{GzI z8~!pIEf6A&*y#3kR-v@#Cda7$LP?wVC3xSi3@2 z!A?tGJB8q9N&6|Bbg2;*OYx3%VpovwG3?>ipJCRCq5JN3X{KARhKwUBo|D-PFjKokHKSty^2^5?rk6=KdDEDqO$ zlE(C=O?Adb3-7cQG}Eo^pfh<6C=Srv}f|5b-c_7Lq9ky$8NSLWtCuN(*43ja_;nGG^5I8Gz^?q6nP)ZGpk{ZHis=6xRwwiRTHA8TKI}=fhUKOwbyp*p) z2p|>-q51KR|71-Kg38;iIutJ<0D2>80Fl&>=|L8@mlf0m2&d@4?5vamXlM>`E$uM& zLKMOdA7KG!y!qBs72u{$B7pt6@44MDK-D!^V87mRI{WLlLppT;;t_9C|0|_TN*h>^ zj^N5re|xwI8NVphb@q+h3wV$~8SxX!(74qyUqIrNE$9sE#={~=qPFP+W1vSt9(&E1 z0@w#M;|jvVascq_~JyI!tV!l;9SsuG>egr&l+Zp2-t5D{M= zxWD<`Yin~kj-C+Uz#rRbhI1ica{}Z${oyYtVl@D9A=jSz95#dEluuTe-J(51Z@~=S z7ayPQ^NX4scV{*~`TbBe`9Qia-9u8(wg>LXe{9TRN|XGS=5{@Zg<_dt<(Sv&+FJ*o ziN0^Nat=6q6b)+J;3_!3{A4WGN_o||LH+#NqgtL0dO9am(d!IKB4BSt_PHey6Isf7y-< z!wfa!9l9>;)g)X8pEG;~IUur8_8G`M#z14}0c@3I_-9^=i5){Fe2nIfdcshi9*ImQ~PmS^Z0g~t#yK>Pt#X=O#0ChmR iZ!Pxq^*{`ifqwzxpI*j|*hYB(00007mXC8pbK{fp*z8BBm>suB`z94 zFfN2z#O6;ZBKV8g7q#_QCCN@qXeOGrX(7q8#^xpS-n%X)ZoHd&?}2j;hXd^?#CwPA zZOi|FM=da>x(?a|HqzI5V_8*ymmEel_W3|*?omo@k`A{uEM5S!qfr}P5)T`~@!}eU zdlYFxI&$ehTOSiaaP7tRABvyQ0Q#b~$wP^DsbasocXJFd1{r#UZe(Mj5kjgVg2g{! zqNg^2ZK_BB-fyps9Bu~itIHvPje7exFE?RqvK{*YgMaUzt9`2BE?9`~vG&X8RZ#K) z7UV#1mhwdS08C|FSktM~pXWCN$lx48!Zft9CIB81XRJb3h^a7vAc@-4!PamLsmo(G z7;ooH(D5G)LNpx!ygxvW^j~~s^Wd{{%hC2_%Bo6qC>J(LcU4;-n|et6 z;lSJ~e$e#y#ZU2?PPWj?9s!L5~f{i={^$pKjX?pAQF9f0f{TU#lQ!M$*KdX4t-O&vGdPrS(ARtsZ%Cc+2!QQ{=VDb z$ZDTt^&CF36BRUR(S*Xj8=PdQQ7miHV(rM{c2K0j(_N#gNlTJmk~39ouyWD33IS_J z7XP|CXfrx*)k97CSv|YB^_+)F=h8LRI45=q`3b{5Jz4>f1FM)q(809%5-`|T2Vru@ zGKlX3frLxo*sC6#jq*=|cLuA^-vNb@?tE>=D{0cXn5jm8+S&5?R=a$R3xT(~^R*eT zpaBK_K|^ER?D>|0W*YN^<82^co0-IDeXEol(ku3ud+BR(w;t!C()#IT&PhZT>YIlj zV>2mDWvKY@{dLud2K0bP1Opc!9uJ{(@LR+Or^Zxv0OniRg=d^G^)Q+QXo8flT(4F` chyfh@3(5jmrgn0s#I%4<)Bp&osQ-~KgQQ%<@;Hi6Q z(k904#d!5(@qoOhMQKuPI7tL$L1LF^*jJ?G0dL=USP~QG@}2o6Gv9n?23nDej}kdT z%m0YSEikG2H%!)m?dFG!uBqxWM%Pu7wAs|rc~xB&@LEke)|w&YfSVIhi(VG60lZkK zLkJ)i38DG%)&FEo4TAZ%TXiUYLICte)Bqx>AJBu`+PbTtCO|kw2WESz9KfOG5zo>N zW6wt+Z1WKo$c;DOdU_tXrjrO@zvz3m5eBGw1`F)eJ08pa^3{+|?SlBkTh#wbsglwL z7NjG1bEv;P+<=TL33aW!apM*~Bv3_MLKP0Lx6BujIAs$$!@BXY2$HC6I$#WRKPX@? z=B5C40L^%U@TeL9Tz-aQyl;>-6FDHG4=Ro}JPG;MZ=V12InxGY0+11nx>Z$m^oHv9 zJ!00#x3=d1ACr6Z*?8-)XLd*YpYD=NHRbi`cN$$Ut<8r~2SrsSI*|#>#fGlOU8Wcj zUm3W!@y)9%vw8NP5@6SjZ8yW&5U_ax3Y~8Fb4pkZKs?B|r;dlspfu&k3YAUTGxQeC z;5~6Tx=$}?a?qXG_~f^Ts>ucEK6eL6ecK**NB^;2!IUTYHO)7Ch=o#>VCjI@>e^ce z$3)+kI)4&4u^$a;e8_u{`}qgpV!5CiA5uTPvR5nM=;@qLjSnR`MUv~wN8Fz_&NpST ztE3)r-lspDs^Lj;ii`tWV>=jR&xu_{?u0VL{y86*m5xnJE=-M|y$MLj$k!`-mlsi< zTxzKPaa_VxaGVFo0};g;Ab(sd-V^((KVFjIE7|@zXYq53SpZotj{5+^U>rC(`QTCw zd}g-JFvE8P_T8lqRy2G?Q;j+67zeWbbBh=q+%0OVSLKD#IiJk$J6tQSJ7w5Lq&0n+ zT-->gq=I;GdqFj#3?=X+f`LyVo(`eVbsO=(S7ZEtfFwG=E}eCzRE(k-pbkj)^@YB^ d9*BV|@Gpq#TX#gkE++s0002ovPDHLkV1lDnjsE}u literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png new file mode 100755 index 0000000000000000000000000000000000000000..da3a78f4c7d2364f251efcf0d311cfeadf3693ab GIT binary patch literal 860 zcmV-i1Ec(jP)HGw^KWKzD1O2?a4gD9ViVO(ALi=Ao3~!3nM+a`evB?;V{tr$^<9Ht z@Ozx-`5N$}TShE+x8EB(Sg}yy2m+X>`*`iE3Tp8XV3Q$zVtka;2@D3}hg5HzodeaM zuV6r)vTsm07j7o>+()SD%qy4MY8GPL03l%(8fhil9EnrbU|)z+i~uB2(+ps1v~_mb zU^43xhMmAih-NLo^Z-r$6p@vVC;(iIW>)CMlbf<#Y`*@W-|d2p} z`R|OgGP|3p27rY^b0W!r7i;O6Uw)vFYRd8R@6`71T~K%Z4h2;uI^c%mdrNwLec366 z#9s{jc5kkyehI4U0&E4N8_n>0h2Q;HfR25^&}VTGcrHNzuRRTD68cmrOE9xWI~;@g zRa*h!!Xw1`zY<8^2~%tI?Zu-K7rhR99V&Q&Uq>%2}c?`l}k+I{g3Ek&naV}M(`v%`QS-6Vs37`vu}7BFU+ zw1_z;d5(bhha}y&C&I*$mHU1m6Kn? z6o#RHp1Ib6S_zl4d*i*e8be^Bwr9Z`#3bzima(q3{<4?X#IMh*M(>-2!l_2~{L;4& z!N*-~{betw7I$4WbWE^ts-d8%#zHc2259RqPvK~3t8?OkUX;Tl>$Z m(G*Yzq;TDI@s{A_#w=kO&avP_}B;zHLpOl zBas;Lp^N{;2ABlG>9-nlBtgb7(4I6xQk!hsPVA5OZ~bv>g;d?_qHs8 zCFn58i#6aoZwd*)zy03OfjESiml41wZAWWg#%PF-0BiK>lf#1)##t<+_gVSn>=jVG z{sR`|nea`@qtRMM#}dMdj=y@fsTM+t=MXX`pxCl2s58CN>+<(53EMTR1?@1s!UhNz zA0T1yGfqsHXe9d1ERt5*nnQEm^aDj_SFZivmu{xz3DwlI96M&h#chtg~lh@tevO^(pN5SMgwUHpS{$p^2~#jlQbK(aQno=$AI7%fJCweu7e?5MO* zfPHAQHoOzFnGy80j3Q>`;M^m8qe1X&rkZ-Ndv3Edwp*{X{2?Q{<;lBW!5EfaifPse zC7K5Zz}oT2V$<2A=>_n6u=;lW{iFqkTAQj<{vOR*uI87flO;P-K3Q)(pW^Z8`A<00 z+EktLOB!;xepw@9-OS0lf);uk82K1zs!okzwEJFuxzGuDX<*9X{k6sy6*H-vkc1-A z4-Y&=ZNpqEPzm7r4b_BUlYaz4c_8Y}RcoQDmkiA$5`a@kJquYuQ+46qht*5 j04Y!0=|GNY91Bxel{hfU)#i;bjeXFf0r)cg|m z+s-(^7BF=7szJD24*=ER!!^!mfoCz4qOTO&dXl>^D)ywH&zYgxW*M#RUNTZ zr~AaLQfyyu=r(kNlhx8TIFjpJ)Q^*E&T}8W*1G?Y>Udn1qQUU(#jh9Nz-7N^o35DT z!r-rsZ=OjmcIw`J0^IUPH?!~uQ^MFq0Xh%+!yn-p69D2uv6TD#!|b>h@q#e1M#-YT zi_jmA2!zQ7QgeUW_VEscxD3AjvF6E;b0b}CsiqqYR=ZV4r=9tv@`q}=A@tM<7Wa7e z?=TA>5q~wpZw&8Jg*s^>jmvKpyS^|s#~^};=EKW7Q72{kDlOIIJxO~>ohqL9R~WaS zZiU>X=tpQTfTX=xKIz$j{av76>=oq4FoWFzCqO2oyoPZ?PlCeq8MbyKj(@ehbAG|7 zcWB1v7=iJOBZEdWHjA-aY7b&xlmQc{Dndu)E{2@XnsnFVNDlS}PQBY`^8LX}% znuTQJqM~4B#zm-92_+Bf zi2p4g0OtWXwe8q|fXG%h&?xNYu0n&b4sL z^QO4X%7Pd6w}FAu%DJt*<9W?LZ}sJzDy)43;DZ5hUZWUqm44ih4)g{eVM}X9JCviI{E9d-bNa6iy*vm1^Sk%H{e(E@vBc# z-aP=|TlA@2q#79;JlIL1;H0w(ER>@j&yA3SNAp3&T#H2>$DO}UvOsY1@;7Fh)u(4; zt7iZxPyTM3-}yp`+0emQD`kSyQQMw0=@Wb0>0bbdoVK6z&aSpu)4j*~fWh82J(ahs zk^ZhEfYWDrc$Q;hgYH!iU><9-BSbx#yM_UN8nQ>e1vk4G9Zmr(R&7rJ9b9?yXnzv? z9;|Z+f{l5Tx$42x*?ZPK)0H*h3u1W~-EK=+bL4|;=wAanQcUf8|2Udo2oy+Cdv8L! zW>BpbXhV_;PA=Cmfr9i4`d*SM(!HKE(xp(jyCf-y=RThl2`wDk>F<@KOZp)req5=I zt7oqy&E?_}H9tt7zmbrnS|wGI<6b=68QGTva1ZnsG5TzOwD^4e+eWX)-a-qNT3C{T yjOM;xOQ2ei-mUtO+R?u!EKPKl0XTtM6#oJKCt7uy9%sA&0000O9aKtjFBFS<5rrbC%}m9M zUgRQo7XRX1b*D;^N?Ky=V?0!jd8EhM#x;n(iGKn8%i01^Q66Wj%mffr}U0-1Ob zAb^oTl>oMustNt26ldfm(WIn8cS^nw*cymV$9HzJdP{oZX z0DX}26@XXvB7mt|#QDeoiB>l%gnMp9<1RSsACR#L4*RrA4QTF}P@O3{P!=X&k|;Kw z{GG>YUjb)6wR^FoRzzG(K&R@4c1B+{Ck6u80E)uu#8e?{x_(D|nn3F|y%Owv`RPM< z0svYxdV1{gIr|r%6wR|rG z_Ipb9%K2T_WLJOdjU!Ia9LO0bFr9z*qjY7Kfbu;WBwM0Eytoa zv${bA+da&=CaBL3UV&D9ecr}20W+2Rq}Z_9H}LJJs7bb1)9v4MC!20Be7t`SouEjX zTsClwx2-l*f?XqA@%P8snc^`e*gjXTY;jv*Dp1Iz~P7fSP6>B%@=;M*NoN z&=$~kJR8Xvu~Q%XbJo)dAR^srD5aiZ-)UR74#8G8#=Rxu>vO8x(`%goMkdxKM;_5P zZ5{Tx07J%srCf@u9_7-oIH4>{e&LkJ13AL?UJoUdiY%}6iF|KsmFfb)$i%E)32)oT z?B9E2TqG~)Q3Yqo~o zxmYX{aZx*b>OB*Z?-*%R3J$iGAKipVIPm_Zv=l_ja&*?#pgOGkGbAD6?ryXZk;1r$ zgpSDY)KMFee7R0UWLTsi-{lZl4&zc)SuWa$RAgPDvy}TEA-!}aN+5?L00000NkvXX Hu0mjfdXb+( literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png new file mode 100755 index 0000000000000000000000000000000000000000..5459ae27c3e0b2d87cd3a2b68996ceda57cd6f41 GIT binary patch literal 788 zcmV+v1MB>WP)EtRqp8xfd%lOk=O*D>zE{8R{hs%EA5QbX z69Qmufyo`u1_Y$GvzL2?9p9?;NLR-=tK3H!AMk?h0wr)JIMuBH8gL~tfxn}}F~9Hl z<$3d+?Z7x3h(1Z$TV(8#I-a(CS;x3zBfP(Wy?+u2f9ZjcsCemK z<&Wal6rHP)KJnQ2Yvtay%j@gj8B=M*sn}#EX+NR&Um>=qX!_pf8Yg>hEzIEY$uM!~ zuUHdl*=yJLoQZl3Qs7L?KGb=y3kHxh*8cX{mbzm{kTmB!c5`-UTn&J< zi;4=Jp|JJtQ2@uGxF8J={k*IYuw2(H9Nf*1CmUz4QUwRhM%rSr=%v%@1-VHpG zX>Y!S*k6sK>@W1)wyDj22^axEz0^)k#2Eth(ON0dDZdreV{iwM$xVfJ3=R;nmUjlM zkIP-KkC1@=DueNqUDFnguRnB%j=2~?_}!KM4^rcK@M}knfZv+b-#PpJX`*(@2BJ)} zaNeHDRUsAFK-0he>Q9Cj2YSXfuX=A1S-C3gx@!a32OlmiuH_RBGzvD5ICz5x0uoat z{>dS!r6pY}w)Cs(4!d3ZqOY|2)f=COOUEq0l(VRO$UN>m{f%P*rjW;&`K2T7Tmw8Z6lW$uEFf_3*{&fP*BdvUl3oztSDYF#=e*{#<6Y z@2PT-f}$j~78AujRW#=~%x_4F-kx6gRJYs_*EOv~0=S@wNBws2I@rmXS`A)kr86X- z{0xjPjE+M4?q1+r4UAUOdqB_spqcWYb+E}M%Vo@AOo3b#kW`T*X<5;T>;4B1IZ1|x S#su9y zXzB&EP@D+}ih{(@X_M%bTA~QHMVd@Y+lgp0)1+x;&YU^F4|6efz3jcey;y7gKP>j} zzX$+u{sV*8eH#$awvz2^UTou3wn^%BjQ2M!)KLP8Whk`@B*5FwGi6BBK^~5Vrc%Bx z!!zfWgB5Ly#h+_Mtr*Q?Q|*O~S36pyZ&YD)F=)-@3=BUtO)xW*`MN8{(m6=&19!`NGz28WWYy2D z+1q#;8#7%4>R+{grs+~ag<|;qeU-2dT)UpNLrnkp%Qp?vkksdoM7O&2zCB2q^rAsE zNbCd5P9!bNM#nGd%QDb@1W7?6sy8)N0=&nOq>e}!Nh$?S`fDTL)RSjdZ#ZYc!?#qd zxhr<#Lh17CddFkQqNHjmoZb7Atjo}DRGw6jONIUf+tP50#f}GqIZXdUSuX>36-<}@nO@B3Kfof;!_8)rw+(TC;ykfz3 z+Zs!+ik#i9-MjZss&8!74os3!|1P@JqO^C)hRgXd3V3~VaW*aQ(dK( zBVB#P?3!PZr114b5K-1ir#FsS0MmVwb-Aa;(!pa$k_xATeam5Q`irBg?^UPoj(3>X zyvk%>=v;R5m{&()cOXqk3LlW9 u3nR;Mq(w=3Ly{^F&W|AlYDO5H8~Yc`rGxxvN7noR0000kltwRJ zHqlzsn8JD1t4U*p9c#2VF|pAm3NBb#Y=vFe5@y-ic^+mVzRUaPd*A#M}wUV0b+eehAL4h6(YK+)S~90XEeon$0@jRSee z1**;&2U2LCpW?xeSzJ_+`+MV|XB|L)n`aCBzmD9N9DuHZ(CO(LZ-gH-r2tr3l^L;t zh=jz-8J6!X#34GaY(O_2UWzjVn5E+Yni{+@Dhu{LyZNn9T?(s@Y97h_%fF3;*|l64 z4QBKGnSYn#VJt@ClE2XvRIamHipXqg_Ee4_ZP<)L#To26V6Da&ME`Geo*HOF!ECLr_vT<^KPyZ&y? zf)c1S`fIsY%e9pgb_i6X!JW^z8JU}?A3#?OD*E0FdvVCq0_AEJVIuEarTvP0NgR%&>G&UbFoz$}i=uVA#~b z@Jo4rqi^uVLmy*$I?%p8W;R6omrW~_o$vgsqp~|Tvx_aeVN(MJ(5tCT&tl7lb->bL z&s5C(Vk+OL&9>t;O9{dBF{%qaEO$IV zv>!x*Ga}WfB&%{VxnU`R>F;g-aq}-PM6T`+1B%6<^tj6iUP-m5bf9+=RR4pYg2rr{ zr37mC`h49p*P9mG&Y}c37`tV9rq~y6ndm7OrU1`WT}F2X?v_Q$YsH{bq_CcF994-# zsQJ&G+jo{N?>y+n71xansHHc-xMG2>;u-iTC;$NOo|M2iWDSBIL?2C`f~p6notk`0000R1tyh>M^I#_qv{QP$XrggLvOd7m!M?9MzFFYo`y^Z8emhj(nv z&MH@wAp>JwRUy-zK)tsfn@+-UZl|( zT7umoary&@;Oto&cW3Bxp*q?DA-WPrA>Hx(-0YX#T9+ErYr6N+8=_3c0}@aG`layq zN0$!)qX#{0U+lCF<*WJUhC+sxnbha4L=+=ZR}5>LR2JCsG3^VgcA{F1 zl`I{k9ksc1ZQRVK zs9{9@mEvq55&=BuE5Rz;SH`(93Ah+Q>i_(-uGBK1U6f$$-P@7 z(E61dDTey6mx6n*{JdiTU@i-Y$m03`yu#^%@1Mr^mB8YP>BfrpKk}dvmX*M-&%Chl z^X>1_z%FK#(CW1d&HkQIfJ|&a3Ab}S3uDRBV(L7#cer#4ps%S;ym8{*ajG1z0Yr|u z{Xpp)Y_$i+0q*3CF-`)`7eyD~@B#Cz2I%*C_Lm^JS}}PwK;(S(miZxq>)*I$GhQa# zoxo!sB6A1#DPd~L8DH6BTi&P74SqA!tz1T%l3RUw>0QI)JDF~1od#ln~EgM8``UT%NteSRUuc||C zBnU9%Ym+e8N}%_NW1xh^KT1|xb;RS-5DZ*8Rsxg*uV%r(BToac4{`w@QomVk$$b$K cTk!q=0mCXct2Z>fF#rGn07*qoM6N<$g2`}%OaK4? literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png new file mode 100755 index 0000000000000000000000000000000000000000..a5d6c1a7a59b8116e393bfade90b5891981070b4 GIT binary patch literal 813 zcmV+|1JeA7P)1v7b7Rs(o7gnOg*DS66-E$$HkCr=ieeTj1et54L@9!bl#=W!D4i)HB3-mD zBK|A}E3_q3jZ`XySZ7>F0;NuAp%t~swa`vAaemsAnaSKauZzi~iDz@Z?>R3o@Aq*S z=YJ;zz_|-X*F75$(7BfFY8CFvt!#@leT=`rD(Wo(#p+rE5BxZLniW6*8EdNR6~6g% zU6f*RCS2RQZ?Wy0b&8$SvTJiO;lI}MFaX>rG!-oiw{H~M8ml10#-{6slXoSKdF6}U zzj|z0Wy%){3Wx>Gtr(Y0qc}Bo9ARYw*+gAcqm|aUnEQ9r;m&Er6R9{RyRj9f&4}QK ztYe+X#{l%Xz{2$p7vpS50&I2w9r`r-`sFNud7MylDk&`m0A0t8yl*#Mc-|xO$9I6g zOe|Mc?Dup=A>d3-ncTx&#%>q^(%`dfZg;_ro*&JK{&~PT!5#NrtgZLNIfm#<)8&E* zFAn(aFOoeu0sTg(_Rd%F);6L>F!u6u#~{8~orUv8Fm>_F7J$%2U%G&0>g%ntfW-- zec$&Vk}J=g8cHOS-9Et0s%s*2!V{5TuLt1}uGy0fVQ`ky0sQvnlH2_0r6`XaLVs`e z9athzww-R@e{A1pB}yHO`%?`mq5hGW?z`%Al-tgfY^~QLeQIyIH=w4criPVln^##{ z8ajX-yn47^O?;G>jZPlTh*-XmYuriYYEnwSB*aYZ)|G?ji}N<2Ym-sQ+3|VzCzWvm z<1sM&XaM&8L{W{63FTzBrKg3Yq8j_8+tS3Y{qxj%ic;ePrNV@+ovg&j-+C{jM#a8u rQ>r~%IE&P{qDHY?HF?>=f6x90pDlLgl6o*(00000NkvXXu0mjf1J{uq literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png new file mode 100755 index 0000000000000000000000000000000000000000..57b2c9bbee26fbc31ea9e384d8ca8c44936db89f GIT binary patch literal 879 zcmV-#1CacQP)~9or#-tqXwE=trZzs6*sn#Qc^H<9#z~Z znvGTpHdzRj*EHGvpJk|zVmyz3{er~ zRJ4A-qSzE22f*LtK!6Znr32yxpa5V8kOUDc9n>+g7yva{Xara*@Sg)>UsD<0H9F4%#7+c6@3i;;3lqVlFP?gN7EtTWC;%2# z#Rt^ml^V7kc?m2Jsjg(W_|fU|hYloy3n=+GP3T~(?GFi-licd6WZ5MW!Uk-bp0CnH zdV;G!!aw|$6jWZssSq8i``+Ff`{ua=Pdyq(|Kd!Z+7C|A0as;{qC>(E#(S*jb* zz%}pic+asr#b5aDQ=TphvIIU13}^V}&P1 z7AI2-Uz|81a{PEhru0d^T$|K(b8`s#qVomo%#_-Z(oa63UFf(JZk5-&^^##mem^L( zQkLlI9`QY+yJ@_a<)q6fgQ!Q1hAV%kJr^Dbe-7`Zln}2uGrz_1J!{)7|137+9bI#E zCp@g!Y1DQ{b8Xl6lSN-SjJjF%k+ErRSGv>zbOO`Mn$Eh=J2wY_`qSCMt47NYttvkR zpdL=MC505m65=&TyGYK@|CmHX65<^qCsLH7w@vUlBc2OY&dz#+Nb$0G;`s@YvCL6} z$U?t(;>nmuPA()h6rTVf4oyT-h~#c1JSmHa_=&s8e*u(ie_+i17}Nj&002ovPDHLk FV1imcqT>Jn literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png new file mode 100755 index 0000000000000000000000000000000000000000..4be832c648910992ec977cd567913fc99db14545 GIT binary patch literal 855 zcmV-d1E~CoP)KUdworlw-BFPg1P$sm6{!i*7qQZo8dHS0D;13N z$%nr9vh_vEth9X*1EygLLIXh(icMPr*-6mG{t20q(4A~%=lHOj&8na9}5Wptd<+DbzR3cxt*Ewh4m34o@}VAfhVy@PsMiN2QcLsHlbZ=iN@H^J-wY%Wz~Wu%moQnV?E zl&<|PvNT)+NkmHdy&`3)&FwM(`N?Hs)&)@e2xVUUoXm*_(C8r9Wtr}g<)JKd+m=go)IkZXy)*G?=PZEl_9=ml8wVe`O|AGu6;sJtb0sn3 zXD+({W}vAs*>m!ZR}VcC)`OK3@o2q4QWn6DvXY99$wCKH9)OqDB;-05bL=?}a6g5J zoDs=M=s1q!WE>5ca3Jk{Zb5kKsFLcrV3e8m_9+kUWYCB;27VWN&La=c3~G!tUWc2d z$>vVxHyepoh6`1ZoFx0QlC(8y)c2!})K!XaUMePSME>aSm3*uuk44sOTM5x87pvc! zgYB|EAD9>x3mDb(;WpHey}MZCMl|x#w|}jaqMR%`U5;ZMlw(ppg{Lf1z59wq9nZh^ z*74}6(^f0-y`z8>UYaXZzYtj!xnSEivQ(%JRL@Bv%*mqTIGQ#KpD$%{AWf~ULk_;(y4AEptLo<`ScaMvi$)DcMJ@lu9EEl#Kq`+yXr&IxC;l5FY?FvWz zxh=}_@^8V`xBid=!`l>x#>H&S#Wm-2#X|w`_fknwvCws=AQEnX0FxYFA8RG8|$;q6XG%r=c zSh^nM%kI`Ly9DYGnU(x~U!>GGVZmPJIuh?c$pEM(ME;TFoG+3E*zGC7D6=b#Ju?b8 zXh3q$f8Li0HCPTxFy^m=?SKysXi&jayj!K^n|jMBZ*FwqCPN5yJ}7~{caIKLwUIgI zQ34}VV-1@-1Pmj;K>M@8!?NPh>g<~?5a=>^_UZoWMv@upqFVwhC+@vd3$PdiL}YE^ zUpH|p;d!_4JSDKUV=}+v^Q$i8GifF8;LwrA8^)Qxgn+#_u-)cCD&`4+d7Q(9e}YfUK>b?o8c{&#~h1?Em)xqyb(Ibn|#eaqF=P7ftn__;9!x;sMT`- z>J#Un?;|s3`X$^euZYZh3D48)nY7`I%0ax<_tDf3jzV1+K-KG)K?!TWri^9OC70_N z(D3|n3Q#tjjDd#YTLAPx+y_K*^ZlW$i-?$n=e+=t8zr75sa7BfZXZXg8NeK5LsQ!P!!h0x$q9-Z^pKELh6!9z9p95xv#B_CqZ&{nIMI zj`RjmE?4}-CoxyOig9>&u(XuqnK9NkPzYV6zGK^i3acPu zp+)?B{7}3KVixL3k%|63wxj`C9Wd`lu6u`n^8w6m0_ls7!(GxGK;yVk(!cV=KW`EL z{UYF2NKEN={nW0k0>JUUG*g{TH`^22feg4*%-u$(VX5J3)2mzU&ga~-O%!OJALksR zB}1bT_nL#zozVmM-5#_V5i}JeeH+*4vfx}Ys~4c}uhJ#BVnq0NOwYFgfkmh7$sv|w zMo-5RzB8OwXe;F?$X^`vU|S|ESSdY_{-CbSP5Oj~#=o*#(>)?2lF0zwum5UFqc0am zE*1{I8)KQU@{&=w^2@-i)7%3iKKfK=02nL}U7#U15`T3N!0EI4^QVs3 z?o+h505>pejJ&DsllyfL;PQ3WLZ!6wtkmUsp0~GX&6c?&W*AP7L9h>_%KXGT#YgfZ z$G(NDZ(B=r;KTHL)2s3N3|eY+EoK0iySabsIj-(J*^QK2NEQ7(Qd@6A5Bz~tUdR@e zmwd zO=w(I7>1v7@43m%Pdnq!XvEY+Ce^{zI;~I}qs$a4wzv{|W6%gOq!B?hJ4LH=t)SvU zLZL3S+5`%XorN2PqQ;q(;zAwh!XFwk{Yg8f+L?6H%)QCX{aoBh?YBAbocBDOZ}Bq8 z#@OLe2f(-ij8OoBCQu+Z?xJC081MjeQziKg=}rLj(3K|#+@>d50m@#anMnd^jENBd z(y$MhhFlN{U+_kl_~hA}yyT%={!zS}{;Vgsd*DO800ZZU^j28o*FPpUTV` z+p?e<=X;h5X}i=>ZT$O;3kFeeeQ)*omd<*AHk;Lo!Y##eY|F_`-hK|yYTr-A$u7M< zG{not0JXdnKiB0)%b{6Lonlo^R4Q*R z1cP((gY$&tXp{B`nXL=k>Xo7sykP5=sRw~M^PbJScD#@6X)dNf-Ct{cwPZVhLHkx0 z>PBi(>;CN5S&j8)6i`z|lJicnR|)yO^}4QmAMM%hPyT(U5(?J`>)Nx7cgywkJBJae zo=MfDCaKyNv8Nl35*mN3C+v&eg@oNdoB$DdXh%8dmg>43NL_a{FzqMDE~MuIwkN)E z5Rq!7NaiZ0@xpmBC(vNX1= zNV`=hEsPys#*@zakEsOspa6_lxRos51<*!}LiPTXXO&<0MwoGb=;KSfO_gLWeeVTt zZ%dGg#fl3vYfCN>05mLaPNlLRR8ewkSbL|{LL<))Zudw;3lINOLC4&7sJZ)zqe~f? zEhl0i%cJkFisbrBAckyC&t^r|r8GBbV63}G9Ij6}0O}b7BRetY@Ko|LK$VFEnU%X+ zlJDyVFioV8srzb6?uZWk z`sZl*J$|J+N`YWAqW3kbfs+m48*L*15l=S^zPt9QR!0s$URnwL*@=z^yCXLvq-p$%Z*QysNn4?nzjye9?zUuU)P znq>vWJ1R5g!t{5h4u1iv@)JL_JcQi;Fs)U>vO-&m)P%`&>zIk;z6m4{nUq*}r^HNT zVg)WSa zJB>!GF_F9>tW+9W?b{e9F%c4DB_Wh2tx{T$!VJ!x`sN)MGt#-6d(W41zVCitjvDWJ z)k(8?^m}&uI_OR0=*`vNH!B6r>Oa4_9U~M(@&K4N@UO^Bo33?@h}i$TlK_8=kl(1<4*5_WLJlW{%hR8cAHq-F0tCAlWY!ftXQZrk%tLa767O)Eux&PC|SJHNv)xg;&YW>F+0s!@}Zo>SK zUP@=D0k#&9oOP;=r+1&o0~8A6&Tptle|$wEK>NNa^*e7lcZP?a2Y9xOs%}%)dvD*j zFes~g^eHgioPS6iJo2rFu&WG8IQwXl>A?YlVZW7aKM+3i0u;)C64nwI4m-^=&)u+< z7I4$a&lwqk$h1aQB#iDJ=$;SsL=lwm^?`HaYRmfptnCIR%a{7njh^IeDj**9F>Ihr z%_}`P1`NNLS8`?QoxLAD9v=RIb>a=zu*3WCcu3%V0^nLD)}5)mnO@?92imD?ybAL$ zG7fC58xqAK)PVJ73qY7MDE7cgv*eV4>wO0D-@tlpf8rD{@tP89%bDoUL(9D$M5jlU z&|I26X{`Hm2p2Obfx+aAYu!6z$l7Pz?1Zr{Ivp=Nw(5;}?LqsCG zZ|XCVK>L2yH-Ph)`s-yl*yL`xtOWQ)XX`!4K+(F86On3l@ra0gENqMkozM9HtZM>Pv%4_IT4XK?fbq_sS;#70QGtql&oyq-dfnH;!*_+I}^ns nfJ)I<216I^jeL^CZ$d11@;kWVw|A_%ItK^W7EB z2jgA3l%Z1jQH?Ge_7uDKzug6#rBm-o<-5Lk-#Z@2?=%7|twr@NBbLC+Rx;hHQD*uD ze|wjxIr5P{yUD$wxhVH7j_J5H_964YtMPjLiaJwT z*7BuKEV*h6#E=>zi_P3y?IG+yQY_t^y_0KI>_uwCYQaJfxd3JyX~TrEbGqdNY&(PI zHcxB)u-+U4@Sd}*?3>sOyv>c01;^{LKq~gcuB(@i0nZmVjOOF_r#PQU=w`oDVBM1X z=@2{|9YO3UvdxHdhkkPtE88iLo`pUm_FWh|A75&BJ7mGF`jx(cLiX>*MYv?a)nvbI z9xW1D^s&+PT5o@l)EAku^Lkz*Mr${E_f)g95^L}I(p_Si#fUT zpLd~H1V&H%ZWJDP$8A2gP)boH+IZHsesm_51t4+468n4!v$y`G4FQ7@t6n*@&D}15 zQ?T&I7+}8k0sg{>@<7Y&!lSBjCxNLXhV6eXKv@8UepLL-izjJl_~_k)&GfiYsYKKjefYg<<;ilyvRvkXk?o z(^{5-Ti!w&T+AqtqDP9;YVGErOHUs2a_KuCX-<-)Dd}3NoLUANfIZOAa`EHXP`8`Pm&ZK4P(h<0c%ii$YU%_p4?zWA_o8@|Yp zf>ie)v-71k0U3(S(Mq!y6%{KunKE4OgKf~dCT+UqCb_xChopF3J~-cx!^h#{Bu$yI z=CtRUgALhN00=iY5D*0-`4;g!-~%`do859zHX z01xIuqcI@iHwpXUb5v9+jdv zfJ667{=}xp!B#6++WM!CtT!AOYZ(`}Zylub_fsBpfI)A1k#eo$-iGpQ0c#n)Cyd2L zW*Qyg)pki2z8TeR*CkvSkZL!irJK_#WwR*t ztyFJUs{V$nYMYDXXH+i+^NkaGHT!AS0^?9B^lsYx;B4 z%D?)F(^6{XS%nQ3DY)j-F3qn@N&Q(Q?K@PsE|n`I(pRnnOxC;qfmq~c!rLyA%4^~A zjFg&D_?v5iSkyd{fbqaH6Rrb9hT1;+19k&5%MQQ?wmS)37oOSqTGFP0`}-D;fq5WL zXO8d6QV=B0N-BhvAF@;fNka)qs_N}UmoSr(zE4R~nE8Ay>zL#?>`4QdG6Qa~idZU6uP07*qoM6N<$g5|EIEdT%j literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png new file mode 100755 index 0000000000000000000000000000000000000000..639834612c846903283f6ed6628f2c79c3fd6a1e GIT binary patch literal 852 zcmV-a1FQUrP)1v7=A^kbt&`HmrW%{6C=M}V3a-o|chR6ILKaRZph%VYN2(T;23%w=byKKd zK^HE>R7EJ0`cp*v12MOv(9&vybP;K2rms`?;8z^xJ(e?|IKT&-o4q z@B1c3g{)W9{bqnFuJ?MtHb&?TwN`-~Tpm1}h}In@`<0F(W5C#|5=)dO83VeSkwxA^ z0AiW}<|UY-O=<})WOKnap1A5&g#sq)@+CKCYL0#A??S}?XT0X{;fJXei5lZOM^By& z#Y^51^Z{EI@fD`7jJ$QLB!Sc}0{W@27@up!0|YR7%L7EYCF|A}SfnTUVqx=`zaA>b z{**6f)TH{{s<zCKn4VKM5y(OT z;I6!Z;83FYU-Ksy0iGE(QJ5U4l$I;y69#k!)pWjThm*S3onjO+pl$~Hv8-?{oD?9zJ?wvK{&E&bT;3D@~|Gk$L#P#-@&bh7VEWB6))9A3Gw6c&P(Z4Y0$ z6`co^HEq9~JYdTGU=3h2j(UB;%HAGd4||~EL*8^-_}q9Fm~R0U&!mEFpYCbz^nK#~ zJVqTw-97??gFx@|eM(L}Ev!8>eJLa)E<1)DAuyJ>asvPgl;H2N*4UK+fZ6Q^n$H2YDFZOPUkOWv{imJCxyJ>2qF)J> z$+fg|qwQh=CetW^F8y!lf!t+j>Fbe}3?gF7 zGV-~Iuk-niyb_?h;drT?;@vF2gc9Hn-LaZPI=#pN5s}5kYp#fl$ZwepA|lqde=s64 zlkdn^Gw8WqxYrh~e3 zL!wPgT(s%}oi=XVV72Mnn3h<@1W^)!4j@#mcBUP$@6pFQE?x^$@8yn@(@;@U~vu zl%z^!OIHK1$LMHl>~L-zE6H!jWk!7SMVMqa0=TghsMH|gY&$-vd+~{g3zMNL4XPR$ zSoLA&-D_G{sultYmE={7<$=NGPqy>)NrH7Now(ffW^24*jRLcmx6_x_2TGyUl|6K@ zVU$7(XD4yABcFh6&e%#VY>GyHaOs2EtM~lC&3^`MLYv#!;AHFb@B<;4nrMZ`~U>1I` ze7g(Z?fYeXoXDb&t^e@-l)kRb9W3}xv834O_1w$uY%V|o{a#5ksPu1 z$ovi$!1>%_=76cxnF_#ljF|2v$^`!&1vqoXLR+as6t#K)e*_k~vH+>qT!?xw;?A#+ zMK-eESKz{{)(9Ry7ug`Qauogfl4pU>+|w3XJud8iZy%ECXIE8~a{UEuxrC%>xf(wx z`A)gKa|%h?lRr_BmZh6LMI>p?H#I5lE`K^L3X8Ri+H#o%@!mnerWs~S<`tEcDn%Pjfu`Kw6H8mQar4y6RQOj#={92Rru`E jl;u7;lYj0beNp@eu20-ipVE6~Uf(?YKCPa%s!3ChnPn!ZgKpNQZ z0|#=Ra)E8K8KD^jI`URfrBJvs4Lm*JSHnEY!*2u6Hf%KZ%y)&qfdgY3&7ZMrPg=08 zq)-BIYqc=uK&SXgO*1yACa|ooO*Opaw^D?$U?xb$2V~AgHo^YTswC@5#0mRg;;bHZ z3FY5+0tsLJx+hr>(+Nid5K29C?!Iv<*S8_6RIaRC+Ou%tvagGQ2(9UseRnTU0}wY@ z^Lm|fW3k-{K-pNk#Co%5YzI)gjb_;+UGmy;WrZgFz1tz%8sSAt>xS+?ku1Rh1i{)6k?NHteb%0MqdDo_0wVf z`n=X!xvPt>{TZ&CXH!%5N*t6l|6YF}v7A`FQ*yi`8I+u8`f|42>8jkSS`_U=%5&Qz z-#lT@e7M&e!K;OpnCmyXRW)$)vvqfN(g(a0wM#DQUEQ6{A|g_~UTP)-KeX5?XDMO0d1lUP{Bp(GzWlHIs((L zt8l}X3p25r+G6!ud_LN&O{7kr+LAGlNV3Tny1V}DG(ZFjKtD=X*ZLx0bgQ}T;nYTl zh=lKFTD(n7E2W%sB4Fn_J13V)$I3Y6wX3v9H{k8w)@c=;W#}?(|1^S2w>W2rTw+{c-BToST=D=u-gmks;^vVo6_VwQX?N4{byAa!t7k%0PfPv$S+D+;X?zot z!aGEs^rz=s;O3gq-t%u>_zd;Hd!s-(Z&beX%uB7%zpV&d*@n!?xPBJKNk`G`n@B%QTE8I-EEN`PPPN8WjZck^Ke6Q^JaxEG%3 z_-PoOa^Y4F+Hu>8FIbC=iCjM>$+c)KUbNOKsb)<4#~ztYHMi!6KuJuByML8S`(`;F zfD&ASiw8?8(aep45-!qHow`RSn&qcK300GcYfU;b$R3aX0WNV?X=dOLZ2$lO07*qo IM6N<$f@c1SEdT%j literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png new file mode 100755 index 0000000000000000000000000000000000000000..962349bfdec5503429689825577af262921ac6c3 GIT binary patch literal 828 zcmV-C1H=4@P);MYf0)t1eLxntVNLyloX+DW?F4xn>Lx4kC|lVz2hP?>BPG^=lt&B&;Nhe z$Nydh0DT7xZunLpKw~}YuQjGxC8&`#9c5=&N6iIbu2e6O2D^vhwGJQ;rz2I`xM9`h z4OZsney!Bc=1tm9ZrFI?M~Uiv@ueNAX(#}uX4f`q5^7~o&di2&DB5-Eug~SqtX1DP z_x?f9>aIt8y%2v0r+v736~o;DEi^xOh-v}}MRKyT9$ND;fSLNc>P3gEkVkZT&0JYT zM56wYHIUNPsBVi}1ts_tc>iJmpgIjuT269gUOB)ft8$JgKlS47!h_?0g`9*kG0=KK zDuDJpm2)Cp87qz(e6XN$&Y7-CY44@1+-%XKU?6$C>!-d;eF*pXpmNUCzFW^uFOL#* zdw|Lnw*J5;@+}xLYJiw^qRKm&Htcw_)b33 zUNqEb3X6BW3)_NBrX2OtSTd4OJrK_;W?Lp)P+=G^$00KuI7fbI-c;0)xY~v9)6S5* zXSZ8bRxvLAY@zUQF?w^c$I(=<;fF$we>%ki@tEa4`EDLf|WcomHtkl$Pt=2S9cG-e) z5_8wBH44S%G)78~Q10~6%8j{+4DxU($qj3*IpoZ5j|~@^Ui)`TxV0C|wSjr>!qu-y zZkvx?IS1WaHM2&Rw-*#8yCq(l6|wakAriAN@4v~-jJ0000C6`P9 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png new file mode 100755 index 0000000000000000000000000000000000000000..2eb4456cc780a288fda6b71ff3ca99b931c51b2d GIT binary patch literal 585 zcmV-P0=E5$P)>Nh!OB=PyYTx622!q|kdqdsU?O6C zY`-77ka~b3JP3L3LgIs!0Zd;Hk+vjd7p0MO?%AFTaRBBC085+0@+}?!{nZ#{lBAI| zk}kVDr6OOCFON@Iau4&u`Bx3c7{kY_)lM4#Ej_Q+6-Yh8Z=k>B*GUKAX*Ese$ICP0 z8L+!^i5ZYC-uYR90EnFzb<-`4*OguP)Ht&3V|Fb%C>5PO>Cx+2p|}NS-`J?gV=)ir zz70x62H<0TQ-CQh41ETce^co0FIr!Wq z)xD}&RR9ok+5ix`BC#9JkkfQycE+FiSLaaE>t?%f+t<`R>!|U-2%8EfD`o&RA>zGy z-TmLTSAiX{>=J&-=7C$F==OWd64&-!+h`ToJoNh)^6=0kLjn7IB#T*5XBMEG*2HBJ~;}B+3L_Rues8tiw8w|~g@?NH`D5Z=;plA# Z{Rey_OQicOEv5hf002ovPDHLkV1i=g!{7h_ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png new file mode 100755 index 0000000000000000000000000000000000000000..e5772a672f9a63d511a03cd14cba48df73df3662 GIT binary patch literal 812 zcmV+{1JnG8P)Nkl)On1-IBH1q<;PWKD5!)^K#Gm-NXO<@41Ze zzwiNY{sXUWyEY)8t(lEB61#OO-ymB$!zBLg?gY45Y!*ns=rxXRW&kDlp=4^?JLamh z+pLsx{?psQ;nd2H&9$@0>|@QooFtWY03Qs1^AczMhH1c3pIA$Wnni;CTdm=A6HvBD zGSA)yb9kU=Vo_p8H@9Leot2K0zwqQFTQ5l6j6QXzX0%lrC%R+u;nrjJ>a~VMdA&GS z4(?H~2|`92GnJ8N11I`10J~7|-FPYNg|#{NK?{Ww{z7T#*F1oE#NjSICp*rq_k97d za|n?Nb}N4A-5~(?5K6>r4=ISt2ORHP%UI$1<@7syMgR_H)=m8Lea!rPr*}v=q>$rkwl*0<*&ZC zU?yImHA4m%OwQOyZvL-(F;7H0sSqT)ZQ<>F^-XwX41l(z{m9Jo9PDVVMF`%KQW{s)bzRqu$o?Gz2qm&E!1bC94yDt;WaxMT`~j@f z9R;OVIa7k-8>=Q5U;fO!(|0web&Z8)^pVL4F?q0000P43(XaExcZC)XO5g_}>Ap4BQE&%nc>iptZDLR|u?6uJVm%UC@+kLF@2I$vP@~#&R zxZV{tB$?U`%t5S1;qJ5m9kDuJM}JpjA48EIcNKk+9(EOFv?MK_o~iwzsEP7#?DW_H f$=?+J;Lm^?$5Vh#T320I00000NkvXXu0mjfxI%Y8 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png new file mode 100755 index 0000000000000000000000000000000000000000..ec47b899c994843115098e2b481d4be9e3545bd9 GIT binary patch literal 270 zcmV+p0rCEcP)g#Y&IE8RMH&I=ZT-N9#$G7Z;2?CV z!MD6x1bD!iYcNdCSF^9tY4hauHh`CC?40-Z-=cvn*3{YeK2>8 ze+2U>lk@GCfAh=B#t>UH24K-BvC9r%eZAQz=j37B8kTNI77Uy6BwY4=Pwc+n50F}o UZhAxmRsaA107*qoM6N<$g4SGi=l}o! literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png new file mode 100755 index 0000000000000000000000000000000000000000..3f345b563e9eeed887691ea64fd476c58e9eade1 GIT binary patch literal 236 zcmVukXtLRdY<7&Yil?6l5smS!)4=ixyV{H|i zEVZ~qQTSxJvPvwAS+^ZYlHpRc4b{SFksE~%lY85BfJbk@bKKYrf4%(Tbt~+$){Jx@>H0 z9v!U*E>kPOV&QLk{US7P`x)X2@FjRl)L3A-;}s^}z6-}5I>m6R*COBYii7&ERd`uB zg+3&W`T8Obyggs*z8c*$1-S7%TAE9JE`yu<`gW@2bNRjVnA)?Z&*lEIf=!Xm246;w!wPWp`HmW)LcC6j8v((m0$X#-)(gTGH^FsQna8H2~_{aHwIgSCq bjE?>S{e=)!nb_>V00000NkvXXu0mjfi(20( literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png new file mode 100755 index 0000000000000000000000000000000000000000..44c4d0d498b2ebfd6caf104f011430dfe5b1d362 GIT binary patch literal 771 zcmV+e1N{7nP)-)V`@Jcg?b! z8h15C(1l>d;+xnmB8bn0s0B?%Nll~j^76-I($3t^#mqxoUG9(XoO|xacaS8dU}7Ul zvH+yz0pJ4=0SEvKI~0J8h$~QMwbUk6iDv-FZCWX+W})l<4*=N!%mPs`Nk=`_H&U(RQN03dn~zOR4?0F^bcZ+3Jd|f@+2ZE!M+C?0$2hh zw{b3pwgYGh$>~DExEw%JKEP9k2~3drd00M~y~*R%-#{v~l9@dz#;0AHy!)E}^O+Rx z94I<&WVukn2$BWI?n_UP21jE!C^3x zDZI1qZ-3U!AASDMd$7RXZN&e z0fPM>xds;872w$+fB2Cr%~H`{g2wRTyVJ=QSO^(AJ6nsq`o(wFD^F{B%*=)68BWF} zXvTF8XWqhPlMHg|MfEYgan$}7+&OQ0>r%*vAZ@7aw5@LMqSO@9nv+wm?Y$}K6O~oM zi|WC5xD2E}p3qgPtJartKM85w>$fDS`}vAa$%oo~qjz(2j6=ZJSTHBHSJMXE^+B4= zU?IC%uSc~Qb0kp_Bdvd-<4w&=Yi?5=q~4jgms(nr_H|mausIPt6w!cMd<>9Q)~-r& zZE1c*ve0jw1WX8%DOgyo1(dtlNXk_={mO$H@Gr*&ZJJBPG>ZTL002ovPDHLkV1id3 BYq9_U literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png new file mode 100755 index 0000000000000000000000000000000000000000..f70ecb3abe0a34f98debe1b004eb08b12c98673b GIT binary patch literal 887 zcmV--1Bm>IP)I|X#-uh;n6?-_SZ&e{l2{{$MlW29fAFB}G}ef6 z^We24CdMpP8)HLjLOaozka(ay2$(iyAm9RpvIS%ZcJ_NcEbH}`=Y5{{AKi!E42k#v zphU=jbn`Zm>HmS%9n|H~b!8xcSy~v6Rp-lqj970})rLVV1{%PFlE?{~Lcju=gp(qd z*%bni;C>+|5^&gTdAuyV+9n|%4%uZeCHnQFxLmLD%HZub4lT)5>VmBj+N=Ys2_K~^ zV1&lW)zNY+0M!g#3me@gLq(%!W}f@y-)j(p62OXcD-EhLnzo)IU-P&fxLXyWwT&xb z|HtPlrdaioIrn2=-l|Ag9$u{a^!EC)Ppq60V{voLo_a4%xm$}zC)9i+&-Yb4NaVNu zT}A86)|x5aNgZC!$fT(ViYpi2T@%Uml)-}(Hzy{iL<&;QO|N1JE$x+v{x@M(!=v#?_jqm~nttmr=t z7iljxDh0nY&TkKQ|yw=xuTui4JdN{KB-Euw&K_M zu{BbrM#JrEfIY|m{;WB-CK-FYS(nvM?GAtt1i*TU3t>YvU|1~Pc4N&XpYNDWq}(Rp z%1DKNpNATlm3g=#xh%PX6<18Wv~9zwZ|N?vGQrSH_Q|xNkpJuzI{_f%wM-vl7TrJv3XRa0I{>wWXqt=#1!scW6rfAj(5QHzka zQyVTW<<9`DcMJh@nWN{PdHpKD^JPY&Lac7bwMEwITfBfU#P$ zpzZ?at5EsKk!a|~d!n)&`)d%L`FKEdSYMII!X7zGr3=u>>pS#Q)ytacEojC;7Z+o- zf~u--d0yT_qhcE=ol>1wU2q&x&iL;z`(Ogy;s!}joq9G5+oR*Er}dKKpt^WLd)oE1 z>NPL#d7`Mhy!(hh-O5rh2nM8MqRZx9h;M!aL}7YtJLn0QcYQGMO@3zpl7-zF5M|-c ndusK)F7<&Z%d5Xbx48TZM|mrzzUzGF00000NkvXXu0mjf3B8d` literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png new file mode 100755 index 0000000000000000000000000000000000000000..7d970234f57cdecc7e45bf765ed313ad92c3161b GIT binary patch literal 890 zcmV-=1BLvFP)6U8pcMp@S4V4`@4ocyo`72k70Lo9~?a<@?V4 z7-HkNVF>`RHUVn@0J9D#kj-8!oah5e0M0sEsTMByP~%y4 zifHE_S9;&`9T#56Ak&f!I@>Q6lLt#!o~PvQmWV0$Q@q{!jiW({!Iqm>)!xxJ?AFuZ zU`dwo>wNprw)yQDKtzs+Bql2VEZ#_Xp67&V7#vKE*?+fZ!Vmso8&H_w08_NvAH?=jCdf)!o1&-%i|OKByIox zqfF}OBHD?>C(oWM%kkR4&uP=|I_j5b%BsXW8h&_k_ouieho*b(@fTn6Bc%mb16K9q z*3XMWKdF`T9i{?#J9BX{Js+F+dx0w|yy5+vx-q!{q<+4=$2V`|kdXo3F66A36uoXB} z6B%s-2h^jkvB2)S^h~NLg^1+yYf==Mu3qsHo)V~txG|rV+P@lK2N5wJ)=f>1h#6iy z<#|>LREs*&0;(mlE0f{!s>rQ;P#O`*uOvKAm2H~}_jk`hrE~}|BS|sCby*QHA9$YU zsQ3v~3!Q-4l>sFSHQSiqlS7WB^FM<_LnNJkFb*ot#_xYEA|jF(lkmKM0Xq7HHc@&K QWdHyG07*qoM6N<$g4?&KI{*Lx literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png new file mode 100755 index 0000000000000000000000000000000000000000..70eab1f3e8c945560bd8aac429f4ae7e15401456 GIT binary patch literal 819 zcmV-31I+x1P) zUuYav6vlt|=B93H!FAKbeW-EU1Z_*GEfkSfldZI*f?}ae8v4g9A}AVcAQbvgb_ad1 z_+L=a2Qj|sjFwP*$U~(g*ir=9^q~+?$RMFRB|@f9lG)Ad93N&k+xu|ge4Ou`?|%0j zHPQd_kqn)qmV5r}K8jA-fW8>+@Sz(>OpOMw#lnx|~>+7u6}GfM*!KBRqQW$9C?c4A!&7q&C) zZR#>l`I#;9n;`}zfIja1F(kDxeFzxcYip8?Y`a8Eaw+*|eJ)F*YtkW27#2cQ3fD9xxO7i8G3U60x|3hHSFj?51%w@g*Fe6W3Pen(-lWthqH zIKkiFo>@#YdUcaWUK;ygeb245ZyzF3y)D)2H^w|bSsEA$o9Abri#(vmLPTOE(QB~) zSe5~l5WL-02egF&)bGsRKK>VQ(gpckWJY9Pc_ctPj$;TQ18@D9pQ6_VgMkYJ99z_qrZHz^_5-O!*)p0$SA@8>``u9&5i?P zA|=hi@vU}}3!lBz`j-#=wU1y@SuW7CZiv^m0KM@Q0KcH*jeYOjEG9ac0J!&rVWd>M zTm-QI%2S3C^+SMqw+Qi_2_+D0d{@?S)rZC17$6rG63sIoBP%l{B_LDHuT0c^g~i>i z$@@l4X0l=b;Z?7{h=@pS`7uw#(~h&(K}4h;O*1O@MIKm|5nrOJfLwFl+f(jocm#${{bV`LSb#O+VcPa002ovPDHLkV1lqyd=>xz literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png new file mode 100755 index 0000000000000000000000000000000000000000..8169e0766fd617850ef249e6919e2c7715e8cd9e GIT binary patch literal 799 zcmV+)1K|9LP)@2%14c(S3tq0sqQ7jEH5Dt=*W+!+tAtp8Hr$`!T0+9nA>H+_Nu>*%{ z!r3O^VVig%GbQn&7sQSz>BdX-(8LRNqlAUFbXQ8s>@Yj8hn=OZ-{pJr``+()pXX(q z|D6y3V*`wCc~&4mdNX^kU6}DLUYoRZlwRfq%GQCJzgfTnJ;GS40;s@jr2P7p4z2EO z`BkgUH8%sL>5Hx;%?&bmOPV#=@MWC5ermDatP_F);K8ay`L*yx0CU$U5dPW*AyM(x z^U`0%jVUtakvX+@=#1S?TU}kPT~eh=ycLt~QG(ax)Axz(Ni^|%eTCC)w+3qW$n?oI2)|Li?GX$SGl8*dSc?ud<`f8{IV6KN8%j`Qq+U;K{fgcF08uYQ&u>V>*Dtc2!ZX}?$gX!6H# zq9Cn==(qf}v5Vg(raVadCdiQojiEb=w{x+vg#B^nIU=LSh3(Hwpb#Cg{#fZvXs8rS zpx-R8%_ni*;!%$Q97uyP7Vy2+OWFi7VFxK|`I`@}h6^VQKwSwCd!Kq$AG)axK=pN@ z)ZD^x=k{?|2~f>$uM}>t@@GFFBGOowS((iZ7bT}nfaD|=o_p+uo70GhNU3w8F8_)= z)mjO_!)u>sms1C9KaF7lba$l=XVnG0*L9g&6OrikiTN+(Z)d=9R3ni9re$PLJ@C(h znY`?I@Kz&}CAt4AP%?e+AOz1kfhiA^Jk0C_dGjZ^Z2waRlkTK#qmH2p^tdhSB@q#k dszd{h^B;3hM?PLq!U_NY002ovPDHLkV1lI=dW!%6 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png new file mode 100755 index 0000000000000000000000000000000000000000..adee1a63dac8b87d57862f233107c1f961f39cac GIT binary patch literal 817 zcmV-11J3-3P)RhGNuC&1^3lbT18NI;gl(OH8CIa|J4-6 z(9S=ILoqD~ih^##tZOy%4^>3nkhO7aYed^*?YbskzI=}tNn_`GbI$pl!}Gk)3x{3& zuL1y^|G>Z{*9HW%EN2fcZERyzc8Sz=jQclD6e|J6GL)JEDR8&*NEuRfkcY2BQz>8a z!ZoKBgJrhG;k3$Z5YIixT?A2j=N`iCBxBp4$A1Hidih4IN`Gk{$) z08G7~Xn=q)d8oQ~xAB^ZOz%*CwyJlUMANgg7yseme0?g$(l$VCe`M3^GyRY*IssWhRbTRzmwo2GAv1_?vkjcY8eA*Zql6w4+_~uC6_XZ@5yYZkJ zq@DvzFOud8@ndK8K^bWM5J^EQt`{{?0^IwMq*yeJrsaW?`NRnL@qv@8)}6B8;#(@# z+?ZG~S2|Z%>$ogflvd5d*|{gpnqgXv%Hs-hUg(dpH3L^!?6F`Vhv~a3>kdP=Q7sal z+%a0YJGrx*gI|q6zmGkh+uTcF(E%j+XHF=YZ_SK!XFi^>K()=g_RFq6cF{f@p10sT zky|(I&Ck1peII>hq4v@}eWf8H!?1Pm7bD8w_(u0hj%`@qWPBZb26TCpn%6&SC_-{V4gwZyxYn-U8Fd&v>xl0)?^# zuHM(>!OCF>?!W@(WZ7Za!0EEuiE^`mY8|g zU7rO>QutJQ;uW3FbW3%h_uie0s&d|)QeA^W<+}BGZFnta8^B+T0{cf3+mI$Cg?CEQ v>5;|Tk>(}o1xc!GnLUCOXsN>Z%vb*cYgu*d%gmGW00000NkvXXu0mjfmz0(k literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png new file mode 100755 index 0000000000000000000000000000000000000000..db6e667fb77d119f4150c6a1ec875be023846c19 GIT binary patch literal 875 zcmV-x1C;!UP)~KgG zR}iEKYkd&s5>XHzgbo{2);@?U76v}7jm|c0SDM(WxlQlAUmue0ynNw%zURyLoI^kL zGTAy>AJ&1*viblBH+3K&3PcJG#WBDGup1&ZI*~%d$8{V4nu?GFD0|!@zybGp`T!?U z0Vu+FXuJxL@-`K&x$fN}fO2^v@PK`OBbv*7;lLJ~Gm(Khvu{j6SulUGk@WbdCub`` z#Z=lc0L-r_Os)Cmk-CR%xAg-4&)Qm_@CkdyHb!T9$TTis^htr($(@hgP-8ijR#wVq z%mpcm4H(sWsqG$YTgG7ffWpkif6KfeWw8MZbNlgLKhph95n^DFO6A(*_O}BS*bbHr zEidX={A}`M!Uli0op_nh zy7S_t!gp1q>P8?#z*R_CPxQ`+cX9UxkHv&I6 zXB;Pcc;Wu^DaQzc_;96iITfyx{hJFr)~AcC`;s&|vYODkUY~ObGd+?@ML(r5^Yv=ZHt8XtwHhg6(hr?2qSD`z z!Y34FRMNLOi`H#fFb6PcT4~E8X;k$v3Nx#@{{d={ku%1$CRzXh002ovPDHLkV1k;- BrV{`F literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png new file mode 100755 index 0000000000000000000000000000000000000000..a6a37fe29c7f17cec2d4824961e1acfe37f0d353 GIT binary patch literal 823 zcmV-71IYY|P)de9(=SZW1EY1mdVOhDM_c6MG5vwyzZ`^%H}ee=A}$LQ95 zF=jyHg=34CF#l@Q#%R2kI}nFKWUgz1neW&VR+pM! zBwEhe<@Uy2y7&2nVtj@elmP1LJezOG3#aM=kNA}QW9}aFhB*PvWR_uB5A7lEkYhOWwzPxbk_-U#y zg(S~e8g@uwO?$n#9ByJQ zGvG#6dKXzeCxaQ8l3B+|Bi?fK&dC{e`mUQ!fzM=bPgsdwY<$iqS6e%2d0>LNqEgNQ z9~D6fzibBC`8{xD4m8{csK)>{_^E|vBliO?{{st?Q~gh3V Date: Mon, 23 Dec 2024 05:34:05 +0400 Subject: [PATCH 21/27] [FL-3940] Work around incorrect serial port handling by the OS (#4040) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: js sdk flipper detection * chore: bump ver Co-authored-by: あく --- .../system/js_app/packages/fz-sdk/package.json | 2 +- .../system/js_app/packages/fz-sdk/pnpm-lock.yaml | 13 ++++++------- .../system/js_app/packages/fz-sdk/sdk.js | 16 ++++++++++++++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/applications/system/js_app/packages/fz-sdk/package.json b/applications/system/js_app/packages/fz-sdk/package.json index 3c2954c9c..f500fae2b 100644 --- a/applications/system/js_app/packages/fz-sdk/package.json +++ b/applications/system/js_app/packages/fz-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@flipperdevices/fz-sdk", - "version": "0.1.2", + "version": "0.1.3", "description": "Type declarations and documentation for native JS modules available on Flipper Zero", "keywords": [ "flipper", diff --git a/applications/system/js_app/packages/fz-sdk/pnpm-lock.yaml b/applications/system/js_app/packages/fz-sdk/pnpm-lock.yaml index 45944a854..67d3bde82 100644 --- a/applications/system/js_app/packages/fz-sdk/pnpm-lock.yaml +++ b/applications/system/js_app/packages/fz-sdk/pnpm-lock.yaml @@ -8,13 +8,6 @@ importers: .: dependencies: - prompts: - specifier: ^2.4.2 - version: 2.4.2 - serialport: - specifier: ^12.0.0 - version: 12.0.0 - devDependencies: esbuild: specifier: ^0.24.0 version: 0.24.0 @@ -24,6 +17,12 @@ importers: json5: specifier: ^2.2.3 version: 2.2.3 + prompts: + specifier: ^2.4.2 + version: 2.4.2 + serialport: + specifier: ^12.0.0 + version: 12.0.0 typedoc: specifier: ^0.26.10 version: 0.26.10(typescript@5.6.3) diff --git a/applications/system/js_app/packages/fz-sdk/sdk.js b/applications/system/js_app/packages/fz-sdk/sdk.js index 2eecf032d..44663203d 100644 --- a/applications/system/js_app/packages/fz-sdk/sdk.js +++ b/applications/system/js_app/packages/fz-sdk/sdk.js @@ -85,9 +85,21 @@ async function build(config) { async function upload(config) { const appFile = fs.readFileSync(config.input, "utf8"); - const flippers = (await SerialPort.list()).filter(x => x.serialNumber?.startsWith("flip_")); + const serialPorts = await SerialPort.list(); - if (!flippers) { + let flippers = serialPorts + .filter(x => x.serialNumber?.startsWith("flip_")) + .map(x => ({ path: x.path, name: x.serialNumber.replace("flip_", "") })); + + if (!flippers.length) { + // some Windows installations don't report the serial number correctly; + // filter by STM VCP VID:PID instead + flippers = serialPorts + .filter(x => x?.vendorId === "0483" && x?.productId === "5740") + .map(x => ({ path: x.path, name: x.path })); + } + + if (!flippers.length) { console.error("No Flippers found"); process.exit(1); } From 626d7ef509af0902b795ef802cd4a31b68bfa1d1 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 22 Dec 2024 17:56:19 -0800 Subject: [PATCH 22/27] Fix skylander ID reading (#4038) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Read skylanders faster * Correct parsing ID from skylanders Co-authored-by: あく --- .../nfc/plugins/supported_cards/skylanders.c | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/applications/main/nfc/plugins/supported_cards/skylanders.c b/applications/main/nfc/plugins/supported_cards/skylanders.c index 6c199f114..b5dc0ab86 100644 --- a/applications/main/nfc/plugins/supported_cards/skylanders.c +++ b/applications/main/nfc/plugins/supported_cards/skylanders.c @@ -7,13 +7,36 @@ #include #include -#define TAG "Skylanders" +#define TAG "Skylanders" +#define POLY UINT64_C(0x42f0e1eba9ea3693) +#define TOP UINT64_C(0x800000000000) +#define UID_LEN 4 +#define KEY_MASK 0xFFFFFFFFFFFF static const uint64_t skylanders_key = 0x4b0b20107ccb; static const char* nfc_resources_header = "Flipper NFC resources"; static const uint32_t nfc_resources_file_version = 1; +uint64_t crc64_like(uint64_t result, uint8_t sector) { + result ^= (uint64_t)sector << 40; + for(int i = 0; i < 8; i++) { + result = (result & TOP) ? (result << 1) ^ POLY : result << 1; + } + return result; +} + +uint64_t taghash(uint32_t uid) { + uint64_t result = 0x9AE903260CC4; + uint8_t uidBytes[UID_LEN] = {0}; + memcpy(uidBytes, &uid, UID_LEN); + + for(int i = 0; i < UID_LEN; i++) { + result = crc64_like(result, uidBytes[i]); + } + return result; +} + static bool skylanders_search_data( Storage* storage, const char* file_name, @@ -88,6 +111,12 @@ static bool skylanders_read(Nfc* nfc, NfcDevice* device) { MfClassicData* data = mf_classic_alloc(); nfc_device_copy_data(device, NfcProtocolMfClassic, data); + size_t* uid_len = 0; + const uint8_t* uid_bytes = mf_classic_get_uid(data, uid_len); + uint32_t uid = 0; + memcpy(&uid, uid_bytes, sizeof(uid)); + uint64_t hash = taghash(uid); + do { MfClassicType type = MfClassicType1k; MfClassicError error = mf_classic_poller_sync_detect_type(nfc, &type); @@ -96,10 +125,18 @@ static bool skylanders_read(Nfc* nfc, NfcDevice* device) { data->type = type; MfClassicDeviceKeys keys = {}; for(size_t i = 0; i < mf_classic_get_total_sectors_num(data->type); i++) { - bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_a[i].data); - FURI_BIT_SET(keys.key_a_mask, i); - bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_b[i].data); - FURI_BIT_SET(keys.key_b_mask, i); + if(i == 0) { + bit_lib_num_to_bytes_be(skylanders_key, sizeof(MfClassicKey), keys.key_a[i].data); + FURI_BIT_SET(keys.key_a_mask, i); + } else { + uint64_t sectorhash = crc64_like(hash, i); + uint64_t key = sectorhash & KEY_MASK; + uint8_t* keyBytes = (uint8_t*)&key; + memcpy(keys.key_a[i].data, keyBytes, sizeof(MfClassicKey)); + FURI_BIT_SET(keys.key_a_mask, i); + memset(keys.key_b[i].data, 0, sizeof(MfClassicKey)); + FURI_BIT_SET(keys.key_b_mask, i); + } } error = mf_classic_poller_sync_read(nfc, &keys, data); @@ -134,7 +171,7 @@ static bool skylanders_parse(const NfcDevice* device, FuriString* parsed_data) { uint64_t key = bit_lib_bytes_to_num_be(sec_tr->key_a.data, 6); if(key != skylanders_key) break; - const uint16_t id = (uint16_t)*data->block[1].data; + const uint16_t id = data->block[1].data[1] << 8 | data->block[1].data[0]; if(id == 0) break; Storage* storage = furi_record_open(RECORD_STORAGE); From dc73096966be9d5b7085a99a1bb6bd0a278f985f Mon Sep 17 00:00:00 2001 From: ru-asdx <39434750+ru-asdx@users.noreply.github.com> Date: Mon, 23 Dec 2024 02:05:19 +0000 Subject: [PATCH 23/27] using GITHUB_TOKEN to make API requests in scripts/get_env.py (#4033) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .github/workflows/build.yml | 1 + .github/workflows/build_compact.yml | 1 + .github/workflows/docs.yml | 1 + .github/workflows/merge_report.yml | 1 + .github/workflows/pvs_studio.yml | 1 + scripts/get_env.py | 6 +++++- 6 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2bc2178ae..66a2bdf73 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,7 @@ env: DEFAULT_TARGET: f7 FBT_TOOLCHAIN_PATH: /runner/_work FBT_GIT_SUBMODULE_SHALLOW: 1 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: main: diff --git a/.github/workflows/build_compact.yml b/.github/workflows/build_compact.yml index f45275204..f98ab8b49 100644 --- a/.github/workflows/build_compact.yml +++ b/.github/workflows/build_compact.yml @@ -6,6 +6,7 @@ on: env: FBT_TOOLCHAIN_PATH: /runner/_work FBT_GIT_SUBMODULE_SHALLOW: 1 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: compact: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1fa025085..65af450cf 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -9,6 +9,7 @@ on: env: TARGETS: f7 DEFAULT_TARGET: f7 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: check-secret: diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml index 90302ce1a..9ee7884c8 100644 --- a/.github/workflows/merge_report.yml +++ b/.github/workflows/merge_report.yml @@ -7,6 +7,7 @@ on: env: FBT_TOOLCHAIN_PATH: /runner/_work + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: merge_report: diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index 8eb6fea48..3f1a164bc 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -11,6 +11,7 @@ env: DEFAULT_TARGET: f7 FBT_TOOLCHAIN_PATH: /runner/_work FBT_GIT_SUBMODULE_SHALLOW: 1 + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: analyse_c_cpp: diff --git a/scripts/get_env.py b/scripts/get_env.py index e8f6b3b77..6d6b3cce5 100755 --- a/scripts/get_env.py +++ b/scripts/get_env.py @@ -34,7 +34,11 @@ def get_commit_json(event): commit_url = event["pull_request"]["base"]["repo"]["commits_url"].replace( "{/sha}", f"/{event['pull_request']['head']['sha']}" ) - with urllib.request.urlopen(commit_url, context=context) as commit_file: + request = urllib.request.Request(commit_url) + if "GH_TOKEN" in os.environ: + request.add_header("Authorization", "Bearer %s" % (os.environ["GH_TOKEN"])) + + with urllib.request.urlopen(request, context=context) as commit_file: commit_json = json.loads(commit_file.read().decode("utf-8")) return commit_json From e11a62694ef9809ca845c4680d5582aa7c12446e Mon Sep 17 00:00:00 2001 From: Jan Wiesemann Date: Mon, 23 Dec 2024 03:32:53 +0100 Subject: [PATCH 24/27] Added flipper_format_write_empty_line(...) (#4029) * Added flipper_format_write_empty_line(...) * Format sources Co-authored-by: Aleksandr Kutuzov --- .../unit_tests/tests/flipper_format/flipper_format_test.c | 1 + lib/flipper_format/flipper_format.c | 5 +++++ lib/flipper_format/flipper_format.h | 8 ++++++++ targets/f18/api_symbols.csv | 3 ++- targets/f7/api_symbols.csv | 3 ++- 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c b/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c index 888a66444..934634c71 100644 --- a/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c +++ b/applications/debug/unit_tests/tests/flipper_format/flipper_format_test.c @@ -265,6 +265,7 @@ static bool test_write(const char* file_name) { if(!flipper_format_file_open_always(file, file_name)) break; if(!flipper_format_write_header_cstr(file, test_filetype, test_version)) break; if(!flipper_format_write_comment_cstr(file, "This is comment")) break; + if(!flipper_format_write_empty_line(file)) break; if(!flipper_format_write_string_cstr(file, test_string_key, test_string_data)) break; if(!flipper_format_write_int32(file, test_int_key, test_int_data, COUNT_OF(test_int_data))) break; diff --git a/lib/flipper_format/flipper_format.c b/lib/flipper_format/flipper_format.c index 8992247d1..d07022e12 100644 --- a/lib/flipper_format/flipper_format.c +++ b/lib/flipper_format/flipper_format.c @@ -403,6 +403,11 @@ bool flipper_format_write_comment_cstr(FlipperFormat* flipper_format, const char return flipper_format_stream_write_comment_cstr(flipper_format->stream, data); } +bool flipper_format_write_empty_line(FlipperFormat* flipper_format) { + furi_check(flipper_format); + return flipper_format_stream_write_eol(flipper_format->stream); +} + bool flipper_format_delete_key(FlipperFormat* flipper_format, const char* key) { furi_check(flipper_format); FlipperStreamWriteData write_data = { diff --git a/lib/flipper_format/flipper_format.h b/lib/flipper_format/flipper_format.h index 46f78e255..4a1bb767b 100644 --- a/lib/flipper_format/flipper_format.h +++ b/lib/flipper_format/flipper_format.h @@ -518,6 +518,14 @@ bool flipper_format_write_comment(FlipperFormat* flipper_format, FuriString* dat */ bool flipper_format_write_comment_cstr(FlipperFormat* flipper_format, const char* data); +/** Write empty line (Improves readability for human based parsing) + * + * @param flipper_format Pointer to a FlipperFormat instance + * + * @return True on success + */ +bool flipper_format_write_empty_line(FlipperFormat* flipper_format); + /** Removes the first matching key and its value. Sets the RW pointer to a * position of deleted data. * diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 23421712d..e18aee04d 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,79.0,, +Version,+,79.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, @@ -1054,6 +1054,7 @@ Function,+,flipper_format_update_uint32,_Bool,"FlipperFormat*, const char*, cons Function,+,flipper_format_write_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" Function,+,flipper_format_write_comment,_Bool,"FlipperFormat*, FuriString*" Function,+,flipper_format_write_comment_cstr,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_write_empty_line,_Bool,FlipperFormat* Function,+,flipper_format_write_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" Function,+,flipper_format_write_header,_Bool,"FlipperFormat*, FuriString*, const uint32_t" Function,+,flipper_format_write_header_cstr,_Bool,"FlipperFormat*, const char*, const uint32_t" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 6f9fc5466..51404c951 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,79.0,, +Version,+,79.1,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -1164,6 +1164,7 @@ Function,+,flipper_format_update_uint32,_Bool,"FlipperFormat*, const char*, cons Function,+,flipper_format_write_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" Function,+,flipper_format_write_comment,_Bool,"FlipperFormat*, FuriString*" Function,+,flipper_format_write_comment_cstr,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_write_empty_line,_Bool,FlipperFormat* Function,+,flipper_format_write_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" Function,+,flipper_format_write_header,_Bool,"FlipperFormat*, FuriString*, const uint32_t" Function,+,flipper_format_write_header_cstr,_Bool,"FlipperFormat*, const char*, const uint32_t" From 519b89665f6b444ccb3ac4178c89229fda0a42c2 Mon Sep 17 00:00:00 2001 From: Sanghee Park Date: Mon, 23 Dec 2024 11:55:58 +0900 Subject: [PATCH 25/27] Fix invalid path errors while deploying SDK by enforcing toolchain to use UTF-8 on initial SDK Extraction (#4036) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix invalid path errors for non-Latin characters by enforcing UTF-8 (#4024) Due to cryillic alphabet on `/openocd/scripts/target/1986ве1т.cfg`, If the system codepage is handling `WideChar` for cryillic properly, It would cause jumbled characters and fail to decompress via System.IO.Compression.ZipFile without Encoding enforcement. (See https://github.com/flipperdevices/flipperzero-firmware/issues/4024#issuecomment-2545385580) * Scripts: fix line endings Co-authored-by: あく --- scripts/toolchain/windows-toolchain-download.ps1 | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 index 025f8341f..85a5ab724 100644 --- a/scripts/toolchain/windows-toolchain-download.ps1 +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -16,15 +16,15 @@ $toolchain_dist_temp_path = "$download_dir\$toolchain_dist_folder" try { if (Test-Path -LiteralPath "$toolchain_target_path") { - Write-Host -NoNewline "Removing old Windows toolchain.." - Remove-Item -LiteralPath "$toolchain_target_path" -Force -Recurse - Write-Host "done!" + Write-Host -NoNewline "Removing old Windows toolchain.." + Remove-Item -LiteralPath "$toolchain_target_path" -Force -Recurse + Write-Host "done!" } if (Test-path -LiteralPath "$toolchain_target_path\..\current") { - Write-Host -NoNewline "Unlinking 'current'.." + Write-Host -NoNewline "Unlinking 'current'.." Remove-Item -LiteralPath "$toolchain_target_path\..\current" -Force - Write-Host "done!" + Write-Host "done!" } if (!(Test-Path -LiteralPath "$toolchain_zip_temp_path" -PathType Leaf)) { @@ -46,7 +46,8 @@ if (Test-Path -LiteralPath "$toolchain_dist_temp_path") { Write-Host -NoNewline "Extracting Windows toolchain.." # This is faster than Expand-Archive Add-Type -Assembly "System.IO.Compression.Filesystem" -[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip_temp_path", "$download_dir") +Add-Type -Assembly "System.Text.Encoding" +[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip_temp_path", "$download_dir", [System.Text.Encoding]::UTF8) # Expand-Archive -LiteralPath "$toolchain_zip_temp_path" -DestinationPath "$download_dir" Write-Host -NoNewline "moving.." From 5fd423951bb5fa98c0a4187b68bacb2dbff97f2e Mon Sep 17 00:00:00 2001 From: Anna Antonenko Date: Tue, 24 Dec 2024 05:58:27 +0400 Subject: [PATCH 26/27] [FL-3933] Pipe (#3996) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: FuriThread stdin * ci: fix f18 * feat: stdio callback context * feat: FuriPipe * POTENTIALLY EXPLOSIVE pipe welding * fix: non-explosive welding * Revert welding * docs: furi_pipe * feat: pipe event loop integration * update f18 sdk * f18 * docs: make doxygen happy * fix: event loop not triggering when pipe attached to stdio * fix: partial stdout in pipe * allow simultaneous in and out subscription in event loop * refactor: move pipe out of furi and decouple from event loop * chore: api versioning * Bump api versions * refactor: rename pipe_set_pipe_broken_callback * Toolbox: add missing pragma once Co-authored-by: あく --- applications/debug/unit_tests/application.fam | 8 + .../debug/unit_tests/tests/pipe/pipe_test.c | 153 +++++++++ furi/core/stream_buffer.c | 5 + furi/core/stream_buffer.h | 11 + lib/toolbox/SConscript | 1 + lib/toolbox/pipe.c | 241 ++++++++++++++ lib/toolbox/pipe.h | 295 ++++++++++++++++++ targets/f18/api_symbols.csv | 20 +- targets/f7/api_symbols.csv | 20 +- 9 files changed, 752 insertions(+), 2 deletions(-) create mode 100644 applications/debug/unit_tests/tests/pipe/pipe_test.c create mode 100644 lib/toolbox/pipe.c create mode 100644 lib/toolbox/pipe.h diff --git a/applications/debug/unit_tests/application.fam b/applications/debug/unit_tests/application.fam index dec3283e4..f92d7e66f 100644 --- a/applications/debug/unit_tests/application.fam +++ b/applications/debug/unit_tests/application.fam @@ -236,3 +236,11 @@ App( entry_point="get_api", requires=["unit_tests"], ) + +App( + appid="test_pipe", + sources=["tests/common/*.c", "tests/pipe/*.c"], + apptype=FlipperAppType.PLUGIN, + entry_point="get_api", + requires=["unit_tests"], +) diff --git a/applications/debug/unit_tests/tests/pipe/pipe_test.c b/applications/debug/unit_tests/tests/pipe/pipe_test.c new file mode 100644 index 000000000..d440a04ee --- /dev/null +++ b/applications/debug/unit_tests/tests/pipe/pipe_test.c @@ -0,0 +1,153 @@ +#include "../test.h" // IWYU pragma: keep + +#include +#include + +#define PIPE_SIZE 128U +#define PIPE_TRG_LEVEL 1U + +MU_TEST(pipe_test_trivial) { + PipeSideBundle bundle = pipe_alloc(PIPE_SIZE, PIPE_TRG_LEVEL); + PipeSide* alice = bundle.alices_side; + PipeSide* bob = bundle.bobs_side; + + mu_assert_int_eq(PipeRoleAlice, pipe_role(alice)); + mu_assert_int_eq(PipeRoleBob, pipe_role(bob)); + mu_assert_int_eq(PipeStateOpen, pipe_state(alice)); + mu_assert_int_eq(PipeStateOpen, pipe_state(bob)); + + mu_assert_int_eq(PIPE_SIZE, pipe_spaces_available(alice)); + mu_assert_int_eq(PIPE_SIZE, pipe_spaces_available(bob)); + mu_assert_int_eq(0, pipe_bytes_available(alice)); + mu_assert_int_eq(0, pipe_bytes_available(bob)); + + for(uint8_t i = 0;; ++i) { + mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(alice)); + mu_assert_int_eq(i, pipe_bytes_available(bob)); + + if(pipe_send(alice, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) { + break; + } + + mu_assert_int_eq(PIPE_SIZE - i, pipe_spaces_available(bob)); + mu_assert_int_eq(i, pipe_bytes_available(alice)); + + if(pipe_send(bob, &i, sizeof(uint8_t), 0) != sizeof(uint8_t)) { + break; + } + } + + pipe_free(alice); + mu_assert_int_eq(PipeStateBroken, pipe_state(bob)); + + for(uint8_t i = 0;; ++i) { + mu_assert_int_eq(PIPE_SIZE - i, pipe_bytes_available(bob)); + + uint8_t value; + if(pipe_receive(bob, &value, sizeof(uint8_t), 0) != sizeof(uint8_t)) { + break; + } + + mu_assert_int_eq(i, value); + } + + pipe_free(bob); +} + +typedef enum { + TestFlagDataArrived = 1 << 0, + TestFlagSpaceFreed = 1 << 1, + TestFlagBecameBroken = 1 << 2, +} TestFlag; + +typedef struct { + TestFlag flag; + FuriEventLoop* event_loop; +} AncillaryThreadContext; + +static void on_data_arrived(PipeSide* pipe, void* context) { + AncillaryThreadContext* ctx = context; + ctx->flag |= TestFlagDataArrived; + uint8_t buffer[PIPE_SIZE]; + size_t size = pipe_receive(pipe, buffer, sizeof(buffer), 0); + pipe_send(pipe, buffer, size, 0); +} + +static void on_space_freed(PipeSide* pipe, void* context) { + AncillaryThreadContext* ctx = context; + ctx->flag |= TestFlagSpaceFreed; + const char* message = "Hi!"; + pipe_send(pipe, message, strlen(message), 0); +} + +static void on_became_broken(PipeSide* pipe, void* context) { + UNUSED(pipe); + AncillaryThreadContext* ctx = context; + ctx->flag |= TestFlagBecameBroken; + furi_event_loop_stop(ctx->event_loop); +} + +static int32_t ancillary_thread(void* context) { + PipeSide* pipe = context; + AncillaryThreadContext thread_ctx = { + .flag = 0, + .event_loop = furi_event_loop_alloc(), + }; + + pipe_attach_to_event_loop(pipe, thread_ctx.event_loop); + pipe_set_callback_context(pipe, &thread_ctx); + pipe_set_data_arrived_callback(pipe, on_data_arrived, 0); + pipe_set_space_freed_callback(pipe, on_space_freed, FuriEventLoopEventFlagEdge); + pipe_set_broken_callback(pipe, on_became_broken, 0); + + furi_event_loop_run(thread_ctx.event_loop); + + pipe_detach_from_event_loop(pipe); + pipe_free(pipe); + furi_event_loop_free(thread_ctx.event_loop); + return thread_ctx.flag; +} + +MU_TEST(pipe_test_event_loop) { + PipeSideBundle bundle = pipe_alloc(PIPE_SIZE, PIPE_TRG_LEVEL); + PipeSide* alice = bundle.alices_side; + PipeSide* bob = bundle.bobs_side; + + FuriThread* thread = furi_thread_alloc_ex("PipeTestAnc", 2048, ancillary_thread, bob); + furi_thread_start(thread); + + const char* message = "Hello!"; + pipe_send(alice, message, strlen(message), FuriWaitForever); + + char buffer_1[16]; + size_t size = pipe_receive(alice, buffer_1, sizeof(buffer_1), FuriWaitForever); + buffer_1[size] = 0; + + char buffer_2[16]; + const char* expected_reply = "Hi!"; + size = pipe_receive(alice, buffer_2, sizeof(buffer_2), FuriWaitForever); + buffer_2[size] = 0; + + pipe_free(alice); + furi_thread_join(thread); + + mu_assert_string_eq(message, buffer_1); + mu_assert_string_eq(expected_reply, buffer_2); + mu_assert_int_eq( + TestFlagDataArrived | TestFlagSpaceFreed | TestFlagBecameBroken, + furi_thread_get_return_code(thread)); + + furi_thread_free(thread); +} + +MU_TEST_SUITE(test_pipe) { + MU_RUN_TEST(pipe_test_trivial); + MU_RUN_TEST(pipe_test_event_loop); +} + +int run_minunit_test_pipe(void) { + MU_RUN_SUITE(test_pipe); + return MU_EXIT_CODE; +} + +TEST_API_DEFINE(run_minunit_test_pipe) diff --git a/furi/core/stream_buffer.c b/furi/core/stream_buffer.c index 783b2d741..902ec931c 100644 --- a/furi/core/stream_buffer.c +++ b/furi/core/stream_buffer.c @@ -54,6 +54,11 @@ bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigg pdTRUE; } +size_t furi_stream_get_trigger_level(FuriStreamBuffer* stream_buffer) { + furi_check(stream_buffer); + return ((StaticStreamBuffer_t*)stream_buffer)->xTriggerLevelBytes; +} + size_t furi_stream_buffer_send( FuriStreamBuffer* stream_buffer, const void* data, diff --git a/furi/core/stream_buffer.h b/furi/core/stream_buffer.h index eef8ee510..deca813c7 100644 --- a/furi/core/stream_buffer.h +++ b/furi/core/stream_buffer.h @@ -54,6 +54,17 @@ void furi_stream_buffer_free(FuriStreamBuffer* stream_buffer); */ bool furi_stream_set_trigger_level(FuriStreamBuffer* stream_buffer, size_t trigger_level); +/** + * @brief Get trigger level for stream buffer. + * A stream buffer's trigger level is the number of bytes that must be in the + * stream buffer before a task that is blocked on the stream buffer to + * wait for data is moved out of the blocked state. + * + * @param stream_buffer The stream buffer instance + * @return The trigger level for the stream buffer + */ +size_t furi_stream_get_trigger_level(FuriStreamBuffer* stream_buffer); + /** * @brief Sends bytes to a stream buffer. The bytes are copied into the stream buffer. * Wakes up task waiting for data to become available if called from ISR. diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 03b8999c4..8a1c4a8c5 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -30,6 +30,7 @@ env.Append( File("stream/string_stream.h"), File("stream/buffered_file_stream.h"), File("strint.h"), + File("pipe.h"), File("protocols/protocol_dict.h"), File("pretty_format.h"), File("hex.h"), diff --git a/lib/toolbox/pipe.c b/lib/toolbox/pipe.c new file mode 100644 index 000000000..9dc1d368e --- /dev/null +++ b/lib/toolbox/pipe.c @@ -0,0 +1,241 @@ +#include "pipe.h" +#include + +/** + * Data shared between both sides. + */ +typedef struct { + FuriSemaphore* instance_count; // role; +} + +PipeState pipe_state(PipeSide* pipe) { + furi_check(pipe); + uint32_t count = furi_semaphore_get_count(pipe->shared->instance_count); + return (count == 1) ? PipeStateOpen : PipeStateBroken; +} + +void pipe_free(PipeSide* pipe) { + furi_check(pipe); + furi_check(!pipe->event_loop); + + furi_mutex_acquire(pipe->shared->state_transition, FuriWaitForever); + FuriStatus status = furi_semaphore_acquire(pipe->shared->instance_count, 0); + + if(status == FuriStatusOk) { + // the other side is still intact + furi_mutex_release(pipe->shared->state_transition); + free(pipe); + } else { + // the other side is gone too + furi_stream_buffer_free(pipe->sending); + furi_stream_buffer_free(pipe->receiving); + furi_semaphore_free(pipe->shared->instance_count); + furi_mutex_free(pipe->shared->state_transition); + free(pipe->shared); + free(pipe); + } +} + +static void _pipe_stdout_cb(const char* data, size_t size, void* context) { + furi_assert(context); + PipeSide* pipe = context; + while(size) { + size_t sent = pipe_send(pipe, data, size, FuriWaitForever); + data += sent; + size -= sent; + } +} + +static size_t _pipe_stdin_cb(char* data, size_t size, FuriWait timeout, void* context) { + furi_assert(context); + PipeSide* pipe = context; + return pipe_receive(pipe, data, size, timeout); +} + +void pipe_install_as_stdio(PipeSide* pipe) { + furi_check(pipe); + furi_thread_set_stdout_callback(_pipe_stdout_cb, pipe); + furi_thread_set_stdin_callback(_pipe_stdin_cb, pipe); +} + +size_t pipe_receive(PipeSide* pipe, void* data, size_t length, FuriWait timeout) { + furi_check(pipe); + return furi_stream_buffer_receive(pipe->receiving, data, length, timeout); +} + +size_t pipe_send(PipeSide* pipe, const void* data, size_t length, FuriWait timeout) { + furi_check(pipe); + return furi_stream_buffer_send(pipe->sending, data, length, timeout); +} + +size_t pipe_bytes_available(PipeSide* pipe) { + furi_check(pipe); + return furi_stream_buffer_bytes_available(pipe->receiving); +} + +size_t pipe_spaces_available(PipeSide* pipe) { + furi_check(pipe); + return furi_stream_buffer_spaces_available(pipe->sending); +} + +static void pipe_receiving_buffer_callback(FuriEventLoopObject* buffer, void* context) { + UNUSED(buffer); + PipeSide* pipe = context; + furi_assert(pipe); + if(pipe->on_space_freed) pipe->on_data_arrived(pipe, pipe->callback_context); +} + +static void pipe_sending_buffer_callback(FuriEventLoopObject* buffer, void* context) { + UNUSED(buffer); + PipeSide* pipe = context; + furi_assert(pipe); + if(pipe->on_data_arrived) pipe->on_space_freed(pipe, pipe->callback_context); +} + +static void pipe_semaphore_callback(FuriEventLoopObject* semaphore, void* context) { + UNUSED(semaphore); + PipeSide* pipe = context; + furi_assert(pipe); + if(pipe->on_pipe_broken) pipe->on_pipe_broken(pipe, pipe->callback_context); +} + +void pipe_attach_to_event_loop(PipeSide* pipe, FuriEventLoop* event_loop) { + furi_check(pipe); + furi_check(event_loop); + furi_check(!pipe->event_loop); + + pipe->event_loop = event_loop; +} + +void pipe_detach_from_event_loop(PipeSide* pipe) { + furi_check(pipe); + furi_check(pipe->event_loop); + + furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->receiving); + furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->sending); + furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->shared->instance_count); + + pipe->event_loop = NULL; +} + +void pipe_set_callback_context(PipeSide* pipe, void* context) { + furi_check(pipe); + pipe->callback_context = context; +} + +void pipe_set_data_arrived_callback( + PipeSide* pipe, + PipeSideDataArrivedCallback callback, + FuriEventLoopEvent event) { + furi_check(pipe); + furi_check(pipe->event_loop); + furi_check((event & FuriEventLoopEventMask) == 0); + + furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->receiving); + pipe->on_data_arrived = callback; + if(callback) + furi_event_loop_subscribe_stream_buffer( + pipe->event_loop, + pipe->receiving, + FuriEventLoopEventIn | event, + pipe_receiving_buffer_callback, + pipe); +} + +void pipe_set_space_freed_callback( + PipeSide* pipe, + PipeSideSpaceFreedCallback callback, + FuriEventLoopEvent event) { + furi_check(pipe); + furi_check(pipe->event_loop); + furi_check((event & FuriEventLoopEventMask) == 0); + + furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->sending); + pipe->on_space_freed = callback; + if(callback) + furi_event_loop_subscribe_stream_buffer( + pipe->event_loop, + pipe->sending, + FuriEventLoopEventOut | event, + pipe_sending_buffer_callback, + pipe); +} + +void pipe_set_broken_callback( + PipeSide* pipe, + PipeSideBrokenCallback callback, + FuriEventLoopEvent event) { + furi_check(pipe); + furi_check(pipe->event_loop); + furi_check((event & FuriEventLoopEventMask) == 0); + + furi_event_loop_maybe_unsubscribe(pipe->event_loop, pipe->shared->instance_count); + pipe->on_pipe_broken = callback; + if(callback) + furi_event_loop_subscribe_semaphore( + pipe->event_loop, + pipe->shared->instance_count, + FuriEventLoopEventOut | event, + pipe_semaphore_callback, + pipe); +} diff --git a/lib/toolbox/pipe.h b/lib/toolbox/pipe.h new file mode 100644 index 000000000..df75f4c48 --- /dev/null +++ b/lib/toolbox/pipe.h @@ -0,0 +1,295 @@ +/** + * @file pipe.h + * Pipe convenience module + * + * Pipes are used to send bytes between two threads in both directions. The two + * threads are referred to as Alice and Bob and their abilities regarding what + * they can do with the pipe are equal. + * + * It is also possible to use both sides of the pipe within one thread. + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @brief The role of a pipe side + * + * Both roles are equal, as they can both read and write the data. This status + * might be helpful in determining the role of a thread w.r.t. another thread in + * an application that builds on the pipe. + */ +typedef enum { + PipeRoleAlice, + PipeRoleBob, +} PipeRole; + +/** + * @brief The state of a pipe + * + * - `PipeStateOpen`: Both pipe sides are in place, meaning data that is sent + * down the pipe _might_ be read by the peer, and new data sent by the peer + * _might_ arrive. + * - `PipeStateBroken`: The other side of the pipe has been freed, meaning + * data that is written will never reach its destination, and no new data + * will appear in the buffer. + * + * A broken pipe can never become open again, because there's no way to connect + * a side of a pipe to another side of a pipe. + */ +typedef enum { + PipeStateOpen, + PipeStateBroken, +} PipeState; + +typedef struct PipeSide PipeSide; + +typedef struct { + PipeSide* alices_side; + PipeSide* bobs_side; +} PipeSideBundle; + +typedef struct { + size_t capacity; + size_t trigger_level; +} PipeSideReceiveSettings; + +/** + * @brief Allocates two connected sides of one pipe. + * + * Creating a pair of sides using this function is the only way to connect two + * pipe sides together. Two unrelated orphaned sides may never be connected back + * together. + * + * The capacity and trigger level for both directions are the same when the pipe + * is created using this function. Use `pipe_alloc_ex` if you want more + * control. + * + * @param capacity Maximum number of bytes buffered in one direction + * @param trigger_level Number of bytes that need to be available in the buffer + * in order for a blocked thread to unblock + * @returns Bundle with both sides of the pipe + */ +PipeSideBundle pipe_alloc(size_t capacity, size_t trigger_level); + +/** + * @brief Allocates two connected sides of one pipe. + * + * Creating a pair of sides using this function is the only way to connect two + * pipe sides together. Two unrelated orphaned sides may never be connected back + * together. + * + * The capacity and trigger level may be different for the two directions when + * the pipe is created using this function. Use `pipe_alloc` if you don't + * need control this fine. + * + * @param alice `capacity` and `trigger_level` settings for Alice's receiving + * buffer + * @param bob `capacity` and `trigger_level` settings for Bob's receiving buffer + * @returns Bundle with both sides of the pipe + */ +PipeSideBundle pipe_alloc_ex(PipeSideReceiveSettings alice, PipeSideReceiveSettings bob); + +/** + * @brief Gets the role of a pipe side. + * + * The roles (Alice and Bob) are equal, as both can send and receive data. This + * status might be helpful in determining the role of a thread w.r.t. another + * thread. + * + * @param [in] pipe Pipe side to query + * @returns Role of provided pipe side + */ +PipeRole pipe_role(PipeSide* pipe); + +/** + * @brief Gets the state of a pipe. + * + * When the state is `PipeStateOpen`, both sides are active and may send or + * receive data. When the state is `PipeStateBroken`, only one side is active + * (the one that this method has been called on). If you find yourself in that + * state, the data that you send will never be heard by anyone, and the data you + * receive are leftovers in the buffer. + * + * @param [in] pipe Pipe side to query + * @returns State of the pipe + */ +PipeState pipe_state(PipeSide* pipe); + +/** + * @brief Frees a side of a pipe. + * + * When only one of the sides is freed, the pipe is transitioned from the "Open" + * state into the "Broken" state. When both sides are freed, the underlying data + * structures are freed too. + * + * @param [in] pipe Pipe side to free + */ +void pipe_free(PipeSide* pipe); + +/** + * @brief Connects the pipe to the `stdin` and `stdout` of the current thread. + * + * After performing this operation, you can use `getc`, `puts`, etc. to send and + * receive data to and from the pipe. If the pipe becomes broken, C stdlib calls + * will return `EOF` wherever possible. + * + * You can disconnect the pipe by manually calling + * `furi_thread_set_stdout_callback` and `furi_thread_set_stdin_callback` with + * `NULL`. + * + * @param [in] pipe Pipe side to connect to the stdio + */ +void pipe_install_as_stdio(PipeSide* pipe); + +/** + * @brief Receives data from the pipe. + * + * @param [in] pipe The pipe side to read data out of + * @param [out] data The buffer to fill with data + * @param length Maximum length of data to read + * @param timeout The timeout (in ticks) after which the read operation is + * interrupted + * @returns The number of bytes actually written into the provided buffer + */ +size_t pipe_receive(PipeSide* pipe, void* data, size_t length, FuriWait timeout); + +/** + * @brief Sends data into the pipe. + * + * @param [in] pipe The pipe side to send data into + * @param [out] data The buffer to get data from + * @param length Maximum length of data to send + * @param timeout The timeout (in ticks) after which the write operation is + * interrupted + * @returns The number of bytes actually read from the provided buffer + */ +size_t pipe_send(PipeSide* pipe, const void* data, size_t length, FuriWait timeout); + +/** + * @brief Determines how many bytes there are in the pipe available to be read. + * + * @param [in] pipe Pipe side to query + * @returns Number of bytes available to be read out from that side of the pipe + */ +size_t pipe_bytes_available(PipeSide* pipe); + +/** + * @brief Determines how many space there is in the pipe for data to be written + * into. + * + * @param [in] pipe Pipe side to query + * @returns Number of bytes available to be written into that side of the pipe + */ +size_t pipe_spaces_available(PipeSide* pipe); + +/** + * @brief Attaches a `PipeSide` to a `FuriEventLoop`, allowing to attach + * callbacks to the PipeSide. + * + * @param [in] pipe Pipe side to attach to the event loop + * @param [in] event_loop Event loop to attach the pipe side to + */ +void pipe_attach_to_event_loop(PipeSide* pipe, FuriEventLoop* event_loop); + +/** + * @brief Detaches a `PipeSide` from the `FuriEventLoop` that it was previously + * attached to. + * + * @param [in] pipe Pipe side to detach to the event loop + */ +void pipe_detach_from_event_loop(PipeSide* pipe); + +/** + * @brief Callback for when data arrives to a `PipeSide`. + * + * @param [in] pipe Pipe side that called the callback + * @param [inout] context Custom context + */ +typedef void (*PipeSideDataArrivedCallback)(PipeSide* pipe, void* context); + +/** + * @brief Callback for when data is read out of the opposite `PipeSide`. + * + * @param [in] pipe Pipe side that called the callback + * @param [inout] context Custom context + */ +typedef void (*PipeSideSpaceFreedCallback)(PipeSide* pipe, void* context); + +/** + * @brief Callback for when the opposite `PipeSide` is freed, making the pipe + * broken. + * + * @param [in] pipe Pipe side that called the callback + * @param [inout] context Custom context + */ +typedef void (*PipeSideBrokenCallback)(PipeSide* pipe, void* context); + +/** + * @brief Sets the custom context for all callbacks. + * + * @param [in] pipe Pipe side to set the context of + * @param [inout] context Custom context that will be passed to callbacks + */ +void pipe_set_callback_context(PipeSide* pipe, void* context); + +/** + * @brief Sets the callback for when data arrives. + * + * @param [in] pipe Pipe side to assign the callback to + * @param [in] callback Callback to assign to the pipe side. Set to NULL to + * unsubscribe. + * @param [in] event Additional event loop flags (e.g. `Edge`, `Once`, etc.). + * Non-flag values of the enum are not allowed. + * + * @warning Attach the pipe side to an event loop first using + * `pipe_attach_to_event_loop`. + */ +void pipe_set_data_arrived_callback( + PipeSide* pipe, + PipeSideDataArrivedCallback callback, + FuriEventLoopEvent event); + +/** + * @brief Sets the callback for when data is read out of the opposite `PipeSide`. + * + * @param [in] pipe Pipe side to assign the callback to + * @param [in] callback Callback to assign to the pipe side. Set to NULL to + * unsubscribe. + * @param [in] event Additional event loop flags (e.g. `Edge`, `Once`, etc.). + * Non-flag values of the enum are not allowed. + * + * @warning Attach the pipe side to an event loop first using + * `pipe_attach_to_event_loop`. + */ +void pipe_set_space_freed_callback( + PipeSide* pipe, + PipeSideSpaceFreedCallback callback, + FuriEventLoopEvent event); + +/** + * @brief Sets the callback for when the opposite `PipeSide` is freed, making + * the pipe broken. + * + * @param [in] pipe Pipe side to assign the callback to + * @param [in] callback Callback to assign to the pipe side. Set to NULL to + * unsubscribe. + * @param [in] event Additional event loop flags (e.g. `Edge`, `Once`, etc.). + * Non-flag values of the enum are not allowed. + * + * @warning Attach the pipe side to an event loop first using + * `pipe_attach_to_event_loop`. + */ +void pipe_set_broken_callback( + PipeSide* pipe, + PipeSideBrokenCallback callback, + FuriEventLoopEvent event); + +#ifdef __cplusplus +} +#endif diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index e18aee04d..0a4b7dde6 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,79.1,, +Version,+,79.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, @@ -161,6 +161,7 @@ Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5_calc.h,, Header,+,lib/toolbox/name_generator.h,, Header,+,lib/toolbox/path.h,, +Header,+,lib/toolbox/pipe.h,, Header,+,lib/toolbox/pretty_format.h,, Header,+,lib/toolbox/protocols/protocol_dict.h,, Header,+,lib/toolbox/pulse_protocols/pulse_glue.h,, @@ -1580,6 +1581,7 @@ Function,+,furi_stream_buffer_receive,size_t,"FuriStreamBuffer*, void*, size_t, Function,+,furi_stream_buffer_reset,FuriStatus,FuriStreamBuffer* Function,+,furi_stream_buffer_send,size_t,"FuriStreamBuffer*, const void*, size_t, uint32_t" Function,+,furi_stream_buffer_spaces_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_get_trigger_level,size_t,FuriStreamBuffer* Function,+,furi_stream_set_trigger_level,_Bool,"FuriStreamBuffer*, size_t" Function,+,furi_string_alloc,FuriString*, Function,+,furi_string_alloc_move,FuriString*,FuriString* @@ -2290,6 +2292,22 @@ Function,+,pb_skip_field,_Bool,"pb_istream_t*, pb_wire_type_t" Function,+,pb_write,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" Function,-,pclose,int,FILE* Function,-,perror,void,const char* +Function,+,pipe_alloc,PipeSideBundle,"size_t, size_t" +Function,+,pipe_alloc_ex,PipeSideBundle,"PipeSideReceiveSettings, PipeSideReceiveSettings" +Function,+,pipe_attach_to_event_loop,void,"PipeSide*, FuriEventLoop*" +Function,+,pipe_bytes_available,size_t,PipeSide* +Function,+,pipe_detach_from_event_loop,void,PipeSide* +Function,+,pipe_free,void,PipeSide* +Function,+,pipe_install_as_stdio,void,PipeSide* +Function,+,pipe_receive,size_t,"PipeSide*, void*, size_t, FuriWait" +Function,+,pipe_role,PipeRole,PipeSide* +Function,+,pipe_send,size_t,"PipeSide*, const void*, size_t, FuriWait" +Function,+,pipe_set_callback_context,void,"PipeSide*, void*" +Function,+,pipe_set_data_arrived_callback,void,"PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent" +Function,+,pipe_set_broken_callback,void,"PipeSide*, PipeSideBrokenCallback, FuriEventLoopEvent" +Function,+,pipe_set_space_freed_callback,void,"PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent" +Function,+,pipe_spaces_available,size_t,PipeSide* +Function,+,pipe_state,PipeState,PipeSide* Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" Function,+,plugin_manager_free,void,PluginManager* Function,+,plugin_manager_get,const FlipperAppPluginDescriptor*,"PluginManager*, uint32_t" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 51404c951..15f4d70d7 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,79.1,, +Version,+,79.2,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -233,6 +233,7 @@ Header,+,lib/toolbox/manchester_encoder.h,, Header,+,lib/toolbox/md5_calc.h,, Header,+,lib/toolbox/name_generator.h,, Header,+,lib/toolbox/path.h,, +Header,+,lib/toolbox/pipe.h,, Header,+,lib/toolbox/pretty_format.h,, Header,+,lib/toolbox/protocols/protocol_dict.h,, Header,+,lib/toolbox/pulse_protocols/pulse_glue.h,, @@ -1799,6 +1800,7 @@ Function,+,furi_stream_buffer_receive,size_t,"FuriStreamBuffer*, void*, size_t, Function,+,furi_stream_buffer_reset,FuriStatus,FuriStreamBuffer* Function,+,furi_stream_buffer_send,size_t,"FuriStreamBuffer*, const void*, size_t, uint32_t" Function,+,furi_stream_buffer_spaces_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_get_trigger_level,size_t,FuriStreamBuffer* Function,+,furi_stream_set_trigger_level,_Bool,"FuriStreamBuffer*, size_t" Function,+,furi_string_alloc,FuriString*, Function,+,furi_string_alloc_move,FuriString*,FuriString* @@ -2926,6 +2928,22 @@ Function,+,pb_skip_field,_Bool,"pb_istream_t*, pb_wire_type_t" Function,+,pb_write,_Bool,"pb_ostream_t*, const pb_byte_t*, size_t" Function,-,pclose,int,FILE* Function,-,perror,void,const char* +Function,+,pipe_alloc,PipeSideBundle,"size_t, size_t" +Function,+,pipe_alloc_ex,PipeSideBundle,"PipeSideReceiveSettings, PipeSideReceiveSettings" +Function,+,pipe_attach_to_event_loop,void,"PipeSide*, FuriEventLoop*" +Function,+,pipe_bytes_available,size_t,PipeSide* +Function,+,pipe_detach_from_event_loop,void,PipeSide* +Function,+,pipe_free,void,PipeSide* +Function,+,pipe_install_as_stdio,void,PipeSide* +Function,+,pipe_receive,size_t,"PipeSide*, void*, size_t, FuriWait" +Function,+,pipe_role,PipeRole,PipeSide* +Function,+,pipe_send,size_t,"PipeSide*, const void*, size_t, FuriWait" +Function,+,pipe_set_callback_context,void,"PipeSide*, void*" +Function,+,pipe_set_data_arrived_callback,void,"PipeSide*, PipeSideDataArrivedCallback, FuriEventLoopEvent" +Function,+,pipe_set_broken_callback,void,"PipeSide*, PipeSideBrokenCallback, FuriEventLoopEvent" +Function,+,pipe_set_space_freed_callback,void,"PipeSide*, PipeSideSpaceFreedCallback, FuriEventLoopEvent" +Function,+,pipe_spaces_available,size_t,PipeSide* +Function,+,pipe_state,PipeState,PipeSide* Function,+,plugin_manager_alloc,PluginManager*,"const char*, uint32_t, const ElfApiInterface*" Function,+,plugin_manager_free,void,PluginManager* Function,+,plugin_manager_get,const FlipperAppPluginDescriptor*,"PluginManager*, uint32_t" From 5fb9558dbb1b48dca1d48e6bffad9f7bf9d9f7a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2024 11:24:01 +0900 Subject: [PATCH 27/27] Bump cross-spawn in /applications/system/js_app/packages/create-fz-app (#4043) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 7.0.3 to 7.0.6. - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/master/CHANGELOG.md) - [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v7.0.3...v7.0.6) --- updated-dependencies: - dependency-name: cross-spawn dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: あく --- .../system/js_app/packages/create-fz-app/pnpm-lock.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/system/js_app/packages/create-fz-app/pnpm-lock.yaml b/applications/system/js_app/packages/create-fz-app/pnpm-lock.yaml index 58f20a385..3f753df15 100644 --- a/applications/system/js_app/packages/create-fz-app/pnpm-lock.yaml +++ b/applications/system/js_app/packages/create-fz-app/pnpm-lock.yaml @@ -62,8 +62,8 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} eastasianwidth@0.2.0: @@ -240,7 +240,7 @@ snapshots: color-name@1.1.4: {} - cross-spawn@7.0.3: + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 @@ -256,7 +256,7 @@ snapshots: foreground-child@3.3.0: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 signal-exit: 4.1.0 get-caller-file@2.0.5: {}