From 74cda02368252fa50d9c3dc2c0a78749572b6e01 Mon Sep 17 00:00:00 2001 From: gornekich Date: Fri, 29 Mar 2024 01:32:15 +0000 Subject: [PATCH 01/16] [FL-3803] NFC: Fix mf desfire detect (#3548) --- lib/nfc/protocols/mf_desfire/mf_desfire_poller.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c index 246107616d..c9d8bbab60 100644 --- a/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c +++ b/lib/nfc/protocols/mf_desfire/mf_desfire_poller.c @@ -225,9 +225,17 @@ static bool mf_desfire_poller_detect(NfcGenericEvent event, void* context) { bool protocol_detected = false; if(iso14443_4a_event->type == Iso14443_4aPollerEventTypeReady) { - MfDesfireKeyVersion key_version = {0}; - MfDesfireError error = mf_desfire_poller_read_key_version(instance, 0, &key_version); - protocol_detected = (error == MfDesfireErrorNone); + do { + MfDesfireKeyVersion key_version = 0; + MfDesfireError error = mf_desfire_poller_read_key_version(instance, 0, &key_version); + if(error != MfDesfireErrorNone) break; + + MfDesfireVersion version = {}; + error = mf_desfire_poller_read_version(instance, &version); + if(error != MfDesfireErrorNone) break; + + protocol_detected = true; + } while(false); } return protocol_detected; From 538422e2a9b8601959d0c08758967151073684cc Mon Sep 17 00:00:00 2001 From: hedger Date: Fri, 29 Mar 2024 05:47:43 +0400 Subject: [PATCH 02/16] ufbt: fixes for generated vscode project (#3547) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .vscode/example/settings.json | 2 +- scripts/ufbt/SConstruct | 4 +++- .../ufbt/project_template/.vscode/tasks.json | 18 ++++++------------ 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.vscode/example/settings.json b/.vscode/example/settings.json index 00da3af2f8..a591949013 100644 --- a/.vscode/example/settings.json +++ b/.vscode/example/settings.json @@ -14,7 +14,7 @@ "*.scons": "python", "SConscript": "python", "SConstruct": "python", - "*.fam": "python", + "*.fam": "python" }, "clangd.arguments": [ // We might be able to tighten this a bit more to only include the correct toolchain. diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 11fefd22b5..784d661612 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -393,7 +393,9 @@ for template_file in project_template_dir.Dir(".vscode").glob("*"): ), "@UFBT_APP_DIR@": PosixPathWrapper.fix_path(original_app_dir.abspath), "@UFBT_ROOT_DIR@": PosixPathWrapper.fix_path(Dir("#").abspath), - "@UFBT_DEBUG_DIR@": dist_env.subst("FBT_DEBUG_DIR"), + "@UFBT_DEBUG_DIR@": PosixPathWrapper.fix_path( + dist_env.subst("$FBT_DEBUG_DIR") + ), "@UFBT_DEBUG_ELF_DIR@": PosixPathWrapper.fix_path( dist_env["FBT_FAP_DEBUG_ELF_ROOT"].abspath ), diff --git a/scripts/ufbt/project_template/.vscode/tasks.json b/scripts/ufbt/project_template/.vscode/tasks.json index 65c749e07b..16ef12836b 100644 --- a/scripts/ufbt/project_template/.vscode/tasks.json +++ b/scripts/ufbt/project_template/.vscode/tasks.json @@ -31,28 +31,22 @@ "command": "ufbt -c" }, { - "label": "Flash FW (ST-Link)", + "label": "Flash FW (SWD)", "group": "build", "type": "shell", "command": "ufbt FORCE=1 flash" }, { - "label": "Flash FW (blackmagic)", - "group": "build", - "type": "shell", - "command": "ufbt flash_blackmagic" - }, - { - "label": "Flash FW (JLink)", + "label": "Flash FW (USB, with resources)", "group": "build", "type": "shell", - "command": "ufbt FORCE=1 jflash" + "command": "ufbt FORCE=1 flash_usb" }, { - "label": "Flash FW (USB, with resources)", + "label": "Open Flipper CLI session", "group": "build", "type": "shell", - "command": "ufbt FORCE=1 flash_usb" + "command": "ufbt cli" }, { "label": "Update uFBT SDK", @@ -61,7 +55,7 @@ "command": "ufbt update" }, { - "label": "Update VSCode config for current SDK", + "label": "Update VSCode config", "group": "build", "type": "shell", "command": "ufbt vscode_dist" From c353182353895daa3470f486c7c8123d280ee350 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Fri, 29 Mar 2024 05:45:01 +0300 Subject: [PATCH 03/16] [FL-3800, FL-3801] Fixed plugins and UI (#3543) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed some UI mismatches in plugin and nfc app * Fixed nameing mismatches in mosgortrans_util * Fix ultralight naming display Co-authored-by: gornekich Co-authored-by: あく --- .../main/nfc/api/mosgortrans/mosgortrans_util.c | 14 +++++++------- .../protocol_support/mf_ultralight/mf_ultralight.c | 2 ++ .../protocol_support/st25tb/st25tb_render.c | 13 +++++++++---- applications/main/nfc/nfc_app.c | 2 +- .../main/nfc/plugins/supported_cards/mykey.c | 7 +++---- 5 files changed, 22 insertions(+), 16 deletions(-) diff --git a/applications/main/nfc/api/mosgortrans/mosgortrans_util.c b/applications/main/nfc/api/mosgortrans/mosgortrans_util.c index f2484a2af5..ef1e18f8d5 100644 --- a/applications/main/nfc/api/mosgortrans/mosgortrans_util.c +++ b/applications/main/nfc/api/mosgortrans/mosgortrans_util.c @@ -464,21 +464,21 @@ void parse_transport_type(BlockData* data_block, FuriString* transport) { bool mosgortrans_parse_transport_block(const MfClassicBlock* block, FuriString* result) { BlockData data_block = {}; const uint16_t valid_departments[] = {0x106, 0x108, 0x10A, 0x10E, 0x110, 0x117}; - uint16_t transport_departament = bit_lib_get_bits_16(block->data, 0, 10); + uint16_t transport_department = bit_lib_get_bits_16(block->data, 0, 10); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - furi_string_cat_printf(result, "Transport departament: %x\n", transport_departament); + furi_string_cat_printf(result, "Transport department: %x\n", transport_department); } - bool departament_valid = false; + bool department_valid = false; for(uint8_t i = 0; i < 6; i++) { - if(transport_departament == valid_departments[i]) { - departament_valid = true; + if(transport_department == valid_departments[i]) { + department_valid = true; break; } } - if(!departament_valid) { + if(!department_valid) { return false; } - FURI_LOG_D(TAG, "Transport departament: %x", transport_departament); + FURI_LOG_D(TAG, "Transport department: %x", transport_department); uint16_t layout_type = bit_lib_get_bits_16(block->data, 52, 4); if(layout_type == 0xE) { layout_type = bit_lib_get_bits_16(block->data, 52, 9); diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index c2aaac5bee..0fe5a7664e 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -30,6 +30,8 @@ static void nfc_scene_info_on_enter_mf_ultralight(NfcApp* instance) { furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeFull, temp_str); widget_add_text_scroll_element( diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c index e3a0f3c50f..6764f99a3d 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c @@ -1,22 +1,27 @@ #include "st25tb_render.h" #include +#include void nfc_render_st25tb_info( const St25tbData* data, NfcProtocolFormatType format_type, FuriString* str) { - furi_string_cat_printf(str, "UID"); + furi_string_cat_printf(str, "UID:"); for(size_t i = 0; i < ST25TB_UID_SIZE; i++) { furi_string_cat_printf(str, " %02X", data->uid[i]); } if(format_type == NfcProtocolFormatTypeFull) { - furi_string_cat_printf(str, "\nSys. OTP: %08lX", data->system_otp_block); - furi_string_cat_printf(str, "\nBlocks:"); + furi_string_cat_printf(str, "\nSys. OTP: %08lX", __bswap32(data->system_otp_block)); + furi_string_cat_printf(str, "\n::::::::::::::::::::::[Blocks]::::::::::::::::::::::"); for(size_t i = 0; i < st25tb_get_block_count(data->type); i += 2) { furi_string_cat_printf( - str, "\n %02X %08lX %08lX", i, data->blocks[i], data->blocks[i + 1]); + str, + "\n %02X %08lX %08lX", + i, + __bswap32(data->blocks[i]), + __bswap32(data->blocks[i + 1])); } } } diff --git a/applications/main/nfc/nfc_app.c b/applications/main/nfc/nfc_app.c index 26821c8f71..02f852e346 100644 --- a/applications/main/nfc/nfc_app.c +++ b/applications/main/nfc/nfc_app.c @@ -453,7 +453,7 @@ void nfc_append_filename_string_when_present(NfcApp* instance, FuriString* strin furi_assert(string); if(!furi_string_empty(instance->file_name)) { - furi_string_cat_printf(string, "Name:%s\n", furi_string_get_cstr(instance->file_name)); + furi_string_cat_printf(string, "Name: %s\n", furi_string_get_cstr(instance->file_name)); } } diff --git a/applications/main/nfc/plugins/supported_cards/mykey.c b/applications/main/nfc/plugins/supported_cards/mykey.c index ac5fec97c2..ed4c4cf9a5 100644 --- a/applications/main/nfc/plugins/supported_cards/mykey.c +++ b/applications/main/nfc/plugins/supported_cards/mykey.c @@ -85,11 +85,10 @@ static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { bool is_blank = mykey_is_blank(data); furi_string_cat_printf(parsed_data, "Serial#: %08lX\n", (uint32_t)__bswap32(data->blocks[7])); - furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no"); - furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no"); - furi_string_cat_printf( - parsed_data, "Prod. date: %02X/%02X/%04X", mfg_day, mfg_month, mfg_year); + parsed_data, "Prod. date: %02X/%02X/%04X\n", mfg_day, mfg_month, mfg_year); + furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no"); + furi_string_cat_printf(parsed_data, "LockID: %s", mykey_has_lockid(data) ? "maybe" : "no"); if(!is_blank) { furi_string_cat_printf( From 64bd2f9c84b7ac33a2b1dff5be4d357c8848ebbd Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Fri, 29 Mar 2024 06:32:43 +0300 Subject: [PATCH 04/16] [FL-3677, FL-3798] RFID Improvements (#3524) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update saved_info and read_success scenes * Update EM4100 rendering * Update HIDExt rendering * Update Gallagher rendering * Update HidProx rendering * Update IOProx rendering * Update H10301 rendering * Update PAC/Stanley rendering * Add strcasecmp() to API, better manufacturer/name handling * Update Viking rendering * Update FDX-A rendering * Update Pyramid rendering * Update Indala26 rendering * Update Idteck rendering * Update Keri rendering * Update Nexwatch rendering * Update Jablotron rendering * Update Paradox rendering * Truncate long Hex string on scene_read_suceess * Fix formatting * Update AWID rendering * Update FDX-B rendering * Tweak string formatting in various screens * More read_success view tweaks * Fix formatting * Fix Pyramid brief rendering * Reset saved key menu when going back * Reset other menus on back where applicable * Update confirmation scenes * Update emulation scene * Update delete scene * Update raw read info screen * Update raw read scene, fix crash * Update raw read success scene * Update write scene * Always return to SceneSelectKey after saving * Update SceneWriteSuccess and SceneDeleteSuccess * Replace closing parens with dots * FL-3798: Fix special formatting in text_box * Simplify SceneReadSuccess * Fix crash when having a trailing newline in text_box * Bump API symbols version * Make PVS happy * Format sources Co-authored-by: あく --- applications/main/lfrfid/lfrfid_i.h | 7 ++ .../scenes/lfrfid_scene_delete_confirm.c | 59 +++++++++-------- .../scenes/lfrfid_scene_delete_success.c | 7 +- .../main/lfrfid/scenes/lfrfid_scene_emulate.c | 29 ++++---- .../lfrfid/scenes/lfrfid_scene_exit_confirm.c | 4 +- .../scenes/lfrfid_scene_extra_actions.c | 3 + .../lfrfid/scenes/lfrfid_scene_raw_info.c | 34 +++++----- .../lfrfid/scenes/lfrfid_scene_raw_read.c | 66 ++++++++++++------- .../lfrfid/scenes/lfrfid_scene_raw_success.c | 22 +++++-- .../scenes/lfrfid_scene_read_key_menu.c | 3 + .../lfrfid/scenes/lfrfid_scene_read_success.c | 65 +++++++++--------- .../scenes/lfrfid_scene_retry_confirm.c | 7 +- .../lfrfid/scenes/lfrfid_scene_save_success.c | 13 +--- .../lfrfid/scenes/lfrfid_scene_saved_info.c | 56 ++++++++-------- .../scenes/lfrfid_scene_saved_key_menu.c | 3 + .../lfrfid/scenes/lfrfid_scene_select_key.c | 4 +- .../main/lfrfid/scenes/lfrfid_scene_start.c | 38 +++++------ .../main/lfrfid/scenes/lfrfid_scene_write.c | 30 +++++---- .../scenes/lfrfid_scene_write_success.c | 12 ++-- .../protocol_support/st25tb/st25tb_render.c | 7 +- applications/services/gui/elements.c | 48 +++++++------- lib/lfrfid/protocols/protocol_awid.c | 27 +++++--- lib/lfrfid/protocols/protocol_em4100.c | 3 +- lib/lfrfid/protocols/protocol_fdx_a.c | 14 ++-- lib/lfrfid/protocols/protocol_fdx_b.c | 45 ++++++++----- lib/lfrfid/protocols/protocol_gallagher.c | 43 +++++++++--- lib/lfrfid/protocols/protocol_h10301.c | 6 +- .../protocols/protocol_hid_ex_generic.c | 10 ++- lib/lfrfid/protocols/protocol_hid_generic.c | 4 +- lib/lfrfid/protocols/protocol_idteck.c | 28 +++----- lib/lfrfid/protocols/protocol_indala26.c | 19 +++--- lib/lfrfid/protocols/protocol_io_prox_xsf.c | 12 ++-- lib/lfrfid/protocols/protocol_jablotron.c | 4 +- lib/lfrfid/protocols/protocol_keri.c | 35 ++++++++-- lib/lfrfid/protocols/protocol_nexwatch.c | 40 +++++++++-- lib/lfrfid/protocols/protocol_pac_stanley.c | 3 +- lib/lfrfid/protocols/protocol_paradox.c | 29 +++++--- lib/lfrfid/protocols/protocol_pyramid.c | 6 +- lib/lfrfid/protocols/protocol_viking.c | 5 +- targets/f18/api_symbols.csv | 4 +- targets/f7/api_symbols.csv | 4 +- 41 files changed, 509 insertions(+), 349 deletions(-) diff --git a/applications/main/lfrfid/lfrfid_i.h b/applications/main/lfrfid/lfrfid_i.h index d94a5f865a..7062315450 100644 --- a/applications/main/lfrfid/lfrfid_i.h +++ b/applications/main/lfrfid/lfrfid_i.h @@ -122,6 +122,13 @@ typedef enum { LfRfidViewRead, } LfRfidView; +typedef enum { + LfRfidMenuIndexRead, + LfRfidMenuIndexSaved, + LfRfidMenuIndexAddManually, + LfRfidMenuIndexExtraActions, +} LfRfidMenuIndex; + bool lfrfid_save_key(LfRfid* app); bool lfrfid_load_key_from_file_select(LfRfid* app); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_confirm.c index b7702e2691..17d024d361 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_delete_confirm.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_confirm.c @@ -1,45 +1,50 @@ #include "../lfrfid_i.h" +#define LFRFID_SCENE_DELETE_MAX_HEX_WIDTH (7UL) + void lfrfid_scene_delete_confirm_on_enter(void* context) { LfRfid* app = context; Widget* widget = app->widget; - FuriString* tmp_string; - tmp_string = furi_string_alloc(); + FuriString* display_text = furi_string_alloc_printf( + "\e#Delete %s?\e#\n" + "Hex: ", + furi_string_get_cstr(app->file_name)); - widget_add_button_element(widget, GuiButtonTypeLeft, "Back", lfrfid_widget_callback, app); - widget_add_button_element(widget, GuiButtonTypeRight, "Delete", lfrfid_widget_callback, app); + const size_t data_size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = malloc(data_size); + + protocol_dict_get_data(app->dict, app->protocol_id, data, data_size); - furi_string_printf(tmp_string, "Delete %s?", furi_string_get_cstr(app->file_name)); - widget_add_string_element( - widget, 64, 0, AlignCenter, AlignTop, FontPrimary, furi_string_get_cstr(tmp_string)); - - furi_string_reset(tmp_string); - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - uint8_t* data = (uint8_t*)malloc(size); - protocol_dict_get_data(app->dict, app->protocol_id, data, size); - for(uint8_t i = 0; i < MIN(size, (size_t)8); i++) { - if(i != 0) { - furi_string_cat_printf(tmp_string, " "); + for(size_t i = 0; i < data_size; i++) { + if(i == LFRFID_SCENE_DELETE_MAX_HEX_WIDTH) { + furi_string_cat(display_text, " ..."); + break; } - furi_string_cat_printf(tmp_string, "%02X", data[i]); + furi_string_cat_printf(display_text, "%s%02X", i != 0 ? " " : "", data[i]); } + + furi_string_push_back(display_text, '\n'); + free(data); - widget_add_string_element( - widget, 64, 19, AlignCenter, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string)); - widget_add_string_element( - widget, - 64, - 49, - AlignCenter, - AlignBottom, - FontSecondary, - protocol_dict_get_name(app->dict, app->protocol_id)); + const char* protocol = protocol_dict_get_name(app->dict, app->protocol_id); + const char* manufacturer = protocol_dict_get_manufacturer(app->dict, app->protocol_id); + + if(strcasecmp(protocol, manufacturer) != 0 && strcasecmp(manufacturer, "N/A") != 0) { + furi_string_cat_printf(display_text, "%s ", manufacturer); + } + + furi_string_cat(display_text, protocol); + + widget_add_text_box_element( + widget, 0, 0, 128, 64, AlignCenter, AlignTop, furi_string_get_cstr(display_text), true); + widget_add_button_element(widget, GuiButtonTypeLeft, "Cancel", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "Delete", lfrfid_widget_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); - furi_string_free(tmp_string); + furi_string_free(display_text); } bool lfrfid_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c index abb173a73d..9c9b39d3d0 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c @@ -18,10 +18,9 @@ bool lfrfid_scene_delete_success_on_event(void* context, SceneManagerEvent event LfRfid* app = context; bool consumed = false; - if((event.type == SceneManagerEventTypeBack) || - ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) { - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, LfRfidSceneSelectKey); + if(event.type == SceneManagerEventTypeBack || event.type == SceneManagerEventTypeCustom) { + // Always return to SceneSelectKey from here + scene_manager_search_and_switch_to_another_scene(app->scene_manager, LfRfidSceneSelectKey); consumed = true; } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index dc39189942..049c24d002 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -2,27 +2,28 @@ void lfrfid_scene_emulate_on_enter(void* context) { LfRfid* app = context; - Popup* popup = app->popup; + Widget* widget = app->widget; - popup_set_header(popup, "Emulating", 89, 30, AlignCenter, AlignTop); - if(!furi_string_empty(app->file_name)) { - popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); + FuriString* display_text = furi_string_alloc_set("\e#Emulating\e#\n"); + + if(furi_string_empty(app->file_name)) { + furi_string_cat(display_text, "Unsaved\n"); + furi_string_cat(display_text, protocol_dict_get_name(app->dict, app->protocol_id)); } else { - popup_set_text( - popup, - protocol_dict_get_name(app->dict, app->protocol_id), - 89, - 43, - AlignCenter, - AlignTop); + furi_string_cat(display_text, app->file_name); } - popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + widget_add_icon_element(widget, 0, 0, &I_NFC_dolphin_emulation_51x64); + widget_add_text_box_element( + widget, 55, 16, 67, 48, AlignCenter, AlignTop, furi_string_get_cstr(display_text), true); + + furi_string_free(display_text); lfrfid_worker_start_thread(app->lfworker); lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); notification_message(app->notifications, &sequence_blink_start_magenta); - view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); } bool lfrfid_scene_emulate_on_event(void* context, SceneManagerEvent event) { @@ -35,7 +36,7 @@ bool lfrfid_scene_emulate_on_event(void* context, SceneManagerEvent event) { void lfrfid_scene_emulate_on_exit(void* context) { LfRfid* app = context; notification_message(app->notifications, &sequence_blink_stop); - popup_reset(app->popup); + widget_reset(app->widget); lfrfid_worker_stop(app->lfworker); lfrfid_worker_stop_thread(app->lfworker); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_exit_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_exit_confirm.c index e8ab481c6f..55f02d7402 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_exit_confirm.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_exit_confirm.c @@ -7,9 +7,9 @@ void lfrfid_scene_exit_confirm_on_enter(void* context) { widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app); widget_add_button_element(widget, GuiButtonTypeRight, "Stay", lfrfid_widget_callback, app); widget_add_string_element( - widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to RFID Menu?"); + widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Exit to RFID Menu?"); widget_add_string_element( - widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); + widget, 64, 13, AlignCenter, AlignTop, FontSecondary, "All unsaved data will be lost"); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c index 1aed9a03c3..f2e0998286 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -70,6 +70,9 @@ bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneExtraActions, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneExtraActions, 0); } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c index f403c1f38e..ba85c6ca1f 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_info.c @@ -4,37 +4,39 @@ void lfrfid_scene_raw_info_on_enter(void* context) { LfRfid* app = context; Widget* widget = app->widget; - // FuriString* tmp_string; - // tmp_string = furi_string_alloc(); - - bool sd_exist = storage_sd_status(app->storage) == FSE_OK; - if(!sd_exist) { - widget_add_icon_element(widget, 0, 0, &I_SDQuestion_35x43); + if(storage_sd_status(app->storage) != FSE_OK) { + widget_add_icon_element(widget, 83, 22, &I_WarningDolphinFlip_45x42); + widget_add_string_element( + widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "No SD Card!"); widget_add_string_multiline_element( widget, - 81, - 4, - AlignCenter, + 0, + 13, + AlignLeft, AlignTop, FontSecondary, - "No SD card found.\nThis function will not\nwork without\nSD card."); + "Insert an SD card\n" + "to use this function"); - widget_add_button_element(widget, GuiButtonTypeLeft, "Back", lfrfid_widget_callback, app); } else { - widget_add_string_multiline_element( + widget_add_text_box_element( widget, 0, - 1, + 0, + 128, + 64, AlignLeft, AlignTop, - FontSecondary, - "RAW RFID data reader\n1) Put the Flipper on your card\n2) Press OK\n3) Wait until data is read"); + "\e#RAW RFID Data Reader\e#\n" + "1. Hold card next to Flipper\n" + "2. Press OK\n" + "3. Wait until data is read", + false); widget_add_button_element(widget, GuiButtonTypeCenter, "OK", lfrfid_widget_callback, app); } view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); - //furi_string_free(tmp_string); } bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c index b2c7c364e1..b86b86b217 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c @@ -1,6 +1,6 @@ #include "../lfrfid_i.h" -#define RAW_READ_TIME 5000 +#define RAW_READ_TIME_MS (5000UL) typedef struct { FuriString* string_file_name; @@ -29,24 +29,28 @@ void lfrfid_scene_raw_read_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - LfRfidReadRawState* state = malloc(sizeof(LfRfidReadRawState)); - scene_manager_set_scene_state(app->scene_manager, LfRfidSceneRawRead, (uint32_t)state); - state->string_file_name = furi_string_alloc(); + popup_set_icon(popup, 0, 0, &I_NFC_dolphin_emulation_51x64); + popup_set_header(popup, "Reading ASK", 91, 16, AlignCenter, AlignTop); + popup_set_text(popup, "Don't move\nfor 5 sec.", 91, 29, AlignCenter, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); - lfrfid_worker_start_thread(app->lfworker); - lfrfid_make_app_folder(app); + LfRfidReadRawState* state = malloc(sizeof(LfRfidReadRawState)); + state->string_file_name = furi_string_alloc(); state->timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app); - furi_timer_start(state->timer, RAW_READ_TIME); + + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneRawRead, (uint32_t)state); + furi_string_printf( state->string_file_name, "%s/%s%s", LFRFID_SD_FOLDER, furi_string_get_cstr(app->raw_file_name), LFRFID_APP_RAW_ASK_EXTENSION); - popup_set_header(popup, "Reading\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop); + + lfrfid_make_app_folder(app); + + lfrfid_worker_start_thread(app->lfworker); lfrfid_worker_read_raw_start( app->lfworker, furi_string_get_cstr(state->string_file_name), @@ -54,6 +58,7 @@ void lfrfid_scene_raw_read_on_enter(void* context) { lfrfid_read_callback, app); + furi_timer_start(state->timer, RAW_READ_TIME_MS); notification_message(app->notifications, &sequence_blink_start_cyan); state->is_psk = false; @@ -70,41 +75,53 @@ bool lfrfid_scene_raw_read_on_event(void* context, SceneManagerEvent event) { furi_assert(state); if(event.type == SceneManagerEventTypeCustom) { - if(event.event == LfRfidEventReadError) { - consumed = true; - state->error = true; - popup_set_header( - popup, "Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop); - notification_message(app->notifications, &sequence_blink_start_red); + if(event.event == LfRfidEventReadError || event.event == LfRfidEventReadOverrun) { furi_timer_stop(state->timer); + + popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); + popup_set_header(popup, "RAW Reading error!", 64, 0, AlignCenter, AlignTop); + popup_set_text( + popup, "This may be\ncaused by SD\ncard issues", 0, 13, AlignLeft, AlignTop); + + notification_message(app->notifications, &sequence_blink_start_red); + state->error = true; + } else if(event.event == LfRfidEventReadDone) { - consumed = true; if(!state->error) { if(state->is_psk) { notification_message(app->notifications, &sequence_success); scene_manager_next_scene(app->scene_manager, LfRfidSceneRawSuccess); + } else { - popup_set_header( - popup, "Reading\nRAW RFID\nPSK", 89, 30, AlignCenter, AlignTop); - notification_message(app->notifications, &sequence_blink_start_yellow); lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + + state->is_psk = true; + furi_string_printf( state->string_file_name, "%s/%s%s", LFRFID_SD_FOLDER, furi_string_get_cstr(app->raw_file_name), LFRFID_APP_RAW_PSK_EXTENSION); + + lfrfid_worker_start_thread(app->lfworker); lfrfid_worker_read_raw_start( app->lfworker, furi_string_get_cstr(state->string_file_name), LFRFIDWorkerReadTypePSKOnly, lfrfid_read_callback, app); - furi_timer_start(state->timer, RAW_READ_TIME); - state->is_psk = true; + + furi_timer_start(state->timer, RAW_READ_TIME_MS); + + popup_set_header(popup, "Reading PSK", 91, 16, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_blink_start_yellow); } } } + + consumed = true; } return consumed; @@ -115,12 +132,13 @@ void lfrfid_scene_raw_read_on_exit(void* context) { LfRfidReadRawState* state = (LfRfidReadRawState*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneRawRead); - notification_message(app->notifications, &sequence_blink_stop); - popup_reset(app->popup); lfrfid_worker_stop(app->lfworker); lfrfid_worker_stop_thread(app->lfworker); - furi_timer_free(state->timer); + furi_timer_free(state->timer); furi_string_free(state->string_file_name); free(state); + + popup_reset(app->popup); + notification_message(app->notifications, &sequence_blink_stop); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_success.c index 09a005298f..3fca9e3f12 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_raw_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_success.c @@ -4,16 +4,20 @@ void lfrfid_scene_raw_success_on_enter(void* context) { LfRfid* app = context; Widget* widget = app->widget; - widget_add_button_element(widget, GuiButtonTypeCenter, "OK", lfrfid_widget_callback, app); - - widget_add_string_multiline_element( + widget_add_text_box_element( widget, 0, - 1, + 0, + 128, + 64, AlignLeft, AlignTop, - FontSecondary, - "RAW RFID read success!\nNow you can analyze files\nOr send them to developers"); + "\e#RAW RFID Read Success\e#\n" + "Now you can analyze files or\n" + "send them to developers", + false); + + widget_add_button_element(widget, GuiButtonTypeCenter, "OK", lfrfid_widget_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); } @@ -24,12 +28,16 @@ bool lfrfid_scene_raw_success_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - consumed = true; if(event.event == GuiButtonTypeCenter) { scene_manager_search_and_switch_to_previous_scene( scene_manager, LfRfidSceneExtraActions); } + consumed = true; + + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; } + return consumed; } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c index 36f0d6d93a..22b083c76b 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_key_menu.c @@ -48,6 +48,9 @@ bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, 0); } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c index b83ef4a399..dbd3192294 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_read_success.c @@ -1,54 +1,53 @@ #include "../lfrfid_i.h" +#define LFRFID_SCENE_READ_SUCCESS_MAX_HEX_WIDTH (7UL) + void lfrfid_scene_read_success_on_enter(void* context) { LfRfid* app = context; Widget* widget = app->widget; + FuriString* display_text = furi_string_alloc(); - FuriString* tmp_string; - tmp_string = furi_string_alloc(); + const char* protocol = protocol_dict_get_name(app->dict, app->protocol_id); + const char* manufacturer = protocol_dict_get_manufacturer(app->dict, app->protocol_id); - widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app); - widget_add_button_element(widget, GuiButtonTypeRight, "More", lfrfid_widget_callback, app); + if(strcasecmp(protocol, manufacturer) != 0 && strcasecmp(manufacturer, "N/A") != 0) { + furi_string_printf(display_text, "\e#%s %s\e#", manufacturer, protocol); + } else { + furi_string_printf(display_text, "\e#%s\e#", protocol); + } + + furi_string_cat(display_text, "\nHex: "); + + const size_t data_size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = malloc(data_size); - furi_string_printf( - tmp_string, - "%s[%s]", - protocol_dict_get_name(app->dict, app->protocol_id), - protocol_dict_get_manufacturer(app->dict, app->protocol_id)); - - widget_add_string_element( - widget, 0, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp_string)); - - furi_string_reset(tmp_string); - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - uint8_t* data = (uint8_t*)malloc(size); - protocol_dict_get_data(app->dict, app->protocol_id, data, size); - for(uint8_t i = 0; i < size; i++) { - if(i >= 9) { - furi_string_cat_printf(tmp_string, ".."); + protocol_dict_get_data(app->dict, app->protocol_id, data, data_size); + + for(size_t i = 0; i < data_size; i++) { + if(i == LFRFID_SCENE_READ_SUCCESS_MAX_HEX_WIDTH) { + furi_string_cat(display_text, " ..."); break; - } else { - if(i != 0) { - furi_string_cat_printf(tmp_string, " "); - } - furi_string_cat_printf(tmp_string, "%02X", data[i]); } + + furi_string_cat_printf(display_text, "%s%02X", i != 0 ? " " : "", data[i]); } + free(data); - FuriString* render_data; - render_data = furi_string_alloc(); - protocol_dict_render_brief_data(app->dict, render_data, app->protocol_id); - furi_string_cat_printf(tmp_string, "\r\n%s", furi_string_get_cstr(render_data)); - furi_string_free(render_data); + FuriString* rendered_data = furi_string_alloc(); + protocol_dict_render_brief_data(app->dict, rendered_data, app->protocol_id); + furi_string_cat_printf(display_text, "\n%s", furi_string_get_cstr(rendered_data)); + furi_string_free(rendered_data); - widget_add_string_multiline_element( - widget, 0, 16, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string)); + widget_add_text_box_element( + widget, 0, 0, 128, 52, AlignLeft, AlignTop, furi_string_get_cstr(display_text), true); + widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "More", lfrfid_widget_callback, app); notification_message_block(app->notifications, &sequence_set_green_255); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); - furi_string_free(tmp_string); + furi_string_free(display_text); } bool lfrfid_scene_read_success_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c b/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c index ddac3e8ba5..41ff9afed9 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_retry_confirm.c @@ -4,12 +4,11 @@ void lfrfid_scene_retry_confirm_on_enter(void* context) { LfRfid* app = context; Widget* widget = app->widget; - widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app); widget_add_button_element(widget, GuiButtonTypeRight, "Stay", lfrfid_widget_callback, app); + widget_add_string_element(widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Retry Reading?"); widget_add_string_element( - widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Retry Reading?"); - widget_add_string_element( - widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); + widget, 64, 13, AlignCenter, AlignTop, FontSecondary, "All unsaved data will be lost"); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c index 2f5d5ae9ff..4eb21e9421 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -21,16 +21,9 @@ bool lfrfid_scene_save_success_on_event(void* context, SceneManagerEvent event) LfRfid* app = context; bool consumed = false; - const uint32_t prev_scenes[] = {LfRfidSceneReadKeyMenu, LfRfidSceneSelectKey}; - - if((event.type == SceneManagerEventTypeBack) || - ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) { - bool result = scene_manager_search_and_switch_to_previous_scene_one_of( - app->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); - if(!result) { - scene_manager_search_and_switch_to_another_scene( - app->scene_manager, LfRfidSceneSelectKey); - } + if(event.type == SceneManagerEventTypeBack || event.type == SceneManagerEventTypeCustom) { + // Always return to SceneSelectKey from here + scene_manager_search_and_switch_to_another_scene(app->scene_manager, LfRfidSceneSelectKey); consumed = true; } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c index 3f1c2d400e..a057b7a71a 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_info.c @@ -4,38 +4,42 @@ void lfrfid_scene_saved_info_on_enter(void* context) { LfRfid* app = context; Widget* widget = app->widget; - FuriString* tmp_string; - tmp_string = furi_string_alloc(); - - furi_string_printf( - tmp_string, - "%s [%s]\r\n", - furi_string_get_cstr(app->file_name), - protocol_dict_get_name(app->dict, app->protocol_id)); - - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - uint8_t* data = (uint8_t*)malloc(size); - protocol_dict_get_data(app->dict, app->protocol_id, data, size); - for(uint8_t i = 0; i < size; i++) { - if(i != 0) { - furi_string_cat_printf(tmp_string, " "); - } - - furi_string_cat_printf(tmp_string, "%02X", data[i]); + FuriString* display_text = furi_string_alloc(); + + furi_string_printf(display_text, "Name: %s\n", furi_string_get_cstr(app->file_name)); + + const char* protocol = protocol_dict_get_name(app->dict, app->protocol_id); + const char* manufacturer = protocol_dict_get_manufacturer(app->dict, app->protocol_id); + + if(strcasecmp(protocol, manufacturer) != 0 && strcasecmp(manufacturer, "N/A") != 0) { + furi_string_cat_printf(display_text, "\e#%s %s", manufacturer, protocol); + } else { + furi_string_cat_printf(display_text, "\e#%s", protocol); } + + furi_string_cat(display_text, "\nHex: "); + + const size_t data_size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = malloc(data_size); + + protocol_dict_get_data(app->dict, app->protocol_id, data, data_size); + + for(size_t i = 0; i < data_size; i++) { + furi_string_cat_printf(display_text, "%s%02X", i != 0 ? " " : "", data[i]); + } + free(data); - FuriString* render_data; - render_data = furi_string_alloc(); - protocol_dict_render_data(app->dict, render_data, app->protocol_id); - furi_string_cat_printf(tmp_string, "\r\n%s", furi_string_get_cstr(render_data)); - furi_string_free(render_data); + FuriString* rendered_data; + rendered_data = furi_string_alloc(); + protocol_dict_render_data(app->dict, rendered_data, app->protocol_id); + furi_string_cat_printf(display_text, "\n%s", furi_string_get_cstr(rendered_data)); + furi_string_free(rendered_data); - widget_add_string_multiline_element( - widget, 0, 1, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string)); + widget_add_text_scroll_element(widget, 0, 0, 128, 64, furi_string_get_cstr(display_text)); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); - furi_string_free(tmp_string); + furi_string_free(display_text); } bool lfrfid_scene_saved_info_on_event(void* context, SceneManagerEvent event) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c index 206074e9b0..f687cd0a29 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -59,6 +59,9 @@ bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event consumed = true; } scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu, event.event); + + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu, 0); } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_select_key.c b/applications/main/lfrfid/scenes/lfrfid_scene_select_key.c index 2a9cc1c634..505985c26a 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_select_key.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_select_key.c @@ -6,7 +6,9 @@ void lfrfid_scene_select_key_on_enter(void* context) { if(lfrfid_load_key_from_file_select(app)) { scene_manager_next_scene(app->scene_manager, LfRfidSceneSavedKeyMenu); } else { - scene_manager_previous_scene(app->scene_manager); + // Always select "Saved" menu item when returning from this scene + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexSaved); + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, LfRfidSceneStart); } } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c index 8a01fc707b..5d0e6112a6 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -1,13 +1,6 @@ #include "../lfrfid_i.h" #include -typedef enum { - SubmenuIndexRead, - SubmenuIndexSaved, - SubmenuIndexAddManually, - SubmenuIndexExtraActions, -} SubmenuIndex; - static void lfrfid_scene_start_submenu_callback(void* context, uint32_t index) { LfRfid* app = context; @@ -18,15 +11,20 @@ void lfrfid_scene_start_on_enter(void* context) { LfRfid* app = context; Submenu* submenu = app->submenu; - submenu_add_item(submenu, "Read", SubmenuIndexRead, lfrfid_scene_start_submenu_callback, app); submenu_add_item( - submenu, "Saved", SubmenuIndexSaved, lfrfid_scene_start_submenu_callback, app); + submenu, "Read", LfRfidMenuIndexRead, lfrfid_scene_start_submenu_callback, app); submenu_add_item( - submenu, "Add Manually", SubmenuIndexAddManually, lfrfid_scene_start_submenu_callback, app); + submenu, "Saved", LfRfidMenuIndexSaved, lfrfid_scene_start_submenu_callback, app); + submenu_add_item( + submenu, + "Add Manually", + LfRfidMenuIndexAddManually, + lfrfid_scene_start_submenu_callback, + app); submenu_add_item( submenu, "Extra Actions", - SubmenuIndexExtraActions, + LfRfidMenuIndexExtraActions, lfrfid_scene_start_submenu_callback, app); @@ -46,26 +44,28 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexRead) { - scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); + if(event.event == LfRfidMenuIndexRead) { + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); dolphin_deed(DolphinDeedRfidRead); consumed = true; - } else if(event.event == SubmenuIndexSaved) { + } else if(event.event == LfRfidMenuIndexSaved) { // Like in the other apps, explicitly save the scene state // in each branch in case the user cancels loading a file. - scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexSaved); + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexSaved); furi_string_set(app->file_path, LFRFID_APP_FOLDER); scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey); consumed = true; - } else if(event.event == SubmenuIndexAddManually) { + } else if(event.event == LfRfidMenuIndexAddManually) { scene_manager_set_scene_state( - app->scene_manager, LfRfidSceneStart, SubmenuIndexAddManually); + app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexAddManually); scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType); consumed = true; - } else if(event.event == SubmenuIndexExtraActions) { + } else if(event.event == LfRfidMenuIndexExtraActions) { scene_manager_set_scene_state( - app->scene_manager, LfRfidSceneStart, SubmenuIndexExtraActions); + app->scene_manager, LfRfidSceneStart, LfRfidMenuIndexExtraActions); scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions); consumed = true; } diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write.c b/applications/main/lfrfid/scenes/lfrfid_scene_write.c index f6e762e4d8..55973d09d7 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write.c @@ -21,19 +21,19 @@ void lfrfid_scene_write_on_enter(void* context) { LfRfid* app = context; Popup* popup = app->popup; - popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 8, &I_NFC_manual_60x50); + popup_set_header(popup, "Writing", 94, 16, AlignCenter, AlignTop); + if(!furi_string_empty(app->file_name)) { - popup_set_text(popup, furi_string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); + popup_set_text(popup, furi_string_get_cstr(app->file_name), 94, 29, AlignCenter, AlignTop); } else { - popup_set_text( - popup, - protocol_dict_get_name(app->dict, app->protocol_id), - 89, - 43, - AlignCenter, - AlignTop); + snprintf( + app->text_store, + LFRFID_TEXT_STORE_SIZE, + "Unsaved\n%s", + protocol_dict_get_name(app->dict, app->protocol_id)); + popup_set_text(popup, app->text_store, 94, 29, AlignCenter, AlignTop); } - popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); @@ -66,12 +66,14 @@ bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) { (event.event == LfRfidEventWriteFobCannotBeWritten) || (event.event == LfRfidEventWriteTooLongToWrite)) { popup_set_icon(popup, 83, 22, &I_WarningDolphinFlip_45x42); - popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop); + popup_set_header(popup, "Still Trying to Write...", 64, 0, AlignCenter, AlignTop); popup_set_text( popup, - "Make sure this\ncard is writable\nand not\nprotected.", - 3, - 17, + "Make sure this\n" + "card is writable\n" + "and not protected", + 0, + 13, AlignLeft, AlignTop); notification_message(app->notifications, &sequence_blink_start_yellow); diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c index 78ba481370..cc407bdcc2 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c @@ -19,12 +19,12 @@ bool lfrfid_scene_write_success_on_event(void* context, SceneManagerEvent event) LfRfid* app = context; bool consumed = false; - const uint32_t prev_scenes[] = {LfRfidSceneReadKeyMenu, LfRfidSceneSelectKey}; - - if((event.type == SceneManagerEventTypeBack) || - ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) { - scene_manager_search_and_switch_to_previous_scene_one_of( - app->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); + if(event.type == SceneManagerEventTypeBack || event.type == SceneManagerEventTypeCustom) { + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, LfRfidSceneReadKeyMenu)) { + scene_manager_search_and_switch_to_another_scene( + app->scene_manager, LfRfidSceneSelectKey); + } consumed = true; } diff --git a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c index 6764f99a3d..0b5cb08d86 100644 --- a/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c +++ b/applications/main/nfc/helpers/protocol_support/st25tb/st25tb_render.c @@ -13,15 +13,16 @@ void nfc_render_st25tb_info( } if(format_type == NfcProtocolFormatTypeFull) { - furi_string_cat_printf(str, "\nSys. OTP: %08lX", __bswap32(data->system_otp_block)); + furi_string_cat_printf( + str, "\nSys. OTP: %08lX", (uint32_t)__bswap32(data->system_otp_block)); furi_string_cat_printf(str, "\n::::::::::::::::::::::[Blocks]::::::::::::::::::::::"); for(size_t i = 0; i < st25tb_get_block_count(data->type); i += 2) { furi_string_cat_printf( str, "\n %02X %08lX %08lX", i, - __bswap32(data->blocks[i]), - __bswap32(data->blocks[i + 1])); + (uint32_t)__bswap32(data->blocks[i]), + (uint32_t)__bswap32(data->blocks[i + 1])); } } } diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index d488cbfc6e..06b63fd902 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -746,10 +746,9 @@ void elements_text_box( } line[line_num].y = total_height_min; line_num++; - if(text[i + 1]) { + if(!full_text_processed) { line[line_num].text = &text[i + 1]; } - line_leading_min = font_params->leading_min; line_height = font_params->height; line_descender = font_params->descender; @@ -792,29 +791,32 @@ void elements_text_box( for(size_t i = 0; i < line_num; i++) { for(size_t j = 0; j < line[i].len; j++) { // Process format symbols - if(line[i].text[j] == ELEMENTS_BOLD_MARKER) { - if(bold) { - current_font = FontSecondary; - } else { - current_font = FontPrimary; + if(line[i].text[j] == '\e' && j < line[i].len - 1) { + ++j; + if(line[i].text[j] == ELEMENTS_BOLD_MARKER) { + if(bold) { + current_font = FontSecondary; + } else { + current_font = FontPrimary; + } + canvas_set_font(canvas, current_font); + bold = !bold; + continue; } - canvas_set_font(canvas, current_font); - bold = !bold; - continue; - } - if(line[i].text[j] == ELEMENTS_MONO_MARKER) { - if(mono) { - current_font = FontSecondary; - } else { - current_font = FontKeyboard; + if(line[i].text[j] == ELEMENTS_MONO_MARKER) { + if(mono) { + current_font = FontSecondary; + } else { + current_font = FontKeyboard; + } + canvas_set_font(canvas, current_font); + mono = !mono; + continue; + } + if(line[i].text[j] == ELEMENTS_INVERSE_MARKER) { + inverse = !inverse; + continue; } - canvas_set_font(canvas, current_font); - mono = !mono; - continue; - } - if(line[i].text[j] == ELEMENTS_INVERSE_MARKER) { - inverse = !inverse; - continue; } if(inverse) { canvas_draw_box( diff --git a/lib/lfrfid/protocols/protocol_awid.c b/lib/lfrfid/protocols/protocol_awid.c index 914f2f01b7..b3e92245e4 100644 --- a/lib/lfrfid/protocols/protocol_awid.c +++ b/lib/lfrfid/protocols/protocol_awid.c @@ -164,7 +164,8 @@ void protocol_awid_render_data(ProtocolAwid* protocol, FuriString* result) { uint8_t* decoded_data = protocol->data; uint8_t format_length = decoded_data[0]; - furi_string_cat_printf(result, "Format: %d\r\n", format_length); + furi_string_printf(result, "Format: %hhu\n", format_length); + if(format_length == 26) { uint8_t facility; bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9); @@ -172,13 +173,17 @@ void protocol_awid_render_data(ProtocolAwid* protocol, FuriString* result) { uint16_t card_id; bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17); bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25); - furi_string_cat_printf(result, "Facility: %d\r\n", facility); - furi_string_cat_printf(result, "Card: %d", card_id); + furi_string_cat_printf( + result, + "FC: %hhu\n" + "Card: %hu", + facility, + card_id); } else { // print 66 bits as hex furi_string_cat_printf(result, "Data: "); for(size_t i = 0; i < AWID_DECODED_DATA_SIZE; i++) { - furi_string_cat_printf(result, "%02X", decoded_data[i]); + furi_string_cat_printf(result, "%02hhX", decoded_data[i]); } } }; @@ -187,7 +192,8 @@ void protocol_awid_render_brief_data(ProtocolAwid* protocol, FuriString* result) uint8_t* decoded_data = protocol->data; uint8_t format_length = decoded_data[0]; - furi_string_cat_printf(result, "Format: %d\r\n", format_length); + furi_string_printf(result, "Format: %hhu", format_length); + if(format_length == 26) { uint8_t facility; bit_lib_copy_bits(&facility, 0, 8, decoded_data, 9); @@ -195,9 +201,14 @@ void protocol_awid_render_brief_data(ProtocolAwid* protocol, FuriString* result) uint16_t card_id; bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 17); bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 25); - furi_string_cat_printf(result, "ID: %03u,%05u", facility, card_id); + furi_string_cat_printf( + result, + "; FC: %hhu\n" + "Card: %hu", + facility, + card_id); } else { - furi_string_cat_printf(result, "Data: unknown"); + furi_string_cat(result, "\nData: Unknown"); } }; @@ -252,4 +263,4 @@ const ProtocolBase protocol_awid = { .render_data = (ProtocolRenderData)protocol_awid_render_data, .render_brief_data = (ProtocolRenderData)protocol_awid_render_brief_data, .write_data = (ProtocolWriteData)protocol_awid_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_em4100.c b/lib/lfrfid/protocols/protocol_em4100.c index 38452007f3..055d06d868 100644 --- a/lib/lfrfid/protocols/protocol_em4100.c +++ b/lib/lfrfid/protocols/protocol_em4100.c @@ -330,7 +330,8 @@ void protocol_em4100_render_data(ProtocolEM4100* protocol, FuriString* result) { uint8_t* data = protocol->data; furi_string_printf( result, - "FC: %03u, Card: %05u (RF/%u)", + "FC: %03u\n" + "Card: %05hu (RF/%hhu)", data[2], (uint16_t)((data[3] << 8) | (data[4])), protocol->clock_per_bit); diff --git a/lib/lfrfid/protocols/protocol_fdx_a.c b/lib/lfrfid/protocols/protocol_fdx_a.c index 667c4f88db..f7852eb101 100644 --- a/lib/lfrfid/protocols/protocol_fdx_a.c +++ b/lib/lfrfid/protocols/protocol_fdx_a.c @@ -217,14 +217,10 @@ void protocol_fdx_a_render_data(ProtocolFDXA* protocol, FuriString* result) { furi_string_printf( result, - "ID: %02X%02X%02X%02X%02X\r\n" - "Parity: %s", - data[0], - data[1], - data[2], - data[3], - data[4], - parity_sum == 0 ? "+" : "-"); + "ID: %010llX\n" + "Parity: %c", + bit_lib_get_bits_64(data, 0, 40), + parity_sum == 0 ? '+' : '-'); }; const ProtocolBase protocol_fdx_a = { @@ -249,4 +245,4 @@ const ProtocolBase protocol_fdx_a = { .render_data = (ProtocolRenderData)protocol_fdx_a_render_data, .render_brief_data = (ProtocolRenderData)protocol_fdx_a_render_data, .write_data = (ProtocolWriteData)protocol_fdx_a_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c index 18ccfa7029..a5af855e48 100644 --- a/lib/lfrfid/protocols/protocol_fdx_b.c +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -288,21 +288,33 @@ void protocol_fdx_b_render_data(ProtocolFDXB* protocol, FuriString* result) { uint8_t replacement_number = bit_lib_get_bits(protocol->data, 60, 3); bool animal_flag = bit_lib_get_bit(protocol->data, 63); - furi_string_printf(result, "ID: %03u-%012llu\r\n", country_code, national_code); - furi_string_cat_printf(result, "Animal: %s, ", animal_flag ? "Yes" : "No"); + furi_string_printf( + result, + "ID: %03hu-%012llu\n" + "Country Code: %hu\n" + "Temperature: ", + country_code, + national_code, + country_code); float temperature; if(protocol_fdx_b_get_temp(protocol->data, &temperature)) { - float temperature_c = (temperature - 32) / 1.8; - furi_string_cat_printf( - result, "T: %.2fF, %.2fC\r\n", (double)temperature, (double)temperature_c); + if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { + float temperature_c = (temperature - 32.0f) / 1.8f; + furi_string_cat_printf(result, "%.2fC", (double)temperature_c); + } else { + furi_string_cat_printf(result, "%.2fF", (double)temperature); + } } else { - furi_string_cat_printf(result, "T: ---\r\n"); + furi_string_cat(result, "---"); } furi_string_cat_printf( result, - "Bits: %X-%X-%X-%X-%X", + "\n" + "Animal: %s\n" + "Bits: %hhX-%hhX-%hhX-%hhX-%hhX", + animal_flag ? "Yes" : "No", block_status, rudi_bit, reserved, @@ -317,21 +329,24 @@ void protocol_fdx_b_render_brief_data(ProtocolFDXB* protocol, FuriString* result // 10 bit of country code uint16_t country_code = protocol_fdx_b_get_country_code(protocol->data); - bool animal_flag = bit_lib_get_bit(protocol->data, 63); - - furi_string_printf(result, "ID: %03u-%012llu\r\n", country_code, national_code); - furi_string_cat_printf(result, "Animal: %s, ", animal_flag ? "Yes" : "No"); + furi_string_printf( + result, + "ID: %03hu-%012llu\n" + "Country: %hu; Temp.: ", + country_code, + national_code, + country_code); float temperature; if(protocol_fdx_b_get_temp(protocol->data, &temperature)) { if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { float temperature_c = (temperature - 32.0f) / 1.8f; - furi_string_cat_printf(result, "T: %.2fC", (double)temperature_c); + furi_string_cat_printf(result, "%.2fC", (double)temperature_c); } else { - furi_string_cat_printf(result, "T: %.2fF", (double)temperature); + furi_string_cat_printf(result, "%.2fF", (double)temperature); } } else { - furi_string_cat_printf(result, "T: ---"); + furi_string_cat(result, "---"); } }; @@ -380,4 +395,4 @@ const ProtocolBase protocol_fdx_b = { .render_data = (ProtocolRenderData)protocol_fdx_b_render_data, .render_brief_data = (ProtocolRenderData)protocol_fdx_b_render_brief_data, .write_data = (ProtocolWriteData)protocol_fdx_b_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_gallagher.c b/lib/lfrfid/protocols/protocol_gallagher.c index 15bebb90bd..b00c14acd1 100644 --- a/lib/lfrfid/protocols/protocol_gallagher.c +++ b/lib/lfrfid/protocols/protocol_gallagher.c @@ -268,17 +268,44 @@ bool protocol_gallagher_write_data(ProtocolGallagher* protocol, void* data) { return result; }; -void protocol_gallagher_render_data(ProtocolGallagher* protocol, FuriString* result) { - UNUSED(protocol); - uint8_t rc = bit_lib_get_bits(protocol->data, 0, 4); - uint8_t il = bit_lib_get_bits(protocol->data, 4, 4); +static void protocol_gallagher_render_data_internal( + ProtocolGallagher* protocol, + FuriString* result, + bool brief) { + uint8_t region = bit_lib_get_bits(protocol->data, 0, 4); + uint8_t issue_level = bit_lib_get_bits(protocol->data, 4, 4); uint32_t fc = bit_lib_get_bits_32(protocol->data, 8, 24); uint32_t card_id = bit_lib_get_bits_32(protocol->data, 32, 32); - furi_string_cat_printf(result, "Region: %u, Issue Level: %u\r\n", rc, il); - furi_string_cat_printf(result, "FC: %lu, C: %lu\r\n", fc, card_id); + if(brief) { + furi_string_printf( + result, + "FC: %lu\n" + "Card: %lu", + fc, + card_id); + } else { + furi_string_printf( + result, + "FC: %lu\n" + "Card: %lu\n" + "Region: %u\n" + "Issue Level: %u", + fc, + card_id, + region, + issue_level); + } }; +void protocol_gallagher_render_data(ProtocolGallagher* protocol, FuriString* result) { + protocol_gallagher_render_data_internal(protocol, result, false); +} + +void protocol_gallagher_render_brief_data(ProtocolGallagher* protocol, FuriString* result) { + protocol_gallagher_render_data_internal(protocol, result, true); +} + const ProtocolBase protocol_gallagher = { .name = "Gallagher", .manufacturer = "Gallagher", @@ -299,6 +326,6 @@ const ProtocolBase protocol_gallagher = { .yield = (ProtocolEncoderYield)protocol_gallagher_encoder_yield, }, .render_data = (ProtocolRenderData)protocol_gallagher_render_data, - .render_brief_data = (ProtocolRenderData)protocol_gallagher_render_data, + .render_brief_data = (ProtocolRenderData)protocol_gallagher_render_brief_data, .write_data = (ProtocolWriteData)protocol_gallagher_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_h10301.c b/lib/lfrfid/protocols/protocol_h10301.c index 2d7a3e669e..6c6d5771ac 100644 --- a/lib/lfrfid/protocols/protocol_h10301.c +++ b/lib/lfrfid/protocols/protocol_h10301.c @@ -359,8 +359,8 @@ void protocol_h10301_render_data(ProtocolH10301* protocol, FuriString* result) { uint8_t* data = protocol->data; furi_string_printf( result, - "FC: %u\r\n" - "Card: %u", + "FC: %hhu\n" + "Card: %hu", data[0], (uint16_t)((data[1] << 8) | (data[2]))); }; @@ -387,4 +387,4 @@ const ProtocolBase protocol_h10301 = { .render_data = (ProtocolRenderData)protocol_h10301_render_data, .render_brief_data = (ProtocolRenderData)protocol_h10301_render_data, .write_data = (ProtocolWriteData)protocol_h10301_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_hid_ex_generic.c b/lib/lfrfid/protocols/protocol_hid_ex_generic.c index 194e153289..efc7a5190c 100644 --- a/lib/lfrfid/protocols/protocol_hid_ex_generic.c +++ b/lib/lfrfid/protocols/protocol_hid_ex_generic.c @@ -193,9 +193,13 @@ bool protocol_hid_ex_generic_write_data(ProtocolHIDEx* protocol, void* data) { }; void protocol_hid_ex_generic_render_data(ProtocolHIDEx* protocol, FuriString* result) { - // TODO FL-3518: parser and render functions UNUSED(protocol); - furi_string_printf(result, "Generic HID Extended\r\nData: Unknown"); + + // TODO FL-3518: parser and render functions + furi_string_set( + result, + "Type: Generic HID Extended\n" + "Data: Unknown"); }; const ProtocolBase protocol_hid_ex_generic = { @@ -220,4 +224,4 @@ const ProtocolBase protocol_hid_ex_generic = { .render_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data, .render_brief_data = (ProtocolRenderData)protocol_hid_ex_generic_render_data, .write_data = (ProtocolWriteData)protocol_hid_ex_generic_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_hid_generic.c b/lib/lfrfid/protocols/protocol_hid_generic.c index e24b5ade71..1a95288855 100644 --- a/lib/lfrfid/protocols/protocol_hid_generic.c +++ b/lib/lfrfid/protocols/protocol_hid_generic.c @@ -245,7 +245,7 @@ void protocol_hid_generic_render_data(ProtocolHID* protocol, FuriString* result) if(protocol_size == HID_PROTOCOL_SIZE_UNKNOWN) { furi_string_printf( result, - "Generic HID Proximity\r\n" + "Generic HID Proximity\n" "Data: %02X%02X%02X%02X%02X%X", protocol->data[0], protocol->data[1], @@ -256,7 +256,7 @@ void protocol_hid_generic_render_data(ProtocolHID* protocol, FuriString* result) } else { furi_string_printf( result, - "%hhu-bit HID Proximity\r\n" + "%hhu-bit HID Proximity\n" "Data: ", protocol_size); protocol_hid_generic_string_cat_protocol_bits(protocol, protocol_size, result); diff --git a/lib/lfrfid/protocols/protocol_idteck.c b/lib/lfrfid/protocols/protocol_idteck.c index 6de8de2060..2075576bae 100644 --- a/lib/lfrfid/protocols/protocol_idteck.c +++ b/lib/lfrfid/protocols/protocol_idteck.c @@ -205,26 +205,16 @@ static uint32_t get_card(const uint8_t* data) { return cn; } -void protocol_idteck_render_data_internal(ProtocolIdteck* protocol, FuriString* result, bool brief) { +void protocol_idteck_render_data(ProtocolIdteck* protocol, FuriString* result) { const uint32_t fc = get_fc(protocol->data); const uint32_t card = get_card(protocol->data); - if(brief) { - furi_string_printf(result, "FC: %08lX\r\nCard: %08lX", fc, card); - } else { - furi_string_printf( - result, - "FC: %08lX\r\n" - "Card: %08lX\r\n", - fc, - card); - } -} -void protocol_idteck_render_data(ProtocolIdteck* protocol, FuriString* result) { - protocol_idteck_render_data_internal(protocol, result, false); -} -void protocol_idteck_render_brief_data(ProtocolIdteck* protocol, FuriString* result) { - protocol_idteck_render_data_internal(protocol, result, true); + furi_string_printf( + result, + "FC: %08lX\n" + "Card: %08lX", + fc, + card); } bool protocol_idteck_write_data(ProtocolIdteck* protocol, void* data) { @@ -264,6 +254,6 @@ const ProtocolBase protocol_idteck = { .yield = (ProtocolEncoderYield)protocol_idteck_encoder_yield, }, .render_data = (ProtocolRenderData)protocol_idteck_render_data, - .render_brief_data = (ProtocolRenderData)protocol_idteck_render_brief_data, + .render_brief_data = (ProtocolRenderData)protocol_idteck_render_data, .write_data = (ProtocolWriteData)protocol_idteck_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_indala26.c b/lib/lfrfid/protocols/protocol_indala26.c index 7ce83af7fa..893361abb2 100644 --- a/lib/lfrfid/protocols/protocol_indala26.c +++ b/lib/lfrfid/protocols/protocol_indala26.c @@ -289,22 +289,21 @@ void protocol_indala26_render_data_internal( if(brief) { furi_string_printf( result, - "FC: %u\r\nCard: %u, Parity:%s%s", + "FC: %u\n" + "Card: %u", fc, - card, - (checksum_correct ? "+" : "-"), - (wiegand_correct ? "+" : "-")); + card); } else { furi_string_printf( result, - "FC: %u\r\n" - "Card: %u\r\n" - "Checksum: %s\r\n" - "W26 Parity: %s", + "FC: %u\n" + "Card: %u\n" + "Parity: %c\n" + "Checksum: %c", fc, card, - (checksum_correct ? "+" : "-"), - (wiegand_correct ? "+" : "-")); + (wiegand_correct ? '+' : '-'), + (checksum_correct ? '+' : '-')); } } void protocol_indala26_render_data(ProtocolIndala* protocol, FuriString* result) { diff --git a/lib/lfrfid/protocols/protocol_io_prox_xsf.c b/lib/lfrfid/protocols/protocol_io_prox_xsf.c index 5cdc9064c7..b41cb66f3d 100644 --- a/lib/lfrfid/protocols/protocol_io_prox_xsf.c +++ b/lib/lfrfid/protocols/protocol_io_prox_xsf.c @@ -236,9 +236,9 @@ void protocol_io_prox_xsf_render_data(ProtocolIOProxXSF* protocol, FuriString* r uint8_t* data = protocol->data; furi_string_printf( result, - "FC: %u\r\n" - "VС: %u\r\n" - "Card: %u", + "FC: %hhu\n" + "V: %hhu\n" + "Card: %hu", data[0], data[1], (uint16_t)((data[2] << 8) | (data[3]))); @@ -248,8 +248,8 @@ void protocol_io_prox_xsf_render_brief_data(ProtocolIOProxXSF* protocol, FuriStr uint8_t* data = protocol->data; furi_string_printf( result, - "FC: %u, VС: %u\r\n" - "Card: %u", + "FC: %hhu, V: %hhu\n" + "Card: %hu", data[0], data[1], (uint16_t)((data[2] << 8) | (data[3]))); @@ -298,4 +298,4 @@ const ProtocolBase protocol_io_prox_xsf = { .render_data = (ProtocolRenderData)protocol_io_prox_xsf_render_data, .render_brief_data = (ProtocolRenderData)protocol_io_prox_xsf_render_brief_data, .write_data = (ProtocolWriteData)protocol_io_prox_xsf_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_jablotron.c b/lib/lfrfid/protocols/protocol_jablotron.c index 467002c7e0..c2cbb41744 100644 --- a/lib/lfrfid/protocols/protocol_jablotron.c +++ b/lib/lfrfid/protocols/protocol_jablotron.c @@ -162,7 +162,7 @@ LevelDuration protocol_jablotron_encoder_yield(ProtocolJablotron* protocol) { void protocol_jablotron_render_data(ProtocolJablotron* protocol, FuriString* result) { uint64_t id = protocol_jablotron_card_id(protocol->data); - furi_string_printf(result, "ID: %llX\r\n", id); + furi_string_printf(result, "Card: %llX", id); }; bool protocol_jablotron_write_data(ProtocolJablotron* protocol, void* data) { @@ -208,4 +208,4 @@ const ProtocolBase protocol_jablotron = { .render_data = (ProtocolRenderData)protocol_jablotron_render_data, .render_brief_data = (ProtocolRenderData)protocol_jablotron_render_data, .write_data = (ProtocolWriteData)protocol_jablotron_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_keri.c b/lib/lfrfid/protocols/protocol_keri.c index d994229d99..2cc7e8cdfc 100644 --- a/lib/lfrfid/protocols/protocol_keri.c +++ b/lib/lfrfid/protocols/protocol_keri.c @@ -212,13 +212,40 @@ LevelDuration protocol_keri_encoder_yield(ProtocolKeri* protocol) { return level_duration; }; -void protocol_keri_render_data(ProtocolKeri* protocol, FuriString* result) { +static void + protocol_keri_render_data_internal(ProtocolKeri* protocol, FuriString* result, bool brief) { uint32_t data = bit_lib_get_bits_32(protocol->data, 0, 32); uint32_t internal_id = data & 0x7FFFFFFF; uint32_t fc = 0; uint32_t cn = 0; protocol_keri_descramble(&fc, &cn, &data); - furi_string_printf(result, "Internal ID: %lu\r\nFC: %lu, Card: %lu\r\n", internal_id, fc, cn); + + if(brief) { + furi_string_printf( + result, + "Internal ID: %lu\n" + "FC: %lu; Card: %lu", + internal_id, + fc, + cn); + } else { + furi_string_printf( + result, + "Internal ID: %lu\n" + "FC: %lu\n" + "Card: %lu", + internal_id, + fc, + cn); + } +} + +void protocol_keri_render_data(ProtocolKeri* protocol, FuriString* result) { + protocol_keri_render_data_internal(protocol, result, false); +} + +void protocol_keri_render_brief_data(ProtocolKeri* protocol, FuriString* result) { + protocol_keri_render_data_internal(protocol, result, true); } bool protocol_keri_write_data(ProtocolKeri* protocol, void* data) { @@ -262,6 +289,6 @@ const ProtocolBase protocol_keri = { .yield = (ProtocolEncoderYield)protocol_keri_encoder_yield, }, .render_data = (ProtocolRenderData)protocol_keri_render_data, - .render_brief_data = (ProtocolRenderData)protocol_keri_render_data, + .render_brief_data = (ProtocolRenderData)protocol_keri_render_brief_data, .write_data = (ProtocolWriteData)protocol_keri_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_nexwatch.c b/lib/lfrfid/protocols/protocol_nexwatch.c index 938ef273d0..3df83be7ac 100644 --- a/lib/lfrfid/protocols/protocol_nexwatch.c +++ b/lib/lfrfid/protocols/protocol_nexwatch.c @@ -263,7 +263,10 @@ LevelDuration protocol_nexwatch_encoder_yield(ProtocolNexwatch* protocol) { return level_duration; }; -void protocol_nexwatch_render_data(ProtocolNexwatch* protocol, FuriString* result) { +static void protocol_nexwatch_render_data_internal( + ProtocolNexwatch* protocol, + FuriString* result, + bool brief) { uint32_t id = 0; uint32_t scrambled = bit_lib_get_bits_32(protocol->data, 8, 32); protocol_nexwatch_descramble(&id, &scrambled); @@ -272,13 +275,42 @@ void protocol_nexwatch_render_data(ProtocolNexwatch* protocol, FuriString* resul uint8_t mode = bit_lib_get_bits(protocol->data, 40, 4); uint8_t parity = bit_lib_get_bits(protocol->data, 44, 4); uint8_t chk = bit_lib_get_bits(protocol->data, 48, 8); - for(m_idx = 0; m_idx < 3; m_idx++) { + + for(m_idx = 0; m_idx < COUNT_OF(magic_items); m_idx++) { magic_items[m_idx].chk = protocol_nexwatch_checksum(magic_items[m_idx].magic, id, parity); if(magic_items[m_idx].chk == chk) { break; } } - furi_string_printf(result, "ID: %lu, M:%u\r\nType: %s\r\n", id, mode, magic_items[m_idx].desc); + + const char* type = m_idx < COUNT_OF(magic_items) ? magic_items[m_idx].desc : "Unknown"; + + if(brief) { + furi_string_printf( + result, + "ID: %lu\n" + "Mode: %hhu; Type: %s", + id, + mode, + type); + } else { + furi_string_printf( + result, + "ID: %lu\n" + "Mode: %hhu\n" + "Type: %s", + id, + mode, + type); + } +} + +void protocol_nexwatch_render_data(ProtocolNexwatch* protocol, FuriString* result) { + protocol_nexwatch_render_data_internal(protocol, result, false); +} + +void protocol_nexwatch_render_brief_data(ProtocolNexwatch* protocol, FuriString* result) { + protocol_nexwatch_render_data_internal(protocol, result, true); } bool protocol_nexwatch_write_data(ProtocolNexwatch* protocol, void* data) { @@ -318,6 +350,6 @@ const ProtocolBase protocol_nexwatch = { .yield = (ProtocolEncoderYield)protocol_nexwatch_encoder_yield, }, .render_data = (ProtocolRenderData)protocol_nexwatch_render_data, - .render_brief_data = (ProtocolRenderData)protocol_nexwatch_render_data, + .render_brief_data = (ProtocolRenderData)protocol_nexwatch_render_brief_data, .write_data = (ProtocolWriteData)protocol_nexwatch_write_data, }; diff --git a/lib/lfrfid/protocols/protocol_pac_stanley.c b/lib/lfrfid/protocols/protocol_pac_stanley.c index dc9eaaf493..67bc3bf48b 100644 --- a/lib/lfrfid/protocols/protocol_pac_stanley.c +++ b/lib/lfrfid/protocols/protocol_pac_stanley.c @@ -202,8 +202,7 @@ bool protocol_pac_stanley_write_data(ProtocolPACStanley* protocol, void* data) { } void protocol_pac_stanley_render_data(ProtocolPACStanley* protocol, FuriString* result) { - uint8_t* data = protocol->data; - furi_string_printf(result, "CIN: %02X%02X%02X%02X", data[0], data[1], data[2], data[3]); + furi_string_printf(result, "CIN: %08lX", bit_lib_get_bits_32(protocol->data, 0, 32)); } const ProtocolBase protocol_pac_stanley = { diff --git a/lib/lfrfid/protocols/protocol_paradox.c b/lib/lfrfid/protocols/protocol_paradox.c index b716acf7a3..bb52f0529c 100644 --- a/lib/lfrfid/protocols/protocol_paradox.c +++ b/lib/lfrfid/protocols/protocol_paradox.c @@ -171,10 +171,20 @@ void protocol_paradox_render_data(ProtocolParadox* protocol, FuriString* result) uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8); uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id); - furi_string_cat_printf(result, "Facility: %u\r\n", fc); - furi_string_cat_printf(result, "Card: %u\r\n", card_id); - furi_string_cat_printf(result, "CRC: %u Calc CRC: %u\r\n", card_crc, calc_crc); - if(card_crc != calc_crc) furi_string_cat_printf(result, "CRC Mismatch, Invalid Card!\r\n"); + furi_string_printf( + result, + "FC: %hhu\n" + "Card: %hu\n" + "CRC: %hhu\n" + "Calc CRC: %hhu", + fc, + card_id, + card_crc, + calc_crc); + + if(card_crc != calc_crc) { + furi_string_cat(result, "\nCRC Mismatch, Invalid Card!"); + } }; void protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* result) { @@ -185,11 +195,10 @@ void protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* r uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8); uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id); - furi_string_cat_printf(result, "FC: %03u, Card: %05u\r\n", fc, card_id); - if(calc_crc == card_crc) { - furi_string_cat_printf(result, "CRC : %03u", card_crc); - } else { - furi_string_cat_printf(result, "Card is Invalid!"); + furi_string_printf(result, "FC: %hhu; Card: %hu", fc, card_id); + + if(calc_crc != card_crc) { + furi_string_cat(result, "\nCRC Mismatch, Invalid Card!"); } }; @@ -237,4 +246,4 @@ const ProtocolBase protocol_paradox = { .render_data = (ProtocolRenderData)protocol_paradox_render_data, .render_brief_data = (ProtocolRenderData)protocol_paradox_render_brief_data, .write_data = (ProtocolWriteData)protocol_paradox_write_data, -}; \ No newline at end of file +}; diff --git a/lib/lfrfid/protocols/protocol_pyramid.c b/lib/lfrfid/protocols/protocol_pyramid.c index f0a506eb39..8711fe13a0 100644 --- a/lib/lfrfid/protocols/protocol_pyramid.c +++ b/lib/lfrfid/protocols/protocol_pyramid.c @@ -243,7 +243,7 @@ void protocol_pyramid_render_data(ProtocolPyramid* protocol, FuriString* result) uint8_t* decoded_data = protocol->data; uint8_t format_length = decoded_data[0]; - furi_string_cat_printf(result, "Format: %d\r\n", format_length); + furi_string_printf(result, "Format: %hhu\n", format_length); if(format_length == 26) { uint8_t facility; bit_lib_copy_bits(&facility, 0, 8, decoded_data, 8); @@ -251,9 +251,9 @@ void protocol_pyramid_render_data(ProtocolPyramid* protocol, FuriString* result) uint16_t card_id; bit_lib_copy_bits((uint8_t*)&card_id, 8, 8, decoded_data, 16); bit_lib_copy_bits((uint8_t*)&card_id, 0, 8, decoded_data, 24); - furi_string_cat_printf(result, "FC: %03u, Card: %05u", facility, card_id); + furi_string_cat_printf(result, "FC: %03hhu; Card: %05hu", facility, card_id); } else { - furi_string_cat_printf(result, "Data: unknown"); + furi_string_cat_printf(result, "Data: Unknown"); } }; diff --git a/lib/lfrfid/protocols/protocol_viking.c b/lib/lfrfid/protocols/protocol_viking.c index f5697012af..4989e4e681 100644 --- a/lib/lfrfid/protocols/protocol_viking.c +++ b/lib/lfrfid/protocols/protocol_viking.c @@ -176,8 +176,7 @@ bool protocol_viking_write_data(ProtocolViking* protocol, void* data) { }; void protocol_viking_render_data(ProtocolViking* protocol, FuriString* result) { - uint32_t id = bit_lib_get_bits_32(protocol->data, 0, 32); - furi_string_printf(result, "ID: %08lX\r\n", id); + furi_string_printf(result, "ID: %08lX", bit_lib_get_bits_32(protocol->data, 0, 32)); }; const ProtocolBase protocol_viking = { @@ -202,4 +201,4 @@ const ProtocolBase protocol_viking = { .render_data = (ProtocolRenderData)protocol_viking_render_data, .render_brief_data = (ProtocolRenderData)protocol_viking_render_data, .write_data = (ProtocolWriteData)protocol_viking_write_data, -}; \ No newline at end of file +}; diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 7a81367059..f6199445d1 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,60.3,, +Version,+,60.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2450,7 +2450,7 @@ Function,+,storage_simply_remove,_Bool,"Storage*, const char*" Function,+,storage_simply_remove_recursive,_Bool,"Storage*, const char*" Function,-,stpcpy,char*,"char*, const char*" Function,-,stpncpy,char*,"char*, const char*, size_t" -Function,-,strcasecmp,int,"const char*, const char*" +Function,+,strcasecmp,int,"const char*, const char*" Function,-,strcasecmp_l,int,"const char*, const char*, locale_t" Function,+,strcasestr,char*,"const char*, const char*" Function,-,strcat,char*,"char*, const char*" diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index bfc0507995..6e65b94719 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,60.3,, +Version,+,60.4,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -3058,7 +3058,7 @@ Function,+,storage_simply_remove,_Bool,"Storage*, const char*" Function,+,storage_simply_remove_recursive,_Bool,"Storage*, const char*" Function,-,stpcpy,char*,"char*, const char*" Function,-,stpncpy,char*,"char*, const char*, size_t" -Function,-,strcasecmp,int,"const char*, const char*" +Function,+,strcasecmp,int,"const char*, const char*" Function,-,strcasecmp_l,int,"const char*, const char*, locale_t" Function,+,strcasestr,char*,"const char*, const char*" Function,-,strcat,char*,"char*, const char*" From 3084469d83e3a2c3edf8f85c6707418026138476 Mon Sep 17 00:00:00 2001 From: Kuronons <110337784+Kuronons@users.noreply.github.com> Date: Fri, 29 Mar 2024 04:40:26 +0100 Subject: [PATCH 05/16] L1_Mods animation update : adding VGM visual (#3502) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update images * Update meta.txt * Reverting to MOD 9999 Co-authored-by: あく --- .../external/L1_Mods_128x64/frame_21.png | Bin 4357 -> 2648 bytes .../external/L1_Mods_128x64/frame_22.png | Bin 4320 -> 2733 bytes .../external/L1_Mods_128x64/frame_23.png | Bin 4332 -> 3084 bytes .../external/L1_Mods_128x64/frame_24.png | Bin 4284 -> 3236 bytes .../external/L1_Mods_128x64/frame_25.png | Bin 4149 -> 3298 bytes .../external/L1_Mods_128x64/frame_26.png | Bin 4260 -> 3363 bytes .../external/L1_Mods_128x64/frame_27.png | Bin 4376 -> 3423 bytes .../external/L1_Mods_128x64/frame_28.png | Bin 4393 -> 3371 bytes .../external/L1_Mods_128x64/frame_29.png | Bin 4380 -> 3106 bytes .../external/L1_Mods_128x64/frame_30.png | Bin 4390 -> 3051 bytes .../external/L1_Mods_128x64/frame_31.png | Bin 4383 -> 3072 bytes .../external/L1_Mods_128x64/frame_32.png | Bin 4402 -> 3000 bytes .../external/L1_Mods_128x64/frame_33.png | Bin 4340 -> 4149 bytes .../external/L1_Mods_128x64/frame_34.png | Bin 4253 -> 4260 bytes .../external/L1_Mods_128x64/frame_35.png | Bin 4342 -> 4376 bytes .../external/L1_Mods_128x64/frame_36.png | Bin 4315 -> 4393 bytes .../external/L1_Mods_128x64/frame_37.png | Bin 4267 -> 4380 bytes .../external/L1_Mods_128x64/frame_38.png | Bin 4301 -> 4390 bytes .../external/L1_Mods_128x64/frame_39.png | Bin 4326 -> 4383 bytes .../external/L1_Mods_128x64/frame_40.png | Bin 4313 -> 4402 bytes .../external/L1_Mods_128x64/frame_41.png | Bin 0 -> 4340 bytes .../external/L1_Mods_128x64/frame_42.png | Bin 0 -> 4253 bytes .../external/L1_Mods_128x64/frame_43.png | Bin 0 -> 4342 bytes .../external/L1_Mods_128x64/frame_44.png | Bin 0 -> 4315 bytes .../external/L1_Mods_128x64/frame_45.png | Bin 0 -> 4267 bytes .../external/L1_Mods_128x64/frame_46.png | Bin 0 -> 4301 bytes .../external/L1_Mods_128x64/frame_47.png | Bin 0 -> 4326 bytes .../external/L1_Mods_128x64/frame_48.png | Bin 0 -> 4313 bytes .../dolphin/external/L1_Mods_128x64/meta.txt | 6 +++--- 29 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 assets/dolphin/external/L1_Mods_128x64/frame_41.png create mode 100644 assets/dolphin/external/L1_Mods_128x64/frame_42.png create mode 100644 assets/dolphin/external/L1_Mods_128x64/frame_43.png create mode 100644 assets/dolphin/external/L1_Mods_128x64/frame_44.png create mode 100644 assets/dolphin/external/L1_Mods_128x64/frame_45.png create mode 100644 assets/dolphin/external/L1_Mods_128x64/frame_46.png create mode 100644 assets/dolphin/external/L1_Mods_128x64/frame_47.png create mode 100644 assets/dolphin/external/L1_Mods_128x64/frame_48.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_21.png b/assets/dolphin/external/L1_Mods_128x64/frame_21.png index 900cc7d12099f0141c1e0c27cd29025fb1645f3b..00a7a6e99c02f28a51350e2ebd079919b2b17a1f 100644 GIT binary patch literal 2648 zcmcImdsGuw8ovRfAc2aCT6@+`qs972GLv^w0)&J|#83n(Xtg+*Okhfq2}y)N1%Y<8 zP_ZbKQWsX()wTFqt$NxmJ!{m~)vm9K0!7`jf(HQ+MB0M#*c%|!?rB~B**WvLbLYFi z@ArM*@7_Cwu`!Xp)8|hI0QhR6)N$~c2d_{s4|rdH{O&98;cbapV*|ivmg{1GU6u0y zn3_W+B-xX+E9C@j;^KN*M{*q|3$z9xD8ymGi8RuV=*SextYG)lon#}FUcpWjX;H03 zMW#|w*;X<>J0^k1P9tP`c1SQ1@R5mw4ZT_Q26>!1 zV!{`EQ?OI*c8i?H%goH=W(v5pHHC-CWHKJg=kfU*Xu+{%neDiPW40}HBdAFmVWli~ ziZ&xIL|jL2v@6(<>39ex%OtGXHjyTnFrEXq@Gvgw3dwD#)jr+SWSX?L*~2rSmkGYV z3T#WrvXHzu(nfEz5@dJ=X|^wPJF}$HcG{Lo{|2|o&%a=(Cng8i>CqEB(ny*h zwhh{1WBe>~m6gQpv^9aIjqXClx;-L%J{Ln?Nu=+P@KRxB7ukOXZTq1+4M7UJX zG3bN>4la@!IAT;pVwlh%CIuK9`Pr|EHqurtt%unaJlU^Nsba0Pfil7mwz$YJL=&zO zV=}Rr18c-a^n~0%TTM6=8fC&$NS?)<;*ve20mF@=Y_P^z6B8+(OrK~ODP&BYa-48Y z3I&^Rse#n9C%UO$N%OM>G!C1YN@q=74m+IybaHII`wB$ z$p4Z0L|Tbd+?+zf6_&?#1;cZ#MdL-`{dc{77KU3~N+^3*HEk1V2*Xn*6B6uJU4$AF zM~I~o5g!f1uyD0ht;QlEFf2lVDkZ82@wiVT<@P=f!h5>6F`iGXq3*$OVRkhqcLew{ zxu=k37^W5O7@>*k&D^&l=&54Cm5&3l2W4X$6dy$I-gcP4^4-I=*IVUdd2T?cI}&A2rr%wl1GK^!c=rj)Hi`dz&ly&Z6qx@R?Jj zIn6OOm237fj(!m_w{R=-)}{V_WgV!9E2+t2p2w#nx*Z;uwO=C&?>9ay40tu!(Xx(;An7I@=;k{!i|=MgZ?ca)vN;NewVKX zD}7YLyO(@lK3#vN6rIIbu&nHa|Gw8A40yg?S((4Xx$6o)E@>9mOZ?`IF;o^y%_ENH=w>4x&ciajcZ_@ z_WOitjsJZ}^ar;CGd!xxgI3GSkG=mx|FkAeablX()9B$mVm#Hl=c{gRkT=LLSl!N2 z%u9Nqb;GB%04XoHw&=nVZr4lR`{o}6p(5o@*51oaYlnYg0A${kqOibq%b16RA8hj3 ztSR%$+c!|ut;^27l-t-brP}|U>kVnogLf1&0$yb7-|~VorR|O&Fi{!{qD~l zvoARPsKGKvc#&FlN_ zt!xRX@7&GYl3to+A9}lZ*`hOy>aOccx;H%sNUNZz_E72fH9!vfLGQG~jRzL@2|i4A P{rhRcW7LPkHWd5^f;iH2 delta 3772 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 delta 3760 zcma)8X*kr4*Zz&M@B5l+RVeCbWF=QF*SVovpDGy;ZA!|aG3?U(pEh#jX zD3T>hmSh=A$o}_U@BjV&Uf20>U-#MWbFTB@RP)VB5{2+!000&l)2#u3*~HJp#MaWp zL?R?C*vBsr4FJ&-1@=)cqyGzALcK7%6ax(}!sRlAoO?#bdDywi;vw0O5n-cc)qg#O0-hxh1^Mav=53hvL(K z&0vVWmwRkfb;qgb;4tDaSR;N1FDVa2&lXXOgI6zXAay|Cz*GD)rQvzCIyXhWT(*>FAy#;KNrk0 z5_0I&8K-?y3l=Wo%AMnEcn8yBTb|{1OIQsNp!XCk>=JB!z(i}*%gBS2)JfvHD$3SWh5JWM?!HIfyookNVy(H1XJF-4KK%JvT{dDJ3uWd`#2O(>;M ze97qNTt8pG{oBeb*cPVgdt#>9pR-2-ele^puiX0+#me@8?Ouf+2!6qT) z9IGo~w)zK&WQUN`Fx)~q7wjzl7r?^xNJgunD5`(*eOEQJn$*gw|I zZfbwezd9&6$ToNf3Wgd$>&E=6eX8TDH78;w+9$l$!`AuNS0|_uAq9p7iUqqm7#;k6 z1XGJfi(N}%%VfmRZ~ZT*Ron{G?x$V$mFZ#gUCf`7U4h@!G3v0mrivP)22m&wriI$< zC!&4@JoRfH-n>6o!9}^JkLic@&;?9HSvXM{)ek68Yw+l z6Irnox-9FgeezQ$eJ7mLuG2TN<(S*5;wom$InY+bIgd3@Cy&xtQJq)a9hrxj;hpDA zzcg}b*FI_bE>cHUB#EqslBi4 zu6)1Z>?`8?JiK5>U7+k5k;0uFnJr+CDNimBdKN>;Tom4D8F}+%wrsvVZ0@_vhxY$0D90~j;D^k9{O-_1+4#3pXM1p=GnvcvUbRfUb|HMSbR=A!*Su!5x0fgV8^a=f$^L{Wa%a~YEZ&na zwpA9iSu78-7UkK#pO;h_2j#U5b(VI?qd4Wt1atOJi=5MW8^U}QYb&t{5T^wd&;>Xpd zPwT4|tEHB3$jEgeho)6M2lHXjPp@4UFfuxCnL^~VMWPQB{rsAs|;G*a?bOsM?KxSu&6*zZ|h*;F6n2vcX?sKbpySxIa9cOtK*yicRTksUz4Fi z0|D%YRg=el-_8&Ga=QCwYseqSzSpEo)3s{78a?y)kdrx!S2u%K(fgEjXWbg?AM4f2 z!_+VML;udv@Na*=DY-wxwNXE=i8KYtPG4x2Y1#c7xwkG5#k^rP65cB59dbA`^`tC@Mcn}=>_8AB5cBK+unnu zx$^pQo}o|iPG`8)@T%pRllh}-LoUbTn=A(@Lmf4^%AGK3Rpl$49;qJj)M8$hv#-A#dwQ(Ae}CtQpe@67{%HzfOxU!0dGE5Z;hwG@o&MjjdOG*Yo!3eYu2?-J-jf2=}&#@HA2?QmPz!K70T53pD zRhR};4X&;RRYR-#K($p=HK1^HxVEaQmL?2^)*=~!d3mETAxhqU-a2MiU>ar`TB_=B z6=PBYJue9azQFol@;vFkq&1DSysDDAgq$@J`9v|w639}RUa4Gm2s9D3dr4OK&;)u39MS}3TtkB_&ex2lFZ45kYC-*_GG zU{nMK9fY;Kq6hguVgH{=Aj%{VgCYJDa+Y_#aaLO^2h+2&vvYWQdU|qll2b){3jk=c zEw30kL{F@_rUboLVO(fDJ6pEA_M1UjysrGJ{TIj4uig5yTAh3cZqHT?JS1v5rq9+N z6uM->zs7|+x2}Do($V|TdJf?=2|WVqzki+35U0cEoU0sBZES`=SUc%=@V4xA7ivE~ zvpi$z)D-0K0Se3KV!@9m!+^Z2U$cxn4TRZZOPrE<;qwz)F>VlF$#;!}_B6Q(2H|yg zE;n%VtVdR>{_v?X?+~ydY`9&2*H-S8GPDBJJRQ>E8$i5$pJxz4SPIkH>k2PxnS%j^ zvGOAk9L;`Dv%^pUZhc@p@LAe!m#e0$UtZ&dM+H#{@Wo?>0{Uc?l$d*N2ImxXBHb&cm8O*BU%G40)38<_e2Fb-KK;`on-=zf!MW*_JQg18k z;IjjvT%rCf^p4pY9gm6qeL6;0*@77u0ny2|8i8|?uv`mDzh4Rccp_8_=;v`%mp+!AKG@m7E z#L3CT#&G}80)@Mb9*yXeq{X0@J$20k0S9yVhQdQBTa`nMZszC4y7F&BCo`H{N!pWh z{E3c=bD)1aS~OFto&U5n-%CIzL*# N()9Y38e`A9{{g_-PG%;MlFX#ZU;pHR##pB_9*JwDrIqtt}E3zx$nE*z4yB_votSvYQm7DApn2`TaG0ko-5&z6*~xy^LJiJgQqxO&T|0(hK>v$ zQQ)n&#{v*L$L%Oo3+*#FLGfyMQE^I|kk<#T0mzsf^6^5cq#{nK*ex5;wmth%#4Q@p z0@_a4eP*e|om1hLW>@4ogo;vu717C=NJfZ*0A5Muk&xFT2e^u8EeM<{|Ka1#D$jAS^Sfzwh*?+=CE{GyA? zw^)09!7C$LqN+X)$AiJ3CaBdY{$iYDSr#WKoT4yjfd$HCl@DQZAf*q%A_WA$+o!q} z83`luPNhsWqL67%2wq=5tQ_b~6HFK$;(a)&A;KZ`8QSglH}!h^tpjTILg=NJ?|{Go zN4ZbJ^QC}N<`<;wg_5kM^f~jDD5?@DQGSG5|LuDiibB6bUzy(%NslPtl1K7F>;SYS zBm8`v*)Q>`;&&*Dr>{_XeI5~t(vV16fm;@pVBo1RquvdYg;ymb3ah5W2rWkF9F&$L zNsiDyL%@;fF||V-h`h?*YfKp&1jSJdN7MHhE27&~{xGI?J7<#vDlZF?&0<6$DUI7L zasuUK2%6C*8SPr6u4WJrc0^-iZq(-B^9zeN$s!kGx~?~$#j2oR$`o1DaI z4K$@^G14hg7)k0R3{ISw#4`fpBm|yt^`p)7yJ6?>p1V=QtVD~dl5~jm)iqx1oEd&L2TJ(J<00z z7_1SBhyv$Q{9Yak&F$rjCEO<$hh-0Gz;JWi0a)Ym-bR`&z1TbRxRHoDIbH}ig%K6P zYLG;Ykf@C)xxot3nLKuIo4 z)C*3G3M<5*+x7CTPk! z#b7eg6k#UGY>UBSA+1)Dv}y?xW47w=vy&kVa3}r0@3dM-y;W~u;La%z@EOTLPeSqg zXFg(i*UJ&6EwTjPxZ!F4s~%qTe=JBc)UO|Y3UEog4*(E7&t@?>LZ`0HYx(U4BKGa> z?#pcFwZlatYuCN<2m6!hXHF$0B-qV$YrkH8yq%qB0LkEs$gpeNd;a9Wu&pnnwI{uu z(Y3*e-EE^Y^zn6ZT}EpH==^;iI6?cC01uN?)A6g}?Aoa2f8Dvvio591pG*ft?mxlG zL6z0~FVfEL_E&z{`uNAlte8dn&sEu6(K}YfZ>?5uS<}J7&A(~;X5NL{!ML?=L?12P zX^#qBy0~Fdeb>!x!@o^mR0=}e_3f#{yE2XEMpaUc^{QdRh3jC{)XH{4{HyKdFWB23 z2V?US^BkARgSSD+^aJl7Lo-1YrCXL-xF%-;S@&l%eZ^OQ%;B+}T zYaY|I&nUm!diEPj;--U_ZyBC69vzRI{VcS8U3cBhTWIsuTI;gaWvd!HQm3u4u~|`B zvB^K1@a*DoS6egU5pePK=c7R;o7|9DU~kOWyQB*JYUb)1`Z|7D{P;^p+1E1W! zOUG`!_Da<|^*T^E6Cr9ES}$9RPOK2fy+tEFbbpJ0mFG==@wBeaZ7}C&58fivohy$H&)I++d&EYm zS8gwhUT`wjd8hc(K+EY&f&bv@cTcsPUp05vs6ox@$+Jt>r?s5?u051cT`~AA)0XBRfYO9}U!!VLxyZFMk87H2p=Ti^1JiC|6 zR7MW5ao~DISiAH=`|=#dJ!dr&_fL+q68I$SGtj2qzQrn%Zq|Y zla4e&Km?QyA|>=*KF)k|F3-%F*^6g(pPBvb>|Sg zjdXQk{(*jOo<2AL2ph|_47DTgacfNO;aF{SM%Uk^`#c2Tq7-N+n|&eMGXN9C#;(;C z9m{CL$#pRYZQc54m=T>DJCiS2@Il)-@RC5~PQ%ovg{JJ>i?cGo|;1`Pt5x>I}8Zw|_F)}(&p z58Yg!nh}|4_Iam+;km-*#xMuZNzWGLKrcd!`z`};IIncTGBR9wXFdG&Ps-Z~JFrIlIHRP^{H0OK1{ zX_2_tO{0VK-CGnr?T99kP7<#S%*emHEyeY8!|tCNbGS(d@smZ0o=8{f4EquZwIQ5# z!axpX1}IO=noK{^f9UpUidX}I<5=tlLjbLaEkry&QwqT5E{`Ja0RXx2AW7>Q2;iNRN*mUUTA_W-> zc+<4`teR9uX+FG#@aJF5nr5r|f>dT+oMONIaM_=S&Y3r_nYZo<1C4eklmjE88hgfn(6}&mxOqOR=1{;BB3O#o#g>uM{V==q4hNQzhZH)?xB^Ck*xU_-UOX z^7+H}w;)4cQn1#iSHk>H+w+442=c5>k!gkA1N>E&)FVF@mJZzf6HEXeJWa$#4j@_F z;(uap$H(Y9|6|85(ZP+Kw^Oi5Qost?DTQzeH05EaP%&^^B*zN$P83_S&!gmILpx(T z-B;o(qE+m*P+Nw_QOM@YaPlj&D)D>l2d!Z)s_&BRNCp&%HIYjqdE$32vvAxcqs3Ud zp3RvuiijtpjF^nP(c@@Dv1PHwAe(27vBYvRzU%iK^nD}+mY!0b%$jWW?5L|{u9{P| zEq2D3p-{}C_&NIx1)SzQ&C3@koy`35TfJp@L(uYA)fWy%`1}xa8OxY54*B3L@!o81 z-R@!psc86HmZ!Vto`=j5p_bv-<4gMK-_i%Y|IjZjEJYlfe--PPTz_qkHOvZt!1+N4ILE|yza2U%+l6U&*b zUR$%izGL&C#K8Jr^Vu?nA@0}1WkS}U%M>g7EJ8^ZN#^~x?iMxNJ$ZD9Wq$u8+93Z4 zy2uJ!-OiUF!rhL-ZP@9I1lmBBrMxptTpZsYtQd#T9tniVgd1Dx29>)}+)~_f7Xo@g zEbZ^6)>VGy+~^bOW9|z_KoFXUcOzaEZWR#~N@MrN8pd2!16R3Mm&d5V{<&JYQn@>- zcvaF~Fhi|k?XBAA+VSB2zZ&1M%fuyyov%AAOTPvTcktUqJ3N1>Bh&$5C0Ti>Vltl( zQW?A6lZ^f2o#9zCupTx2>SDJ?ZAz<0(ST9WVL>B``h9ZTOaF-KxqiW>n`A45R|*6z*BNFxtl#bB%Q|+U;4Dv_r<;L)Or53+QARscZ#wy}oBR6VNl8TbfOAw8A4e$%ny%#YJB@OUN^#;+1k%-I)`s2K#>xbPZN&Ah{ z#7*uEQ(9-x7tlPY{vrRve<9uwVVX>um-Gs>3ACT-WG-lM>0Dr7wiga0`!(@w3LIeN zCfud62DrXLbGaM@4rO~Jj4zE`OnlN0on(vfD>*P;>iGzboig-^tr7O*%j7vAJ>a}@ z6;&?%_g0l9A%L(aHA0!QKXR{6+(;^m|8#xa>tkSuJH^Y#!UwyOT@zVTUh^<4W?z3J zot?lT#<2R&TjfMgu+|zs%vhjJzL1NN!?Zcb^U?vp~n+5gWz^K z1~ZFm#1y_~pG3DzZ1hHcQ~G}f{6wfc&v~9xWWns2R(0)%Y_(*yncKB%Wac^I0{*K2 zIp*u$oajN&);O0>wDCYul|`jF3%9Y%t7~D7ypCZt=z6rnkldu(>E8oUrbi_1`}N6h z&1>HNJ6aQFzcB@G)c?#a8fVE9DvflqsC5nVTpxVU?a}={?ZkY>&cb@ri#V~FXz_UP zX5*->Ck&x{O{fUiPKz**Cr+-Kh7{9{%=Zow-6_BVk_PF8;=ELcu++oy6X%-mkq7 z1&qR~W6wv*_-27((eAxml2fY1W?qa(S}ke)z2UpMt)YiI(2UOj7j@bQw*&HmKSdOhFXk^VJLL zsej^&a)s^)88%d(MNpBm*x(L@B-_0FTuJqfgSkiK*;3ciyj)XF^`XoO;-2wwrnl2A zr+RnY{ya?{!kTgQ-My}@-=xJPr}aAjZTOzcxOnx=3iV2L!wCPAY4jUMzh&HB_o}U0 zC1KlS`T79$JL%A?X*lS|{ts!V7sPsM`-(uduf#9G8u8kl{gB;No>0a$;|ZsGNnvk} z8^%ul(jT(^B~_ii!!_Zj_MMxY`&;Z;sVodJd8g0MzQ;@t8ul0ti;@gP6OwX;g$0=T z>o;Bd$kV0oOF8<#M%bJYmxEp}PM*vh-R!qJ9$jZTi0f~xB$jOjQeT(7QSA`z5U%SZ zs&AAWSM-fPpw>`pCp+qeGbN9;PR+Ll4zV4j-^}(j7Vn=P%j`vM9Yv~$Up)Ub`3U-U z1WP|RLMSE>2WY$cak=1NMtF=n&H{&VB?ffi)BpeymZBPeRY@9!z{<+X!jx2`kuHh~ z7$p@Kc_|o57KM_LRg^)>Ay6n)S>$=7tP;piK?sNi&W(%#T_TTySZL0*DA@*tAd?|5 zF=b_WjGP=&5h0IKkVnYl7fDe;Sy535gF>8B;Sll|oIFBV zNg0b!adUH3a+On5KqBSf|Mjc7`eB3dIA4O1o;v)0!T&##!e!1OWCC5?&KZKRfu8;Z zPe0%PrpS2cWm3xLdc^sv=**3+^v}-D&ZTwp_Xq$0ml^44TZN6yWhU6P$U>t(QO^cV z(@V6V6Y{Iug4dO*KPU>PzetwcRQ-1McjM4Fl_&P|tK3HFz9n$#eZWks102XmH|_7> zXte~@nrgvKF}KY#s##$wP2M@{Uxbc;42W7fEfcBzR|;(bcy)=cS=7@G=sW@JK)mE> z2#`|xPNcUk_-22Kt~a(PoAljK&*u9C%)F+!}qv0vyz*@{~R@Ug7J5%B@bvJemUf$Q4i%m(eI}NG_B&Dw|0o z9`~A2C;Q3xY!a>4OE$brn1}}5!*F9#5rebtikO3azE3g6ub$3mKis8jU`IvjkegVc zn|CE+Kg#w)U+XrbadizkGGy-$Wi9Vkd!*+FZ?NWRPn@{6uYi-`Oj-;~GQOxqpx}pz zZhRS}ojcJ^aGuF=RCtwVpC-VmDpTa(j~l3Gku3SqMOb>2%__9o-eF?YdKMrQZK`gJHMa$qzbs+>T+&9!jP(wV#7~Hc@qUjf zkFwJ=O>Xc6Uj`2ED3*75INb(@Prz#lyBwd0br%tKZ>3e@^T`du>kNBw>!I}zOx@I)u09%Ucf;T`m;x~!0-Hk0V91= Ky-FSDNB;qnnE;#s diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_24.png b/assets/dolphin/external/L1_Mods_128x64/frame_24.png index d7c614902b6491d32e08a005c97c1f5bbc87fb2a..53d7b07a5eac0fd2b94d210954210c6fe46654a2 100644 GIT binary patch literal 3236 zcmcIn3se->86I3kK`>SzDk_eH4?H-#^V*%6738tY3XxSrjMh_4XJ_uhM0RJ{Sz$p_ z5Zd^tN2umBN5E&mM@!nAsP!Eon$%)qeWdD%4^)m3s{(q^p9)CvvcRp z|9}5?|Nr~%%&thAJ~cQnJP<*U;FM%@Iy@J_BYr?1_&a}BQxrS}xRQUaBFMlH-{FUp zR}Mpv0T<0FQur9obIR=#9lXaE);6o7Q1yhlyul zESBI4h|aa9p(e!*P=l7hMS`GFme-O7Mo+PmP>P@k9SOgBoMep*XQU~#>x02)Zpm&; zH(R=5!K+v-%j0nwb-KK~JZ&DWRot06lIM9HLFp(8hZeY+@AL>>+^I%(Bbb3Ix@DI~ zR-C90QLrhCJh2#L+PMUWs|VJpcBKg>Oy?C`I#Nscmeg%%wZ7cc;pnkeJxRGRN*CW= zfz^zB7to~xRaxW~K~gSodLp}nxv~_GqGl;S!L8@^3k)T(C!lMQJG&!2lBfgOzyYyU zXiIkRa~Vx;Ab1pahN5J57b>kgB1%zO5{;T8J0&Ggo#!_5MuO-xwL%?8f=77KnBp=B%1E(BntZ`nkz{-R>zG=t#uTUO5u74OF~?$% zlvb7{BP|LH5I6!SX_m!Bo{?}~5)3#;>j^;HDM_S32QTPvR>VcVO!)jeWh*Hn#OTSU zk+Rzb5zsttV@U%}+874s7&syTPw4G>)-KRwH`)xh3_C~2eje4wN`i=@-9~W~!Quu% zU~s@P1TF}AJx+@@&aNj7fM)F&`eR&^lC8L{iUhMuKR=&hGNri{yPOSgsOeJ^(Uc^U zf#eMa9M%X!CDCYC+ztT>O?C*GK<9F1`eYAjz~v^(Dy(sSS0l{=IbEY{8SPM~Q4oDi z5sQgFH2?|gnwEP>^Xo0B6E-gkK+u<{q|;1Q?4CTq4H7b8NnWW!-A|EMh1{1@hj|xx zULbJ*2o9I*48R2gXUBP1OM(NmNCBpM>Hin%U1=4w1ZO6IJFE_S^ToWL+|viY)vlT*3Yk6Cozi<_!mgnip5bwUB5k+?d<{ha zJq}1#VkC|SlEEI+!6I9J6_#Mrvw&d)oDf73hqxlnk(`JVB*hA>4TvJkye<~tX--cW zc(_+c6i$**TyS8+fxwEajSvOGuK#bW?rnn5QT3vz9sot!|_KKTz#k4 z9X>ZxQ>#uIgWN9Piy;?A1%LDCU~6S5vi$Lr7WK^0Pj_%%^hI3T`xk{G+=u^4ynA-G zW^dVheM`(?ZI6}&`mYPFolfnVa_0VsB)`vQO?WEZC_i8L&cjvVWtTs31lArHKj9L2 zzqF;?ujT9>|DwDr&kBZIZ&-GtqEDLT?3h8z9vqmAd^H?-wlZMM#%&Km3;mGa6$e>C z1ifT5;z;uDUhr#8?L^Z~FZPF!yP=j_tu^5pBX`IT8RU~ETadvsCoTB;hoviSS$3A* z`1Zi15$mI{*-JM3*6S%{2YYp`7TeQ#cN-}rIL&3LzgTtqv0PnW6;AlYr;HpoZd*{~ zKd;9Gr6NtM7if^mkq^H4RZaP-m2DA$_tFYC9jyr{JTyK!$1r=(lApaR9cfB0{h)er zP2Zx=W)w)Ae#Eje|@vf{F+eA761 zC%$@U=mJgre4^i`?*^ETkryuAuCJnI46R3&4E$*AxG(2}dHaS=uKQTdO+T`+dBm3O zK{dnwaiM-o&9^&FyB?p8tOzeWCUb`Gkn;7P`j1_)(MHYRcAoH5P9^2oB} zgfkP9*KD@k)(tAc+H0qc(ZmO3C2bDTS`%zq_l(uY_?dyLwVT^7HXr>>)TvVsathyS zS+xq8yua;8Kx6fskbb9-ru~LP{!!N>E;e0x8lyQ~Q{Csv-;qBqKioF#;K0jiMK{;@ z*Ty}moHBm)No?c$1FFzg;9NMmXzazxt0QjVSMRCRMyEnE(I) delta 3716 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 X+Rzc3$jHOL2ML%MT-UGF^@#r$oHEJ` diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_25.png b/assets/dolphin/external/L1_Mods_128x64/frame_25.png index 768030b3c5614c681e3e2af785c662695bc9296f..2838804e566049d8593108f52f572d6f6f185dcf 100644 GIT binary patch literal 3298 zcmb_e3se->8NLf@EYXU_CWuDIQP4z&otd3C5=E93Dyz6+lqA^9&fJ9+c4ysLU_k>b zo=DZCdVE9$50Ew*M1zf*#Hhztd~5KL6RP$kkxEjlMyXo0jS0QWE860tp6=P5xpU|L zzyG`c|NVFGg0zX_dxu4a0RVcZB%5vM9zegbJwwnnbL+*C==KXwa)u7T)6WEd22j2x z0?qYyrcd`xx28&p=8Bb7%?@MzE)TK>VDuQjM^c(%{yk&MQIf`ZrrQ>^CAGE$-_8cEtn(*&|0^g_2!_7iS>SOWbIt@i{d& z7DSZoTAnW+M@-w6;PQ0By7l%np@bRzvd2iplEEc)7+S3lH+8u>t#w~gK8n)Lw@Y9> zz0d=VHmGZPUIixQL$_~OM=(#e=F{|S?E&06U*E@2RXPKD^1QjN=}{FU%!Mw5ts`5i zm7hmS^g`LEdDAs5x1&&L9T72_j-{}X)0}QqE6`sEGHQ2#X4wbhaa1*iAWa0xq|+ve z7DkbjM3QZGRwM#d_R05K(n2~(OEfQ0?0v?X>U0!7j;YlurMPvU>{eikIUYxhVx3M^ z;wUKESw$frlpO>|Gb~}Z+etzYI2tM>LpxNql^60iYf4@)4MG1l&8nJ$FgjC-`0>y# z(vYAPks%xu0|`-NA>p7+stGa*uQ=LPiO?o_ov3Z(+ z0u>0yR7G-VUYCqi=5)zf(CBez1vQUoAcDzG9Tm8+y^E&6S?!}-C)O%WNmha#5sxcD zDL@r(pLTYU=Eqx58*D)~M4%6nNSlqWIeZ1O7baw(dOT8p#s@Li<@|^9Ht`B0QY=k4 zcn2f|kuec6qh&EOu{^TopJfU}0fi7C_Ns7?* zVvNE4v`t&a|CHU&`tInF1k}AjQR?jl9VcgXmiDEtTT*baCPrjLLZ&G@p+bfvWD>Fj zs-nuMf(TX4F}jsSuJch?vYq82!^i|FD-?ln6+)l{g&--Kmw7u>6rOopEGU`;mgYo) zva2*f1-A+`uoF<`72ZxNGU;Id8>?f3AX(ZnPDr3xnoOjqB(q>PQx*$FSxjUCpJ?G8 z>B&8@XTlSB@}z*+vj4lM#Y}M)PT-N}xJRtBwT5kiFg{#4t(N!vr=zmBPN9QBaM*c5 zK@Xj09Oy><=S3%&c{@M82terU6mvql|J?Vlo%Mc(h2Lmt(aU#zR2jMT+(4VZ`TWEY z+=lqArAOrQ)>yATDp;?JKWc_@N-IYI5l5*;}m!e?!xIBi0q)sq5RE z`$i7%OINFSLrGOV-@Q5}qip1$i`(PCfxcsBlo_bX_iy!YF$Cl=8?&Ntb>j_?J!;>E zf8ZQg!!V;Jne*_nj^YV-4(um5E!}Hw>EU5zx#gXOR0<|HbWi{{Mk^IKcDd88QCe?Uu-UeywyTAD- zu=Kg#g5_ays`vQI{0@A5>KAMAz?hJ4kN0b=-MsqK=SJ;ac~n||Rl7Ry_qG8UrrO0T zl2R)0FFcn{n^u>Xq?~$w;LLy+m(x9YV2_BvnGfFG_&h&#czwgIEA{545GwrJxuwh2 z$)H)o+{cdZT)O=3@m^tT4$7&m?;K?caSG394r;8qs6#Gc9*_C;Uz4ZoHDzZ>3aRhl!>|ruyVtMUI09 zYRZN#43xchBx%+9C5?=)XX9VM@qXZNu_>y>c7#56?sxNg_`c$zFU`@{MTLL}?awQ= zg-3^7TDGO^smeyG{@S6s>iPCMS8{>_kefbV`);pq`t^~7lFGTx2Ad%*Y3!W7#V2lm zeyk45EH^a2`kSW%rIwXff4X>LmBmn9+c;;)`IU#_E`J~o;38)Z%Ux^RAG2>^`|&`TMomG3BM{yZYSuuyOW; z2{GHU!>+qwy-ymdp9*N$ X`N*M1_L8U#0Z2)jXs#Rgdg;Fbz)FQz delta 3593 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 lv8MJxbnIUv>V}q2-WX2||MR!|k delta 3700 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 AN&o-= diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_27.png b/assets/dolphin/external/L1_Mods_128x64/frame_27.png index 9fca976de90623f1e32455e09b0efe1391403f20..7dc44d061e5e7cb6e5fa8ddfa4e0f099f0855c8f 100644 GIT binary patch literal 3423 zcmcIn3se->8J-m}NTfAtRg6c+5mQ8Gcb>a5qae#J3tLNUF6qK@*LV_-K7#X^Rht;xw}?(&GXL@noZ&(6%9 zJOBUv-~IpZpL1t*=Crh!=n>Hfg2Y(U%~|kT4v*xhKJYx}?W^(d(%+Mw<3o_A2Zs&~ z^2)2j;oKUhJ=>pcn<_}EODoE%184&-541**$x{LzQJN3@r~~9X-HF)E1BWrxDJNpH zSsP*Vm_UIueW4f3SUAltEu1g$GBza%og5G#fD8CVG~g<9`-DIu7RD9eI5e%p(6EVr zej;WJ1w^xLnW#zi0#vUhaETyj)WB;=J;PFli6})-gpP!N7AFk?!wEEncK$FJ%_}QH zmf6x73*IGS1%AIr(CJD_O0*@kR`urVNS^0)1f`=W99rPMQnz0W;BMd8E(A01NnWSN z?^NAr2vKyXMgBw#GVNG`%hL_(_I0KSCQKI)JvvfLgqGB0XtO=q)aB~7_W4tbVU$k3 zJp%jer5>Qm0zS3KD}mHv;P#L03g#(L{i?4({T6QBj~`+vOWgrIMc%@2dSpol3V{n^ z`=Biu=I0SiULg8auU%COy9$-r6%nN~sM7(MML$TyVAU9$pmBn+ zQ?x+y0%Mp!kODz;nA)HYWYI4^Y)o->f)XgbK(h}StFlumeG*ffO|ZIse$g!ft2q&a zq_j?_EEq^i;W$~3>m7OpXDNolMGk6#C0T{w^}H-eN|+b)H>*-nC=((74%y181Tng^ zDJT+0u>?=!v@G&CFESkNpaH;H2dyxApqEIp8*RGR2|GtDd=NFnN`{C)CKZ+e3hppa z4xFMH8s}Ngfb$MLBO54!RDgt`-^4Ykg{s%4$}qe1gY#CCDbuSe&O-RWmz9=+T2oDW zlGp2TSR)LTB|%ZWE)fdN=@RpS&g0Gx$sW>x%T0IsV2w*V8)*hu&^an}qG5Fkq7-V1 zL`({)0mxYAw6ljaKiPshU`q-B1bu``I?Q~k;x7@sz?ct9@>mt>zKy(3EPga~j%FE- zb{KE~WEtlHqu@Lv@wf~N1Z2HKp%u1k>Hin%ooST{M0Y-bZ&)4H`^7w&+sP3pNZIfbPep5bwkA|1F47y=gw zz~X>3$c)VKu#n2+FpEOxVLALDw?rAZTdW)VjK+lxNy}<%**z;bveOl5|VnmRLGOO8W4>W(4+vHwO3~0N5zck}= z!rKdu@B39lo5na3KiVoy?w{KpyQ@`rjzdNwXTqnz@SWa~zK!RmJk#)L?N^$HNgEHB z@#|y8HuRlxq*@c?Hw{FJV{!+Te`YaVS@h$;nS8{2_g-K(b@7$8t>ciWHx?r4nz~<# zbJJVin^#`{w_zWk6_JaRwk@+N5qk#5>{{<%v?CrV{`>N6P46CW-!>#^N59JUf-QZB zb8VHAs_y-B_cKK8Ar@J??yE22f;azi+op-nI5#HxDdEOA+kDN*$MZAJ-l<1U-9Pfj|Fp_Ewr;v5ffG^RTX%01lJL%Jw??3CkrR=fDVyVX<*hAqD(qF))?mqpB2T_N?!%dY_UUFV z)4f!bb?Nd&gLx+<)>pU88amFkcj}Rvj6cfrzBKK8!ZmeyF(>%X>g@Rf|;yg5Yfhum()kUu0OT)Z0mdh$;z zb21|925V{;ZyolJ4-N!lA{V&Xh~U0MxlvJh#-A^Ga|{vLf9{o8UuY0z&bG#EH74Qk zFIF^1{itThZ%^&ZxUe{_c~kqIRn&pmUk^C%Ut%2-iBzAH<7Q2JamTQ}$pv?}l-*Cc zzV6uN_G~7yX3^+d(vh-^jH&wzc>A7RGyhCh{9uJ~=KGV9)Yj+|9^~}hgqMq3B9db- mtv%V;ymIb#jey)iZaw|s4Ypqr6YLxM1hS@1Gw)8xTlQ~>x2bjj delta 3809 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 delta 3826 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 delta 3878 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 G>;D7y8Z;#U delta 3840 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 DW7!Jk diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_33.png b/assets/dolphin/external/L1_Mods_128x64/frame_33.png index b6c6fbb19168b4e5b01f36c2a17bbe00336a4a7d..768030b3c5614c681e3e2af785c662695bc9296f 100644 GIT binary patch delta 585 zcmV-P0=E71A+;c|u?z=7Gcq?uK{%7R3^fNrGcq?uK{u1Q3>~u(4Fmyy74nb}00006 zP)t-s|Ns900033O(|!N|0rE*iK~#9!?3s&>gdhw>d;kBZcQ&H*fl!dv-DI5TOrn_{ zN_$(7=yTw@9AE6`?**hDD);vtUx2?3K>rp1jw5mkfTS203@;quW2gD?9nS%94-l7z z6N3xOf^sTD$VG%&~OTvHIW{|K7s*+7VPx^d;p~N{zl{C08AE=iONVF zumrjgU{wiy<@gA+QfZ`Q*S@sejOYiT08~f9?Z#|YUxTafP1M*B380Ly4O8x&uva!* zYf4qnkv8Q3R$is9@&$%`h-qDM|0;m>$Q4&-B%MKRyDt7DORrD8FP5}+_fhu){0J}r XJERAEvsQ=J00000NkvXXu0mjfK%D;@ delta 776 zcmV+j1NZ#3AoL-yu?z<`FhMyuFgcUB3^fKeFhMyuFq5|o9c+j(>pP3Lt+T0D)tr34jmzKIQ4)xbO8 zTRzasNcR#E4u0iepVT@rbTW zf#}0US~eQbY0eA^^&Xv$n`24Jg=v{@{PZ0VrxhiYJjImIIi!1cA|u z$s#lmw9wQq!cXAJY+5U>698V~SB{#?C34V+GvY!y4g%-Y~4 zhyh3gK*WY?PJ?zMhziZq09pW*VQe*wlr6LAr~vpbnF9zQxbzDQ^#LQJphQtmN#uU(EkgDPYo-P2lXTmgj*QC97=swz$c8)>gNnkuW#sIB^Rl(N^D^|Z|`<+ zy63^sa)>*kjXuA`cx}&>0%`(4ApATl*R?xp&~F3$2rvM>mkCDR%M8f?0000~u(4FmyyFa*R>00006 zP)t-s|Ns900033O(|!N|0%1u+K~#9!?3jse!!QVj`QQJg*QrUt95(pKHffpGMoMab z<_4nIh0n+POWb~cfYeLZ^L^Jpz;6YhKM#P!by5YuLpCrt-f@7}KJH)cS_eQ3aLx@2 zhdZW%>tAhz=K>J}&<_KDSZ8QUJXC?4<_?``nBGOtQ z!o|=%9L`DL9Rw2qBL|t401|5cNZ{JO5&o(QAfIPc5XEWu=V%YQ41h@XRst>nEdY$d z<31I*ZrFP~mH>tlfUdz*enh8MtN=t_VE0#xs5rH?Bx@Fgcm-a69B`D7b3;m1#qb4L zj)c1aI7h7mr22yBh z~u(4FmyydMF~g0000C zP)t-s|Ns90000RI2@Vbp+&)wN00071NklEV20FW(QK;-u^U`;LEr-wr^3762WGrwM?EeBfqz?f|cO%%8qv z9RPIzzc3tFoLdxJf5k+9crFYx0J#I837~`F0RYR=SdgK#;z$(S0eFXD)}RKkpa9(i zoB`kRfxC>frT{L6=HlR&iq9n23jn@98PPif&|zj221UxV+ACfI>0zj!LU@Cx*_0L)36E;MFo?mK`-yD>Gc25Moi%%sYE;!gJ z)~DfMyLSlu>szW~s9`Jxxb4Hb0u6v{-n8cMUiRlVWEHjn)K<4RK}S4XL1Y!`)d%>= zMuTVuV6aQ(4nQ*iVoz`eK&^k50>Rq^+3PDow&;Xf0quo<6$QQvK+^!21i;q;?5ku4 zCxjh_(*HdHn8B|okSRL}kj`<~j8Fg+6$#&*e45oF*AVG-4s@?40E$Ui!!(<~@smpoegqf* YdZP#}Qxh%?_W%F@07*qoM6N<$f+LqJx&QzG diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_35.png b/assets/dolphin/external/L1_Mods_128x64/frame_35.png index 461270ba4b63ea40869f892013b3241507550ef3..9fca976de90623f1e32455e09b0efe1391403f20 100644 GIT binary patch delta 791 zcmV+y1L*wrA($etu?z<}G&VLhFgcUB3^fNiG&VLhFgTOA3>~u(4FmxMG)b^ElNk;h zf6qxoK~#9! z_xp}N1wdXdzrlbBp!^pTKwq9_0D&V8AkP5sLngS{-a3FX2Jl|Te8rA+0Pq1k7X^vU z?T7-Cf%YdB!XsgL1EA~&fD8Z~3_k$Cf3h?dXjsvKI{-Vva0H-p-~o7+fg|8YK5(Z< zYXHzN3^#}87&t^Q1mJtKgdRW{Z3aNV%n%&l((axw4_xG2T!y-dSs-kJ!{(=WfUn*N zLd$N)sqhwh(+mKeM(;`;5$~|Rj{vxKz=GF`RuM&#fqMXE7+&Wu{&=SuBXe*k0+ zCh{Y$t$aS}@n022fYgmJ0Aqc*A4Wj??RtS&VSrZyAb_BJ;JxOn-(PFxk0T=92q})H zRJuTQePF4^5&(LRLfdlv-uC;iwxw&eT7drQI);Fvdpqw>b_mC(VgO|g0E9?_`hW^U zUCt~ve9*Gxgic!)mIp7kmy%DOe~iGAAx1wi2LPs|l63qzd7X!YV*vrU8h1q7n|ap< zm|GD5o7$(06AULmk1=4|WJTOkG{GHl%>n>YqYi3op@~TVr%V)Hn&js}vmt&~+DLUc zqs<{5&sPx|Mu1+i(h@Gv91Wdm0DzZWAi&8FV@Buwd54{Bh|@3xtk%YCe>i&-(DsS7 zQIc9r0H7Vq2teU(Vgrb@uT~6_HOy!-`h-mYL}dkc@WAI&Fm!j&Q3V*F-78?#OrVR} zRc#7@e2R_3uCA2_nesLv1V{jY6W_2+pj)s-1_pBrG6GU6B=z2z{={qgVA|q?7N<=_ zLI<+hin%}2CXombQ$pkqX)~u(4FmxM7l;@IlNk;h zf2~PGK~#9!?3oF2!ypVr_3{1h+@!{!)d*v!?M&$;aq5MKb`+x5KzrLi*x~yPq%NxG z?;HOBzZHP~JOC2oNfiJevVpzSKncTp;QI^uqww z89o5uX&MV+fVyqQ3q}CbZa4|hWe@>8e=C9A;m>Sf6p_{f5h}y!;_#dV_8^!7_+Bg% z4}=F@l>icIhLQnV>}Ek^1t2MbW+W|maASeL4!O{E0Gmb;sK5oF>i~zn3t5=Q-lGEJ zhkT+={+agzN&#ro zgs=hQh9Yy`0AS*Qm4Gr_9x-k)LinXfPex)!rp5}t{ z0Q+co(IH48N70Ph@c{q#@zvdV=yMK1)(^ez&#^jEaYY9}^d)r4F2R%y!~l@%fa^h8 z43ciReq05Bl#<@H;}(D}OIRJq4B#|~asaUk2uc_>y)FX~!WN`*b?<2be*j1*Ay1$! zSro7E3yi3L*OUj!6O6C45@3YBlO-boa`WrfVBEo+=uz+uRM2@ncGsq#9|BA?NkAv_ zDhi!@833*Vq+8oCtpy3-Z<1%?K=yYK-wmbxrvWfO*nQOtWY&Wy{zu7ZUI4QAgvi<~ zsZ+9fJPjbXRy|0Wav%$EM_vBlo(o;2GHd!(&08eD6yt?K(#D51s n!u=vG>)Z3cn*B7umjD9*1@H+*2nLDc00000NkvXXu0mjfk}Fk- diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_36.png b/assets/dolphin/external/L1_Mods_128x64/frame_36.png index b018a94c19c4cdd239a3043b1ffe49e93cb0f471..4b2ab5863a557a803ea13e88513282e1c26b2c00 100644 GIT binary patch delta 831 zcmV-F1Hk;-A*mv;u?z<}G&VLhFg2673^fNiG&VLhFf^063>~u(4Fmyy3l+6$0000I zP)t-s|Ns900092}{s{>Q4h|0e{r!N0yn_G$0@_JLK~#9!>{yF#d@~N9Z z{1_7h>8*4sZM9XDg|R&z21@xIzvF)%NQ|Fd|Kou^F>5MN`jZ>+w13_M8Ke<_z5Ja3 zHbyel(O&~Fdgs7zW}B^l^)`U=VF13E5A{%%13vg?+W!FXg-ixV0M^@3zktDOV*r~< z)TcoZKROL~PfBJJ_=T%$r2S5ip`Hv;#oCc(L48Q&)O_J_EE3`~NTf^~z(7 zck%!LY)%21fp-izR&5_(?H4K}1whuf@yG<6Q=_^>tzmWW*a8 zwt*{}q(BWhu;sA~fNPKHG1u>~-@op;w@uYh+F?HP*j^dx3 zP>VB>Sg>Rdks#jT*NzNJ0I(}=z|>7cNb5xaz~p6zthLd^NdVVraO!|Dm>Tnv#{51e z*Jr23Y(KYF&Zpos+t$J*4o5>W4N{HIM*W+FRk&Erj2AAkK={WuR>`$2KDb&;ZVdp!*zx z{su3BKZ4zVWx(PMuAtb0#Ky5l@%jV+*NgPF1#;2ij*0L(K<=}!Vp>$`6au>uV5EfN zq`G(y-Z<462d5M5s6C!)J^{l0N_Oj6#e;b}negAe*DgZZ*s`8-IsZ_C;_h~G6Vvi(002ov JPDHLkV1lg=hIRk| delta 752 zcmV~u(4FmyybLkGg00006 zP)t-s|Ns900033O(|!N|0+>leK~#9!Lp z!CHG97;n!Har*m)W*YVD*ByTffc?6CBLX3S_Fqx}|MhkRKpZ@P^#}kTO2K4!=Kxv_ z;L%U`;*N6w$N}8Pf`!38jH6{Rx zGK!X`DgYW2X*Bs}q0x+o<8@~1#B?e!YP zM*PjN4d%o9rUU@m5dbaFPyo{0BDjBld50(>J_WHLpD#EOTM@|uHvvOOx7j`HER+Ba z{OVEyWFNprZ1-t@2<1}HoeFKq*fe0}3YK$=TI+x{mBeaP=S&P+j4G3EaghM})1#}> zJtOn8Xpz;PBe4Bv^)^HB22hTTHY7;JB}EX}$8^<58uaG#;)Tt+qTyJjg(H|)FKnTI zqBal&vJYmp2vrIu0HC83j_eq~NI_(cbp>z)VgWQ}5++Q4K>>hSzN&so8V<*FIe>=5 z7QQJi)Hqye`n!u272_uOrBL i-p>Em?Ari-1Q-CO>~u(4Fmyy1(n_30000I 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 delta 763 zcmV~u(4Fmyyb_4XU0000C zP)t-s|Ns900092}{{8*^nX3d|0007=Nklm5MRc?VS;l22TP}AqWQF9?=Ar1m|+(d^F1Kj07wR!U~$oLzWxk=T37!}Gk_fc zI|adTng}2b!X<#v%qfo?kvfy2@v7Aw> zd})AbjR8wQS7I08^+j5UA>C_j(F}lKVqud94&}yyxiU?U06+>o2Vhpxc!~jt*C4QB z#b9bEP^Q`hqpXa71+a73X{A@7gqjmn{fM={ag*A-hB3!j0DvV{LDnBw0KlafEpUX# zool1ctLChu=~EZ0(lU)zSNUroSuBJh@|$apAO!yO*0^%PGrT}VqObIt;&j)LJBZ8C z_+ktD88doU$XVi;i9yz-7#+a?fK6vk!sgsI0jOHa&E}PV76XU{AdCoOq`>i#TP(;+ zuFS%)-D=AAz+C_uL89b-q_Hf5DIhA3h42WNmPC?;bND;}8iBth4FI&W;}}p;B!lL! zHl+s{fDG1kD6;sE8q`j}M8Q%E7$ty^CYODHy|?EojNwB$;oVL+ddtN(t9o4uge(;q zT_!*`Fd6_>SCQy-<>3?1yy>ol-y0UXt@rrvi@28Glz0Pk1&KHOo+4u(fFvNJY=}2_ to%9yWE&w9@s#ErF=l^T=ZU8?53;-<>2@9NP^V9$U002ovPDHLkV1i^>TM_^O diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_40.png b/assets/dolphin/external/L1_Mods_128x64/frame_40.png index 7f6b4b29a38e935cdf69f158929d29cf0de5091e..acf000827ffb7177e5b7dbdc7935afc06a35d147 100644 GIT binary patch delta 840 zcmV-O1GoIyA+jQ{u?z<`FhMyuFhrBM3^fNfFhMyuFhi5L3>~u(4FmyydIsCu0000C zP)t-s|Ns90000RI2@Vbp+&)wN0008%Nklc{)kT|Pe`=ArWYeaAlqz}}X>V1NRU|HTFHw=Xk*#E}QE&H(UB zE|^SjAApPjJnYa{?$`%^fEd8{R>8vL?pOtsf$}zn@Rema0U-ASzzP7r7=8f&^E5VS z*vSJk0Cz0I5rD=)1mL?2ychf^24)#)4*&{=;pFf=2Hr_<1pq(`)nxz)H^YpE{%&(# zzrxgI1)v^M9>_9Kp8R;;3=Fhi>ZOU6{-Fy1qV+qFI_7z>-wgnNw+t%jkJBk~9ItQB zlggu@f42-w0RS$-x<5VbzpR&!gU>attp?a~BMfu^A{Sgzxw8Q9;RKdMHz_0~-7{2cf1 zuc7m|;3jStWG%gwl4N%vKnP$nu(Bva0GE>+u+(W_0Dw7yB&AV#0+{@6(KkmBml|o*((m@_Q5R?gK!>Vco<34q8g!e5VRckpo-6>`Bg&U8gaG(1y#*A!r3<13 z41g)Two->#e4+R4gZmRyaLuYVH^5aXH`F=-qQ;i~43Ofqu4ohTQ|B5HKhLzLyw1N$ zD6Y4Y8<_Un$HtB3DcvJ4pppbI%7%EY=Bf+q1^|g8 S@tutT00006qLO7GS3^fNZF)>6qLN}AR3>~u(4FmyyUtq?Y0000C zP)t-s|Ns900092}{{8*^nX3d|0007zNkl;L-87l`Y<@BjEhpO}?eX!zl-eA=(q1qNvbV2{5B;PypRoqbOL zt1$sjRHGpHKuhIRE{`(F9BFwn>Yy&_UK%=33e-l8bT$>pS06PG78iM6C z9zd9em8MDMCq@=alCd@m+Za z2!k7ObtOwMYC}xr^@IqtZkB(cry0PgQ6MVW`5Y2OoRJ0Zis)0OBAnAK%AyJbGXX=1 zLxeJn=)8q%%^PY4fHSdNgmE_u$;0#}o*(tHB30E7{t&lDJ5Vv7ZTxyhBQG=#YQoL%cp02)D{ z5=t}`tr97i$0`vh!2Vgo9{a$%E z0-85{Dq+`2hQevrO^aLlr8Z(uWR%_0f0!qEXwwA g{=a6Q2Jj=m0Ny?b`5&15WB>pF07*qoM6N<$f=b~`v;Y7A diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_41.png b/assets/dolphin/external/L1_Mods_128x64/frame_41.png new file mode 100644 index 0000000000000000000000000000000000000000..b6c6fbb19168b4e5b01f36c2a17bbe00336a4a7d GIT binary patch literal 4340 zcmbVOc{r49+rP~q+t^D;rV%X~v)IO1vKwV5v?yZ?4KrpMgBe*9DOt*vED0qV+QcMV zQI@e631!KW5Gsk#H$BhO^L>B3$NR^3ANO)y=k@!Y+jXADeO+|0vx0~yhyVZpvBsG@ z@xD>K$5;r=drt}_+X8@y8OhAd!P?9WN~2SKNMs@aFb8v;L)=qWwi%7A5G7nq2NoY> zlA{5b3R|c}@@RqNRRDipQrh@MY`lo8>{h89+>xepeImHr`0@O`9AA@5@y8O5oWOim z9xi?y9=b3;|MShyn(3VB^-floM3j`Y+LfsSKS0kZ)&M)8oNSM4YORd|ZN-i9LnWuU z&wT}60Kg)c#f)IwKe*0!j|zYQTA#*l>E^LtG1U%gZUFQOFbb2N7UfHE0$Ma&T4aHi z?ZAEKN8Tbp8UT=4VWu#k6avhBvo;a{21-(g<$!^L9VJ3QA|H^o*J?M&_$lCdB--f! zsNp$~&o+i>@Hdq6MIEg~TY;i#1%Tir_Y?_ZParDG^gufR#ee{_oiZI@s0}zD*;_wa za*7tq!SMkN-1Lsj!@Ziaob+ymkS~j)Fgudflu=en>F-r8!0&O^Eo; zTiqNAT?EctH_j?+D2&-SgAWKUcS@S(@|&UpR;E=pn`0w4lJ@nCYdtt|VC$tN_lq0{ zm~AubiPIJHU9IWk(le3hCFP6_;nWZaz>c$Tu&dnim`iSm_{;}N5O?nl28<)V!Y?a` zUI8S1wa}~;07$K0O*U5N13EH`J^(6b3RqDt#h)mL@0Y zxjpas_L?|hev=j<8T>A6yp&hc0IX9;E-Q&2hR11LjVgl9#LGIuUbN7QKqaPrS3Gbk zU*L3Ey~Gp7Ybir5Q1qqsOEp%gwCIP9;Mbra<0p5N6)wHZ5A0!RNq9tM6!`ZjRO~d2 zsw^n(Is7q*0fb*lB1H9|#COw8-qv?DhY+WoCLN z=39suBn}eOjPu$n?=OE=zIc0;JR-~Hw!o{*tQHsK8?<7U?=^{Q)FbZuzwZy*dZkS| z9h>f&9+5s>>T)FT$iY5Vsi@1{BNBH{x`yAjJo3olLy7R~ZFl=hl#V2*r1LBs3_l2e(C=5~Qx;LCI~Y1x zH|RA_pWik=JGc-;%QenDkh_efU{`{Kt97a!t7EH&g1SB#y&}xAW`vj9mc?h@^;j)a zz7;OZeOl;W=uy^1Y6xr?qEOK5PO3+DY&KS%H;|gThW~{KUxf;2$T$f$nu&w(~%pRDf^t+KA z6y37Lva#9R$~$G*Wlh1^lo8o++2nn_`<9&p22peCb0Txs=LF~VzT>~Eey4ozQfl8J zt<zn}_y)**d2)%Kk?sgq)#IQ~! zqCX-lV#|8oPyIFSM)HsPG1ixDOLhXDe9!nM`D&vTq91|%!OHwu{I|Af3nU6W6-0;` zZ8a4Wh8$I3f;T<_W1CX~DNnBp3;UCm}^zf@9Om(Yl01oif+%}jfx zQ2eMBIi90TlFyP`4VT@wA6=^P$+5zjafY#Spr1W)^rvra(o%BCewFlKU$!6F ziA=kcoI?gzr*d7> zzhmC%hnuaPj zy;b%;>guVG3Dy*)O)>RC+scH>YT(zQt>jpnp27;JatHBkHi$dw%;VdSGdZ|g+_Bf1 zBR-p-R#imLZ40YSx$nTa|7d{2Ji0UrtG9T%O(nruE<^+6;Z*I-BrW!ab5C<0W^6c& zyE`5E;>Q~PlH?TAd+U~&`Z+hP^pdnzZb13BV^0WM@#)@27W^{%j=M(gmrFmzrm^po z|G00-nHqPqJJlDq%%ThqzCTpeHb0A-Et(BruN-M%mq}y0>bB{q^rc&VCh-QQE5i zej#nH=CL}{&A-+nd^w=^^SO7)3SNrU^-~e=I94I{FY=Pl-a5PGyT!t+AA?FBzE@xW zP?+MRleiOyrA=l>Q3tcc=Y z_hRdV*ziJ1zTwTAOtmQC%fh4=k7IJTAetT`k~mnI4{@k zpyBJRVb+SxdX~S3qerc;Sy$dcImTz3s#7biU*AtBdo0$_zQI8DT1C({ z#&GwJQ)h`Q+<7;Ha>h5?S^b`cSCMOe4Sj)ce!S7}xWTGjcsZw76`=NR2S>Gf`A6{g zdASgg&o;vzp~=j9>ve-0pSG+?e2T2td_ZiVjQ;R!@a$?lDzP9gd?9Z$?boXdW4%`G zR(&d#Dv8Ot%F2omh1xIPovCBR4~u2G+9F(kv1SABPLFJi|2*8~zCN%hx|+~cU(PD| zN?*8Jau3_A(yUz5$ueBJy&({x?R0NulxB0AO&CNy8KTi43S0(U%lp2wQks4}+4t4PkEDwn$r=8Syj;7fL5O zhuXOiLj4JP-mrs4Py;5Gr+`9a;Gs+kIUo?rG=%-77t0&}79(KLzaR{ML)hO=x!XEG z&8T!DR96EHCm@kXsIHy{%1cKZudC;!bpVP+qR|MX4g#eKN29Sw6z_)qbHR89>E1qA zCv(ex%<*Q1u+t0%4U0g8goJ2>XlhXDz6g|_o*n{;MxfDf9s(Z73Si)w@PI&-KN`%5 zfdo2<#voAxpuaWZy{JJ9Lm1E5zoVegY;FHl91!?Vu6S8PF!3}5N&|_YP=1H@7dnvP zMEqYj{v$fjg+(JGoQQ$cAUc7!B|a*D$h`FZcSFB{Ja4e}blw3X08cij5`rki0EV@> zA&j@8;Z5?!VlY~GO-+;zTnnwO1=k{K`oQ&&nmTZ_Hd;?p6QheF5HWxB{3pJp4$?#q zgVfVTBTZ2#3v-OQIm*%!g|gH{n&_HY>ipqa2Lv+k0R-Y7+a#Xtf4G?c%Eg+|iFgK; z?n0%K{}h75X)1#nc$!LsqBS+Np?hud1X93n$-dva^mowaL^>&$=xs@-QlNjO8B6*P zF6d}ubaZs_XgDuaB3uhk)PiGlF$B1tkB_&mx2BFZ3Z)7AhwuIWO&@~i6ykTS{GZzS zBjOe0@8Q4Az?=NrPQ(CS57Bwe5^?xm4X@F}9Bf=He*OBz8wv{xi;Ig78#!+P0O+o@ zxrqyNa4sw1Y7A7&B5?WFFV;^7p)tX8rcd=BeJ0PkA5LPWUNR6b%3R6DhSwRl+cr;s zO^S>f5rIbT1fmJIgrg)@ji;9~zy=fD^#f;=0i%X$sD{~0gobHI5rI{-v67fF z;F<9fqTLWi*)0+ZG~zKrDM6!r?7jy5(c?zlT-=JZlz7}!BR*H~y)}&=$dCP0DkzD3 z#D`P20dMaG?A-7-`2*y$AftJ2*3M%x%R$CDK&~%HA#NrN@wPHWu)7anCxds4?u$Ho zIJ_1DVmdl-brVhi@q1upv^&R*fl3iiQ4sEqx}aN8PgG^NJ2(@dC+^o;lzn>9sj(tF zdS&9Vpxx@3J!#v4%aH{#aoKF({ul17e&)f(Ev17XkxJ*)RN%72x&GP>gWFwOTy90S zcACdPT9^0kRYVIcX3guZCHDJ4XxRrwlx`$MJyBib58=yid*~rsy2T&#ap(Lu&+sR8 z7x`Wvj=7xk5jsP@pZvqR7}GgxU-T*ZW240}9fdAZPs2`8QY8OVQisBhEU*0ZQ;JgF zeGa{ooR6R1Z?GG-7d3cD7$5I!9Y1Pxudg$@`1&K!WO-xExI>--X3|%)+fAd)1ubT>9 b7eWHHnZlOuxnixqe;n2pcIM@#p6C7teSDMt literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_42.png b/assets/dolphin/external/L1_Mods_128x64/frame_42.png new file mode 100644 index 0000000000000000000000000000000000000000..7d2dcda5d98ac939ae8263cb7db583cef6574fb7 GIT binary patch literal 4253 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 z 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
`cx7 zHpiOjL42rGG71I@4h~igR#PQWykQ6(9UT}P2}2^GEDdM?ok+#dpu_-$-wdX>04xPh zrs7FN@GnM;2Pu%M2Vpt;R}=)Ywe>&5#DKqZ#mX9th9Sccs&E*A@GG=GwF9Vjxc_(K zU$p}q=wuws4i`WQq+nTF;-&B#%u3&XH}p%9 z!i^CKGgEC-Q-rxW0%5KOH_|dO*ZhsOBnD71L@e&NZ9L2N-&nQ(iba`Fa2P6y;y@z# z|1Jbu9}<-m;6oyVk!q?M;KSA!ES~sFa^%-8{S~w+j)D)ud74v51n{3}M&bX(1x*cY zO-(Hf63Pk{2UW-5)S=p1+E}QLmzSrOr<$e)0-*-^8}IqQrVqw)3ihj3{-bt&i&zEu zYxqwyuqOYs6OPF0AquNmB%`IcSdGSSYvo|}^XE_25Cj4V3JMPC+wTAXdx@o~kppdD zJ`F^94dypnS=wO^<-g78<-ry%Sj{S4_sg7K96YBN1MTP#PH(-_g4B44Z9KFq~^=mqGNP{EgxF<0W`#-`{uwB z@N;dc_cQ%VfS^&IQ0=#_a+KW#Gj78lO4L@>mq`MRK>QqI&geA%ZDLi}SuRG>?ct{r z5)9Q7E^^KMqw3KVpq!WIxRhqCDu?hZiF`^R6VC>q4{jZQhKH7q@@8F>21?oU&kVv# zxIm+%+uq}btHHt}0tGvTY;c4AOTB!aeUXQEEn0le0Tt_4ng>*h>?`QH0^mv~J#-oc z%tv;DwR0eE33u~&UQH|l;hOc%Y(7D0o`t%+3hE#w9 Z;93Nk^X$3K`tbw+OS6-vrN-`;{|~Y=oI3yj literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_44.png b/assets/dolphin/external/L1_Mods_128x64/frame_44.png new file mode 100644 index 0000000000000000000000000000000000000000..b018a94c19c4cdd239a3043b1ffe49e93cb0f471 GIT binary patch literal 4315 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 Bcas1B literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_46.png b/assets/dolphin/external/L1_Mods_128x64/frame_46.png new file mode 100644 index 0000000000000000000000000000000000000000..ed38122f52a73a977c2ed044d22e8c113ae6c3f9 GIT binary patch literal 4301 zcmbVOc|4SD_rJ$d$ks%*$~2-yV>ZNO?91q}HTDXPF&JiRjAbNEDJ_Z-k~LdNhBjrA zts>QkD6*9$OF~11Wcy9e^Yr}QKc3I~$9sS7<+{#wzUO?;xz73A*Ets_J2@G382|v} z>~Yq3(U&87%%#Ld?-8jaM*xtqB3oIx*jrga7)*K~nL+}9sGc0xNUxOnji&wcBn1!4 zuIW4JloJ3{izoG5(YrwL5`>kgevrlavj-B~ z4;g*Y>?^*1ELu1 zm4N4}z-`xi0Wv@;08rR5mQdiP95DIa-c%CkDoN>60lEq{l}G^>K|sb1yKQ3TkATDO zC-A$)>Yo64Jaed?M147kxC-Wn0ZxYH?q&dDBnDV*+3-pn;vk-f zXs;V6@nf9k<3K>YAnjH9AMI!*e%fpG$gk4_LtBTQQ0`a~RCX!`N)JP`)3dZT;Ksxq zT4Vu0eWC5~ijf(2Y;<^VG;oX}{N7lw5;{$Xnr@xxU#Y$zCIKiqwF-OuMn*ct8BL(u z;dP-U2Z0_BAh>_Q2i+^3cR=QK^g;sLXvus_ZobQNlg*oC)~%b;_Rp_Dd`vK1MGw*k z5@P2>t1G?H)4-8T^Nh0k!noxl1X@HnK5OFCei5d9U^ez&!RF z?1K8bWI!=UAH|*rfRwt0By(L5@G8CNBLGx?QayAdRmI_%6aZM~o!E8TVy*0BC0@OJ z_T$af^(tb7HP+^`+v-geE!PZlHp%3xUbawktH*Rn+`lcZp0_@8P_g1M(nxM>0DS1g z1Vd%bVb$Cxs@3t*5*E*;HW0RA&aU@Q?1HvPsbnNdKnXbgOPnIe_*td>(8lM?BC!(7 zkYpd6)>jx)$@te_lv(33pamwLsR|yv%Ii2mcD`y=W;=4 zn|j3-EayQ%aoZoC!dbwv^NB=G8&W>-LO0>ig)=sX&v~iqG;Jh~co}#k84x#n?TJv< zsLv$`rM8JBa5hXz`JPgILOIFh+Icy6S>4c{)T#hiNqI`gokBj@4o$h?T%jEeUTBE& z$K2t1+46M0Y~8XoSKD{H{06@i+&1|amxi5WwrX=x_Ur6JalJTLk!z7@yCON;L1%(X zeeq#f>Mn-|Nl&iiDsY`IExvdtX#ZYTocZJhgzuGQMF>#N;Pmq{XI<-E?ygb2sZ?-(2Tb>aI}g>v8P5t@}Nfk0sJ?H9(e_le|BeJ+PFJ=U(41Oxt60aahMtGPG?A2U|DS0 zo}TEQ+8+NY=G4Zii5_7%BgZ^vcg`$^ikT0Wt}?9JUv;{wH@x+;=^Nq%dt7?<<*fYp z`!>5->i5D~mCwRXVVmY2guav^SB-)+B2G7RiJwBRkoj%Xrv`7V7X()&Hv|{9*%xl- z>wGyA^wA(n#Wavd-{jBJ|3K;sH6i$uQN2YCMUBI?J71-SW(HQZRlH#}H$UIq$Lr#~ zm`pV8$PUdV)OJ_*E?qo*%_h5XUxHym)8(GaF#^fUj+f_oS3FET*dDVU?|Al|hbDGU zP&+*-E*hR$vRRlc!GjxRS!E3oS=4@|A*H09?K@{(X+6lvrAe8|%*i!V#&d)@?K$dP z>*nT7;LV+TS$kuctnlUt3Zr_WXi)!YNkhrganB%)p!~3$Zw4yY|KJM1=@IEFuGHf5 z#k8x@g0vC!FIDaL-^>!!Ya-;8ZMR=Vh@m!l&TLbDDV3vqK;tK(S;t{Z=laC>R;hl)So-w^ zhw9TTLih0Cf)|#+!&+NSHS6a%|_xNmM(Br=aFKXYIH_RTTzSKxL^KyPzYa#4guQKJdLt9}5zT8EAqXYbgZqz~5gHe23 z4er2ObbsK==LM~GCpX5_aBsWtZ{O?UM|sZ-K7Mk|Pb*1%A)u9iKk$&KOh^lG7lx#F9P~K8OC`;Z z$Kc&4|8d)vKRV>;8yJndGwon z+-BvyAB3rs)%SIyJVR@2j?L2AznpxZr0%a#RW};@o^KcF+?bnm^xDz2b2h?>5Eh-% zcdM@Mt~3>IcyS93!x+ip(0j6`G9&LZ4(yVDVlnb~d;j&AqaR&9<+nWTd-|$B<9Ow) z)C7*L+m5LV=fMi8dD#3K;in6QIh%bq+tpUC#tM-iiQ!EKNuIfRIXkd33&STVc%x-vF9C@GD3a!`_u$kwdR_su)a7{`pv)i_F*i^XS$m49Xjvdt6lj=8MYj2 z7t2^4#N9ebpCHW(raVo`S>GKe_O=P%9RC?o-x2oi$2&crtLz%#vq_Cgn$G)8eC?{) z9}#m?Dv>f@9Qu5slcH`d)%GlZUi(ww^YMz6JEVH*z>mZAhg+X|D+uMK&*ZM8uD&@l z*lyQs*P&&rbulSNQ&U4uz2<8`OUhvJ-Qo?cFJnDc*%M)^&kk=?vl zUXBhJBAu#7_@$#4MP-Q40AOMk#UKzvNi2vzDTqwNLWPg&pb&BZ7V2r>h;U?Bk%Gy% zXeP-u+R2R=9ZEC~fSQ>?OrkI%15^@=0EwbfXknNrEc8#i7}5Bb7!HN}slp1yLjNM= z<>&&jqBBX5J$fh@5rIHJ_899S{S6HWdyM_{cSBGJ6bg(QA(aHO%ZF&u${qfjuB1}uzCV-ccYv@osT z7OY8OL?)TRBGYM*Uls}e^l%myDkA$=6jX+z<3EOJVSndJlr?x1fdNPAA>dT%uh9O~ z4rAd-|4-vzwZq)l3=$kq3ZsWJiJ~nD)cOq;rSHEx`lTphgK=hx4iGc~#hOkGr;=za zduuFIw4xV44!{^0=@Zasq#;ZnWuOn!C!qsj#t5__3}t{aMx%}PAc-WS-*)~DZ;L`A z(P#r>0~Ep%iL|jcvbILr+9Hv*XoSTcD_g_gSbJI+i$Eiie)}eieE*F#`mb1w6_Z3@ z(V1>^I^}mExCGN#^srz$1A;>989;V85{P8lFUihdyYyGk)+8o5f)rrOq*Eb(rWr&2 z7Yc?3Muvua2q>5+R1!>|K+=a9?J*+4i~|D$_5`2}4MfpF|HcRWujzw}NWp*A%74_( zZ;_}Ve+~a>2GQi7b|TS4J;W3>i@o2>w5ZW!T^!tOR##U=!lp!QVCxG5IbdW zZQ&NxGwBVYH9%w&Dpyz8?Jt!X;LjD=PMOal+F7sxqa)xe-K#$|kF(8Hh3{8C#((j0 zN9ixM+15_J6LL=Ki%(yuTQe}nEom`@1ErDjOBDw?-X(#$&VZtVXzg5e@ee<+U(J9R zD*{7upjOtvbWvB0vv!lzJ%z`&{0DYvVZ}M|gJMIysz#UcfB}+jgt%f~3<~hzWnjZW zrz&++-lAq{fY-SnU|pyM}IaJy#0_><015!Qk1uwh=Yy# zg|$qr7i-pcP{t>H0Rv$*j+Aa9P`vJt<0$T%7#?&5e9Ctx#CJ$(-3&L>6GfTRs@uX} zci39dni*L_Pe2$bdMBP9mrB(=mnYLS9S)$xOtoP^cI_KgRkwfyfnQ-lyrXRW#zpXX z+^XrCCWpKmc`V5bN*vvk5tF>!-j6%Je@#XWzR#LQ>kR0XM*GR|ca5mQHXw3QPQcsb z3yVF03Kyy`R)Q%SGL>cXKZIGbD%T4mQ#%S4=VnJ!Ll4A`;!>o~?l?FU{;<5QPMia& lYZjRJtlsJ_1}#Y;fGIzzxBCUtF~9x@_BKw|<(7v}{vXJZjPn2h literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_47.png b/assets/dolphin/external/L1_Mods_128x64/frame_47.png new file mode 100644 index 0000000000000000000000000000000000000000..38610bb4b4a9aa68f97442f901f3318941a2305a GIT binary patch literal 4326 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 Lomr`g$L0S4vAUb6 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_48.png b/assets/dolphin/external/L1_Mods_128x64/frame_48.png new file mode 100644 index 0000000000000000000000000000000000000000..7f6b4b29a38e935cdf69f158929d29cf0de5091e GIT binary patch literal 4313 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Mods_128x64/meta.txt b/assets/dolphin/external/L1_Mods_128x64/meta.txt index 0225c7e559..3164fe8d8b 100644 --- a/assets/dolphin/external/L1_Mods_128x64/meta.txt +++ b/assets/dolphin/external/L1_Mods_128x64/meta.txt @@ -3,12 +3,12 @@ Version: 1 Width: 128 Height: 64 -Passive frames: 23 +Passive frames: 31 Active frames: 18 -Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 +Frames order: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 Active cycles: 1 Frame rate: 2 Duration: 3600 Active cooldown: 7 -Bubble slots: 0 \ No newline at end of file +Bubble slots: 0 From 002e5cd9de36ce44f09080413c6c860eda43271e Mon Sep 17 00:00:00 2001 From: Guacamolie <140027638+vanguacamolie@users.noreply.github.com> Date: Mon, 1 Apr 2024 14:49:51 +0200 Subject: [PATCH 06/16] Bad USB: fix crash when selecting a keyboard layout (#3555) Commit 6de2934 (BadUSB: BLE, media keys, Fn/Globe key commands (#3403), 2024-03-25) changed the life-cycle of the bad_usb_script object, so that the bad_usb_script is allocated when entering the work scene, and freed when going to the config scene. It also made it so that the keyboard layout always gets reloaded when entering the work scene. The logic of the layout config scene, however, assumes that it still needs to reload the keyboard layout after selecting it. The keyboard layout data is stored within bad_usb_script however, which is NULL when within the layout config scene. The fix is simple. Since we are now reload the keyboard layout anyway when entering the work scene, we can just remove this extra call. Resolves: #3552 --- applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c | 1 - 1 file changed, 1 deletion(-) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c index 2dbacbe12f..a5d0df94c3 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c @@ -31,7 +31,6 @@ void bad_usb_scene_config_layout_on_enter(void* context) { BadUsbApp* bad_usb = context; if(bad_usb_layout_select(bad_usb)) { - bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); scene_manager_search_and_switch_to_previous_scene(bad_usb->scene_manager, BadUsbSceneWork); } else { scene_manager_previous_scene(bad_usb->scene_manager); From 9f71be6fef632d1e50576ab2bfff7023d508b450 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 1 Apr 2024 14:42:47 +0100 Subject: [PATCH 07/16] IR: Remember OTG state (#3549) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/infrared/infrared_app.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/applications/main/infrared/infrared_app.c b/applications/main/infrared/infrared_app.c index 5a83dfbd75..93311716a1 100644 --- a/applications/main/infrared/infrared_app.c +++ b/applications/main/infrared/infrared_app.c @@ -13,11 +13,12 @@ #define INFRARED_TASK_STACK_SIZE (2048UL) #define INFRARED_SETTINGS_PATH INT_PATH(".infrared.settings") -#define INFRARED_SETTINGS_VERSION (0) +#define INFRARED_SETTINGS_VERSION (1) #define INFRARED_SETTINGS_MAGIC (0x1F) typedef struct { - uint8_t tx_pin; + FuriHalInfraredTxPin tx_pin; + bool otg_enabled; } InfraredSettings; static const NotificationSequence* @@ -488,11 +489,15 @@ static void infrared_load_settings(InfraredApp* infrared) { } infrared_set_tx_pin(infrared, settings.tx_pin); + if(settings.tx_pin < FuriHalInfraredTxPinMax) { + infrared_enable_otg(infrared, settings.otg_enabled); + } } void infrared_save_settings(InfraredApp* infrared) { InfraredSettings settings = { .tx_pin = infrared->app_state.tx_pin, + .otg_enabled = infrared->app_state.is_otg_enabled, }; if(!saved_struct_save( From f426c44811a7829970f87cd93d505c1c73015d05 Mon Sep 17 00:00:00 2001 From: RebornedBrain <138568282+RebornedBrain@users.noreply.github.com> Date: Mon, 1 Apr 2024 18:02:11 +0300 Subject: [PATCH 08/16] [FL-3801] Mifare Ultralight naming fix (#3551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed some UI mismatches in plugin and nfc app * Fixed nameing mismatches in mosgortrans_util * Fix ultralight naming display * Fix naming for Ultralight on read screen * fbt: fixed regression with pvs-reports autoopen * Revert st25tb_render.c Co-authored-by: gornekich Co-authored-by: あく Co-authored-by: hedger --- .../nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c | 2 ++ scripts/fbt_tools/pvsstudio.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index 0fe5a7664e..bd2015889d 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -247,6 +247,8 @@ static void nfc_scene_read_success_on_enter_mf_ultralight(NfcApp* instance) { furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); + furi_string_replace(temp_str, "Mifare", "MIFARE"); + nfc_render_mf_ultralight_info(data, NfcProtocolFormatTypeShort, temp_str); } diff --git a/scripts/fbt_tools/pvsstudio.py b/scripts/fbt_tools/pvsstudio.py index d74aae7686..290531321d 100644 --- a/scripts/fbt_tools/pvsstudio.py +++ b/scripts/fbt_tools/pvsstudio.py @@ -32,7 +32,7 @@ def atexist_handler(): for bf in GetBuildFailures(): for node in Flatten(bf.node): - if node.exists and "pvs" in node.name and node.name.endswith(".html"): + if node.exists and "pvs" in node.path and node.name.endswith(".html"): # macOS if sys.platform == "darwin": subprocess.run(["open", node.abspath]) From 096a26b07dfa223fb9bcd7995e3397a3984631aa Mon Sep 17 00:00:00 2001 From: hedger Date: Mon, 1 Apr 2024 21:02:45 +0400 Subject: [PATCH 09/16] fbt: added -Wstrict-prototypes for main firmware (#3557) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: added -Wstrict-prototypes for main firmware (excludes extapps) * unit_tests: fixed declarations using strict prototypes * furi_hal: ble: changed gap stop log level to debug Co-authored-by: あく --- .../debug/unit_tests/furi/furi_test.c | 8 +-- applications/debug/unit_tests/test_index.c | 50 +++++++++---------- .../services/notification/notification_app.c | 4 +- scripts/fbt_tools/fbt_apps.py | 2 +- site_scons/cc.scons | 1 + site_scons/extapps.scons | 3 ++ targets/f7/ble_glue/ble_glue.c | 2 +- targets/f7/ble_glue/gap.c | 4 +- targets/f7/ble_glue/hw_ipcc.c | 16 +++--- targets/f7/furi_hal/furi_hal_infrared.c | 4 +- targets/f7/furi_hal/furi_hal_interrupt.c | 4 +- targets/f7/furi_hal/furi_hal_os.c | 2 +- 12 files changed, 52 insertions(+), 48 deletions(-) diff --git a/applications/debug/unit_tests/furi/furi_test.c b/applications/debug/unit_tests/furi/furi_test.c index 2f271c305c..e287f9927f 100644 --- a/applications/debug/unit_tests/furi/furi_test.c +++ b/applications/debug/unit_tests/furi/furi_test.c @@ -4,11 +4,11 @@ #include "../minunit.h" // v2 tests -void test_furi_create_open(); -void test_furi_concurrent_access(); -void test_furi_pubsub(); +void test_furi_create_open(void); +void test_furi_concurrent_access(void); +void test_furi_pubsub(void); -void test_furi_memmgr(); +void test_furi_memmgr(void); static int foo = 0; diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index eb475fada4..5d0282bd77 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -8,31 +8,31 @@ #define TAG "UnitTests" -int run_minunit_test_furi(); -int run_minunit_test_furi_hal(); -int run_minunit_test_furi_hal_crypto(); -int run_minunit_test_furi_string(); -int run_minunit_test_infrared(); -int run_minunit_test_rpc(); -int run_minunit_test_manifest(); -int run_minunit_test_flipper_format(); -int run_minunit_test_flipper_format_string(); -int run_minunit_test_stream(); -int run_minunit_test_storage(); -int run_minunit_test_subghz(); -int run_minunit_test_dirwalk(); -int run_minunit_test_power(); -int run_minunit_test_protocol_dict(); -int run_minunit_test_lfrfid_protocols(); -int run_minunit_test_nfc(); -int run_minunit_test_bit_lib(); -int run_minunit_test_datetime(); -int run_minunit_test_float_tools(); -int run_minunit_test_bt(); -int run_minunit_test_dialogs_file_browser_options(); -int run_minunit_test_expansion(); - -typedef int (*UnitTestEntry)(); +int run_minunit_test_furi(void); +int run_minunit_test_furi_hal(void); +int run_minunit_test_furi_hal_crypto(void); +int run_minunit_test_furi_string(void); +int run_minunit_test_infrared(void); +int run_minunit_test_rpc(void); +int run_minunit_test_manifest(void); +int run_minunit_test_flipper_format(void); +int run_minunit_test_flipper_format_string(void); +int run_minunit_test_stream(void); +int run_minunit_test_storage(void); +int run_minunit_test_subghz(void); +int run_minunit_test_dirwalk(void); +int run_minunit_test_power(void); +int run_minunit_test_protocol_dict(void); +int run_minunit_test_lfrfid_protocols(void); +int run_minunit_test_nfc(void); +int run_minunit_test_bit_lib(void); +int run_minunit_test_datetime(void); +int run_minunit_test_float_tools(void); +int run_minunit_test_bt(void); +int run_minunit_test_dialogs_file_browser_options(void); +int run_minunit_test_expansion(void); + +typedef int (*UnitTestEntry)(void); typedef struct { const char* name; diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index 514a37f000..8e183f74e1 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -24,9 +24,9 @@ static const uint8_t reset_display_mask = 1 << 5; static const uint8_t reset_blink_mask = 1 << 6; static void notification_vibro_on(bool force); -static void notification_vibro_off(); +static void notification_vibro_off(void); static void notification_sound_on(float freq, float volume, bool force); -static void notification_sound_off(); +static void notification_sound_off(void); static uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value); static uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value); diff --git a/scripts/fbt_tools/fbt_apps.py b/scripts/fbt_tools/fbt_apps.py index 7e0aec5ea6..3a17a79a34 100644 --- a/scripts/fbt_tools/fbt_apps.py +++ b/scripts/fbt_tools/fbt_apps.py @@ -44,7 +44,7 @@ def __init__(self, buildset: AppBuildset, autorun_app: str = ""): def get_app_ep_forward(self, app: FlipperApplication): if app.apptype == FlipperAppType.STARTUP: - return f"extern void {app.entry_point}();" + return f"extern void {app.entry_point}(void);" return f"extern int32_t {app.entry_point}(void* p);" def get_app_descr(self, app: FlipperApplication): diff --git a/site_scons/cc.scons b/site_scons/cc.scons index 507cd2d12c..603ec621c6 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -4,6 +4,7 @@ Import("ENV") ENV.AppendUnique( CFLAGS=[ "-std=gnu2x", + "-Wstrict-prototypes", ], CXXFLAGS=[ "-std=c++20", diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 22d0be8677..b67ce96218 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -48,6 +48,9 @@ appenv.AppendUnique( "stdc++", "supc++", ], + CFLAGS=[ + "-Wno-strict-prototypes", + ], ) diff --git a/targets/f7/ble_glue/ble_glue.c b/targets/f7/ble_glue/ble_glue.c index ecc2f83c51..c50360361f 100644 --- a/targets/f7/ble_glue/ble_glue.c +++ b/targets/f7/ble_glue/ble_glue.c @@ -49,7 +49,7 @@ static BleGlue* ble_glue = NULL; // static int32_t ble_glue_shci_thread(void* argument); static void ble_sys_status_not_callback(SHCI_TL_CmdStatus_t status); static void ble_sys_user_event_callback(void* pPayload); -static void ble_glue_clear_shared_memory(); +static void ble_glue_clear_shared_memory(void); void ble_glue_set_key_storage_changed_callback( BleGlueKeyStorageChangedCallback callback, diff --git a/targets/f7/ble_glue/gap.c b/targets/f7/ble_glue/gap.c index fded66a9b4..8f4299c701 100644 --- a/targets/f7/ble_glue/gap.c +++ b/targets/f7/ble_glue/gap.c @@ -375,7 +375,7 @@ static void gap_advertise_start(GapState new_state) { uint16_t min_interval; uint16_t max_interval; - FURI_LOG_I(TAG, "Start: %d", new_state); + FURI_LOG_D(TAG, "Start: %d", new_state); if(new_state == GapStateAdvFast) { min_interval = 0x80; // 80 ms @@ -420,7 +420,7 @@ static void gap_advertise_start(GapState new_state) { } static void gap_advertise_stop(void) { - FURI_LOG_I(TAG, "Stop"); + FURI_LOG_D(TAG, "Stop"); tBleStatus ret; if(gap->state > GapStateIdle) { if(gap->state == GapStateConnected) { diff --git a/targets/f7/ble_glue/hw_ipcc.c b/targets/f7/ble_glue/hw_ipcc.c index 6a3aace5a0..4daaa7e49d 100644 --- a/targets/f7/ble_glue/hw_ipcc.c +++ b/targets/f7/ble_glue/hw_ipcc.c @@ -15,14 +15,14 @@ (LL_C2_IPCC_IsActiveFlag_CHx(IPCC, channel) && \ LL_C1_IPCC_IsEnabledReceiveChannel(IPCC, channel)) -static void (*FreeBufCb)(); +static void (*FreeBufCb)(void); -static void HW_IPCC_BLE_EvtHandler(); -static void HW_IPCC_BLE_AclDataEvtHandler(); -static void HW_IPCC_MM_FreeBufHandler(); -static void HW_IPCC_SYS_CmdEvtHandler(); -static void HW_IPCC_SYS_EvtHandler(); -static void HW_IPCC_TRACES_EvtHandler(); +static void HW_IPCC_BLE_EvtHandler(void); +static void HW_IPCC_BLE_AclDataEvtHandler(void); +static void HW_IPCC_MM_FreeBufHandler(void); +static void HW_IPCC_SYS_CmdEvtHandler(void); +static void HW_IPCC_SYS_EvtHandler(void); +static void HW_IPCC_TRACES_EvtHandler(void); void HW_IPCC_Rx_Handler(void) { if(HW_IPCC_RX_PENDING(HW_IPCC_SYSTEM_EVENT_CHANNEL)) { @@ -134,7 +134,7 @@ static void HW_IPCC_SYS_EvtHandler(void) { LL_C1_IPCC_ClearFlag_CHx(IPCC, HW_IPCC_SYSTEM_EVENT_CHANNEL); } -void HW_IPCC_MM_SendFreeBuf(void (*cb)()) { +void HW_IPCC_MM_SendFreeBuf(void (*cb)(void)) { if(LL_C1_IPCC_IsActiveFlag_CHx(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL)) { FreeBufCb = cb; LL_C1_IPCC_EnableTransmitChannel(IPCC, HW_IPCC_MM_RELEASE_BUFFER_CHANNEL); diff --git a/targets/f7/furi_hal/furi_hal_infrared.c b/targets/f7/furi_hal/furi_hal_infrared.c index bbb00198e3..6f2210cc1d 100644 --- a/targets/f7/furi_hal/furi_hal_infrared.c +++ b/targets/f7/furi_hal/furi_hal_infrared.c @@ -92,8 +92,8 @@ static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polar static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num); static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num); static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void); -static void furi_hal_infrared_tx_dma_polarity_isr(); -static void furi_hal_infrared_tx_dma_isr(); +static void furi_hal_infrared_tx_dma_polarity_isr(void*); +static void furi_hal_infrared_tx_dma_isr(void*); static void furi_hal_infrared_tim_rx_isr(void* context) { UNUSED(context); diff --git a/targets/f7/furi_hal/furi_hal_interrupt.c b/targets/f7/furi_hal/furi_hal_interrupt.c index a0ce2f6656..5c2c315ef4 100644 --- a/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/targets/f7/furi_hal/furi_hal_interrupt.c @@ -305,8 +305,8 @@ void DebugMon_Handler(void) { extern usbd_device udev; -extern void HW_IPCC_Tx_Handler(); -extern void HW_IPCC_Rx_Handler(); +extern void HW_IPCC_Tx_Handler(void); +extern void HW_IPCC_Rx_Handler(void); void SysTick_Handler(void) { furi_hal_os_tick(); diff --git a/targets/f7/furi_hal/furi_hal_os.c b/targets/f7/furi_hal/furi_hal_os.c index 4738faa7cc..ba28a141f3 100644 --- a/targets/f7/furi_hal/furi_hal_os.c +++ b/targets/f7/furi_hal/furi_hal_os.c @@ -51,7 +51,7 @@ void furi_hal_os_timer_callback(void) { } #endif -extern void xPortSysTickHandler(); +extern void xPortSysTickHandler(void); static volatile uint32_t furi_hal_os_skew; From d32f1955578782efd2c9735476d8c6048c54043f Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 2 Apr 2024 15:30:42 +0400 Subject: [PATCH 10/16] fbt: fixed missing FBT_FAP_DEBUG_ELF_ROOT to dist env (#3563) --- SConstruct | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SConstruct b/SConstruct index a57714a3ce..532c37b7f5 100644 --- a/SConstruct +++ b/SConstruct @@ -230,6 +230,7 @@ firmware_debug = distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", + FBT_FAP_DEBUG_ELF_ROOT=firmware_env["FBT_FAP_DEBUG_ELF_ROOT"], ) distenv.Depends(firmware_debug, firmware_flash) @@ -239,6 +240,7 @@ distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", GDBREMOTE="${BLACKMAGIC_ADDR}", + FBT_FAP_DEBUG_ELF_ROOT=firmware_env["FBT_FAP_DEBUG_ELF_ROOT"], ) # Debug alien elf From 5793d5271c22a4de58f894b349afa6ce8169f57c Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Thu, 4 Apr 2024 03:01:41 -0700 Subject: [PATCH 11/16] Hide unlock with reader for MFU-C (#3553) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../nfc_scene_mf_ultralight_unlock_menu.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c index 4d97040ee2..a23e4b306a 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_unlock_menu.c @@ -20,12 +20,17 @@ void nfc_scene_mf_ultralight_unlock_menu_on_enter(void* context) { uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu); if(nfc_device_get_protocol(nfc->nfc_device) == NfcProtocolMfUltralight) { - submenu_add_item( - submenu, - "Unlock With Reader", - SubmenuIndexMfUlUnlockMenuReader, - nfc_scene_mf_ultralight_unlock_menu_submenu_callback, - nfc); + const MfUltralightData* mfu_data = + nfc_device_get_data(nfc->nfc_device, NfcProtocolMfUltralight); + // Hide for MFU-C since it uses 3DES + if(mfu_data->type != MfUltralightTypeMfulC) { + submenu_add_item( + submenu, + "Unlock With Reader", + SubmenuIndexMfUlUnlockMenuReader, + nfc_scene_mf_ultralight_unlock_menu_submenu_callback, + nfc); + } } submenu_add_item( submenu, From 54312574703dd90641f22c9812a1e6a9831921a0 Mon Sep 17 00:00:00 2001 From: Sergei Gavrilov Date: Thu, 4 Apr 2024 22:42:58 +1000 Subject: [PATCH 12/16] [FL-3765][FL-3737] Desktop: ensure that animation is unloaded before app start (#3569) * Desktop: ensure that animation is unloaded before app start * Desktop: unload animation only if it is loaded --- applications/services/desktop/desktop.c | 8 +++++++- applications/services/desktop/desktop_i.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 74979d1284..49aa04e35e 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -34,10 +34,12 @@ static void desktop_loader_callback(const void* message, void* context) { if(event->type == LoaderEventTypeApplicationStarted) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalBeforeAppStarted); + furi_check(furi_semaphore_acquire(desktop->animation_semaphore, 3000) == FuriStatusOk); } else if(event->type == LoaderEventTypeApplicationStopped) { view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopGlobalAfterAppFinished); } } + static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) { UNUSED(context); furi_assert(canvas); @@ -120,8 +122,11 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { switch(event) { case DesktopGlobalBeforeAppStarted: - animation_manager_unload_and_stall_animation(desktop->animation_manager); + if(animation_manager_is_animation_loaded(desktop->animation_manager)) { + animation_manager_unload_and_stall_animation(desktop->animation_manager); + } desktop_auto_lock_inhibit(desktop); + furi_semaphore_release(desktop->animation_semaphore); return true; case DesktopGlobalAfterAppFinished: animation_manager_load_and_continue_animation(desktop->animation_manager); @@ -270,6 +275,7 @@ void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) { Desktop* desktop_alloc(void) { Desktop* desktop = malloc(sizeof(Desktop)); + desktop->animation_semaphore = furi_semaphore_alloc(1, 0); desktop->animation_manager = animation_manager_alloc(); desktop->gui = furi_record_open(RECORD_GUI); desktop->scene_thread = furi_thread_alloc(); diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index f6eeef6b1b..c0b29f922d 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -80,6 +80,8 @@ struct Desktop { bool time_format_12 : 1; // 1 - 12 hour, 0 - 24H bool in_transition : 1; + + FuriSemaphore* animation_semaphore; }; Desktop* desktop_alloc(void); From c31b60c7b713b9f42eb06552a424c1291fc4fb5e Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 7 Apr 2024 14:21:25 +0100 Subject: [PATCH 13/16] IR: Fix crash on duty_cycle=1 (#3568) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * IR: Fix crash on duty_cycle=1 * Infrared: use float around duty_cycle Co-authored-by: あく --- lib/infrared/worker/infrared_worker.c | 2 +- targets/f7/furi_hal/furi_hal_infrared.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/infrared/worker/infrared_worker.c b/lib/infrared/worker/infrared_worker.c index a867542e01..89f351eb92 100644 --- a/lib/infrared/worker/infrared_worker.c +++ b/lib/infrared/worker/infrared_worker.c @@ -612,7 +612,7 @@ void infrared_worker_set_raw_signal( furi_check(timings); furi_check(timings_cnt > 0); furi_check((frequency <= INFRARED_MAX_FREQUENCY) && (frequency >= INFRARED_MIN_FREQUENCY)); - furi_check((duty_cycle < 1.0f) && (duty_cycle > 0.0f)); + furi_check((duty_cycle <= 1.0f) && (duty_cycle > 0.0f)); size_t max_copy_num = COUNT_OF(instance->signal.raw.timings) - 1; furi_check(timings_cnt <= max_copy_num); diff --git a/targets/f7/furi_hal/furi_hal_infrared.c b/targets/f7/furi_hal/furi_hal_infrared.c index 6f2210cc1d..a0b166fad4 100644 --- a/targets/f7/furi_hal/furi_hal_infrared.c +++ b/targets/f7/furi_hal/furi_hal_infrared.c @@ -357,7 +357,7 @@ static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cyc if(infrared_tx_output == FuriHalInfraredTxPinInternal) { LL_TIM_OC_SetCompareCH3( INFRARED_DMA_TIMER, - ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle))); + ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1.0f - duty_cycle))); LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3); /* LL_TIM_OCMODE_PWM2 set by DMA */ LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE); @@ -368,7 +368,7 @@ static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cyc } else if(infrared_tx_output == FuriHalInfraredTxPinExtPA7) { LL_TIM_OC_SetCompareCH1( INFRARED_DMA_TIMER, - ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1 - duty_cycle))); + ((LL_TIM_GetAutoReload(INFRARED_DMA_TIMER) + 1) * (1.0f - duty_cycle))); LL_TIM_OC_EnablePreload(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1); /* LL_TIM_OCMODE_PWM2 set by DMA */ LL_TIM_OC_SetMode(INFRARED_DMA_TIMER, LL_TIM_CHANNEL_CH1, LL_TIM_OCMODE_FORCED_INACTIVE); @@ -609,7 +609,7 @@ static void furi_hal_infrared_async_tx_free_resources(void) { } void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { - if((duty_cycle > 1) || (duty_cycle <= 0) || (freq > INFRARED_MAX_FREQUENCY) || + if((duty_cycle > 1.0f) || (duty_cycle <= 0.0f) || (freq > INFRARED_MAX_FREQUENCY) || (freq < INFRARED_MIN_FREQUENCY) || (infrared_tim_tx.data_callback == NULL)) { furi_crash(); } From 6b120a3b09c08cb4c4f7976ab756e0fb67e9cb3b Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 7 Apr 2024 14:49:00 +0100 Subject: [PATCH 14/16] Furi: Add "out of memory" and "malloc(0)" crash messages (#3574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- furi/core/memmgr_heap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/furi/core/memmgr_heap.c b/furi/core/memmgr_heap.c index 24bd327fd2..3f62b518c2 100644 --- a/furi/core/memmgr_heap.c +++ b/furi/core/memmgr_heap.c @@ -486,7 +486,7 @@ void* pvPortMalloc(size_t xWantedSize) { configASSERT((((size_t)pvReturn) & (size_t)portBYTE_ALIGNMENT_MASK) == 0); - furi_check(pvReturn); + furi_check(pvReturn, xWantedSize ? "out of memory" : "malloc(0)"); pvReturn = memset(pvReturn, 0, to_wipe); return pvReturn; } From 16b34c6e4de4d983ca938b0803b04339067fc767 Mon Sep 17 00:00:00 2001 From: WillyJL <49810075+Willy-JL@users.noreply.github.com> Date: Sun, 7 Apr 2024 15:11:23 +0100 Subject: [PATCH 15/16] Explain RNG differences, add FURI_HAL_RANDOM_MAX (#3565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Explain RNG differences, add FURI_HAL_RANDOM_MAX * Mark FURI_HAL_RANDOM_MAX unsigned Co-authored-by: あく --- targets/furi_hal_include/furi_hal_random.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/targets/furi_hal_include/furi_hal_random.h b/targets/furi_hal_include/furi_hal_random.h index 051b6f928d..fab62083f1 100644 --- a/targets/furi_hal_include/furi_hal_random.h +++ b/targets/furi_hal_include/furi_hal_random.h @@ -6,12 +6,16 @@ extern "C" { #endif +#define FURI_HAL_RANDOM_MAX 0xFFFFFFFFU + /** Initialize random subsystem */ void furi_hal_random_init(void); /** Get random value + * furi_hal_random_get() gives up to FURI_HAL_RANDOM_MAX + * rand() and random() give up to RAND_MAX * - * @return random value + * @return 32 bit random value (up to FURI_HAL_RANDOM_MAX) */ uint32_t furi_hal_random_get(void); From 27e61eb80826baf367a8826b5296b9c5d5bbca3b Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Sun, 7 Apr 2024 23:47:48 +0900 Subject: [PATCH 16/16] Move crypto1 to helpers, add it to the public API (#3567) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move crypto1 to helpers, add it to the public API * F18 API version bump Co-authored-by: あく --- lib/nfc/SConscript | 1 + .../{protocols/mf_classic => helpers}/crypto1.c | 0 .../{protocols/mf_classic => helpers}/crypto1.h | 0 .../protocols/mf_classic/mf_classic_listener_i.h | 2 +- lib/nfc/protocols/mf_classic/mf_classic_poller_i.h | 2 +- targets/f18/api_symbols.csv | 2 +- targets/f7/api_symbols.csv | 14 +++++++++++++- 7 files changed, 17 insertions(+), 4 deletions(-) rename lib/nfc/{protocols/mf_classic => helpers}/crypto1.c (100%) rename lib/nfc/{protocols/mf_classic => helpers}/crypto1.h (100%) diff --git a/lib/nfc/SConscript b/lib/nfc/SConscript index 41332362c8..82317918bd 100644 --- a/lib/nfc/SConscript +++ b/lib/nfc/SConscript @@ -48,6 +48,7 @@ env.Append( File("helpers/iso14443_crc.h"), File("helpers/iso13239_crc.h"), File("helpers/nfc_data_generator.h"), + File("helpers/crypto1.h"), ], ) diff --git a/lib/nfc/protocols/mf_classic/crypto1.c b/lib/nfc/helpers/crypto1.c similarity index 100% rename from lib/nfc/protocols/mf_classic/crypto1.c rename to lib/nfc/helpers/crypto1.c diff --git a/lib/nfc/protocols/mf_classic/crypto1.h b/lib/nfc/helpers/crypto1.h similarity index 100% rename from lib/nfc/protocols/mf_classic/crypto1.h rename to lib/nfc/helpers/crypto1.h diff --git a/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h b/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h index 5269743b5c..af22b5234f 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_listener_i.h @@ -3,7 +3,7 @@ #include "mf_classic_listener.h" #include #include -#include "crypto1.h" +#include #ifdef __cplusplus extern "C" { diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h index a5af315307..14a7c61fd4 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h @@ -3,7 +3,7 @@ #include "mf_classic_poller.h" #include #include -#include "crypto1.h" +#include #ifdef __cplusplus extern "C" { diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index f6199445d1..72116ccfdf 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,60.4,, +Version,+,60.5,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 6e65b94719..4ddff921bc 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,60.4,, +Version,+,60.5,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -123,6 +123,7 @@ Header,+,lib/music_worker/music_worker.h,, Header,+,lib/nanopb/pb.h,, Header,+,lib/nanopb/pb_decode.h,, Header,+,lib/nanopb/pb_encode.h,, +Header,+,lib/nfc/helpers/crypto1.h,, Header,+,lib/nfc/helpers/iso13239_crc.h,, Header,+,lib/nfc/helpers/iso14443_crc.h,, Header,+,lib/nfc/helpers/nfc_data_generator.h,, @@ -853,6 +854,16 @@ Function,-,coshl,long double,long double Function,-,cosl,long double,long double Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" +Function,+,crypto1_alloc,Crypto1*, +Function,+,crypto1_bit,uint8_t,"Crypto1*, uint8_t, int" +Function,+,crypto1_byte,uint8_t,"Crypto1*, uint8_t, int" +Function,+,crypto1_decrypt,void,"Crypto1*, const BitBuffer*, BitBuffer*" +Function,+,crypto1_encrypt,void,"Crypto1*, uint8_t*, const BitBuffer*, BitBuffer*" +Function,+,crypto1_encrypt_reader_nonce,void,"Crypto1*, uint64_t, uint32_t, uint8_t*, uint8_t*, BitBuffer*, _Bool" +Function,+,crypto1_free,void,Crypto1* +Function,+,crypto1_init,void,"Crypto1*, uint64_t" +Function,+,crypto1_reset,void,Crypto1* +Function,+,crypto1_word,uint32_t,"Crypto1*, uint32_t, int" Function,-,ctermid,char*,char* Function,-,cuserid,char*,char* Function,+,datetime_datetime_to_timestamp,uint32_t,DateTime* @@ -2782,6 +2793,7 @@ Function,+,powf,float,"float, float" Function,-,powl,long double,"long double, long double" Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t" Function,-,printf,int,"const char*, ..." +Function,+,prng_successor,uint32_t,"uint32_t, uint32_t" Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t"