From 4c771b66dcb0dca0552c9a4b187645cf0a7e0b67 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Mon, 7 Aug 2023 12:18:46 +0300 Subject: [PATCH 01/99] [FL-3471] Infrared: buttons move feature rework (#2949) --- applications/main/infrared/infrared.c | 7 + applications/main/infrared/infrared_i.h | 5 +- applications/main/infrared/infrared_remote.c | 14 +- applications/main/infrared/infrared_remote.h | 2 +- .../infrared/scenes/infrared_scene_config.h | 1 - .../infrared/scenes/infrared_scene_edit.c | 4 +- .../infrared_scene_edit_button_select.c | 34 +-- .../scenes/infrared_scene_edit_move.c | 101 ++------ .../scenes/infrared_scene_edit_move_done.c | 48 ---- .../main/infrared/views/infrared_move_view.c | 215 ++++++++++++++++++ .../main/infrared/views/infrared_move_view.h | 25 ++ 11 files changed, 280 insertions(+), 176 deletions(-) delete mode 100644 applications/main/infrared/scenes/infrared_scene_edit_move_done.c create mode 100644 applications/main/infrared/views/infrared_move_view.c create mode 100644 applications/main/infrared/views/infrared_move_view.h diff --git a/applications/main/infrared/infrared.c b/applications/main/infrared/infrared.c index 5957cdb13..fcf45c25c 100644 --- a/applications/main/infrared/infrared.c +++ b/applications/main/infrared/infrared.c @@ -165,6 +165,10 @@ static Infrared* infrared_alloc() { view_dispatcher_add_view( view_dispatcher, InfraredViewStack, view_stack_get_view(infrared->view_stack)); + infrared->move_view = infrared_move_view_alloc(); + view_dispatcher_add_view( + view_dispatcher, InfraredViewMove, infrared_move_view_get_view(infrared->move_view)); + if(app_state->is_debug_enabled) { infrared->debug_view = infrared_debug_view_alloc(); view_dispatcher_add_view( @@ -209,6 +213,9 @@ static void infrared_free(Infrared* infrared) { view_dispatcher_remove_view(view_dispatcher, InfraredViewStack); view_stack_free(infrared->view_stack); + view_dispatcher_remove_view(view_dispatcher, InfraredViewMove); + infrared_move_view_free(infrared->move_view); + if(app_state->is_debug_enabled) { view_dispatcher_remove_view(view_dispatcher, InfraredViewDebugView); infrared_debug_view_free(infrared->debug_view); diff --git a/applications/main/infrared/infrared_i.h b/applications/main/infrared/infrared_i.h index 96932d9bc..6fb6a65c7 100644 --- a/applications/main/infrared/infrared_i.h +++ b/applications/main/infrared/infrared_i.h @@ -30,6 +30,7 @@ #include "scenes/infrared_scene.h" #include "views/infrared_progress_view.h" #include "views/infrared_debug_view.h" +#include "views/infrared_move_view.h" #include "rpc/rpc_app.h" @@ -60,8 +61,6 @@ typedef enum { InfraredEditModeNone, InfraredEditModeRename, InfraredEditModeDelete, - InfraredEditModeMove, - InfraredEditModeMoveSelectDest } InfraredEditMode; typedef struct { @@ -96,6 +95,7 @@ struct Infrared { ViewStack* view_stack; InfraredDebugView* debug_view; + InfraredMoveView* move_view; ButtonPanel* button_panel; Loading* loading; @@ -116,6 +116,7 @@ typedef enum { InfraredViewPopup, InfraredViewStack, InfraredViewDebugView, + InfraredViewMove, } InfraredView; typedef enum { diff --git a/applications/main/infrared/infrared_remote.c b/applications/main/infrared/infrared_remote.c index a04a338ba..70d1b59ef 100644 --- a/applications/main/infrared/infrared_remote.c +++ b/applications/main/infrared/infrared_remote.c @@ -108,19 +108,13 @@ bool infrared_remote_delete_button(InfraredRemote* remote, size_t index) { return infrared_remote_store(remote); } -bool infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest) { +void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest) { furi_assert(index_orig < InfraredButtonArray_size(remote->buttons)); - furi_assert(index_dest <= InfraredButtonArray_size(remote->buttons)); - if(index_orig == index_dest) { - return true; - } + furi_assert(index_dest < InfraredButtonArray_size(remote->buttons)); + InfraredRemoteButton* button; InfraredButtonArray_pop_at(&button, remote->buttons, index_orig); - if(index_orig > index_dest) - InfraredButtonArray_push_at(remote->buttons, index_dest, button); - else - InfraredButtonArray_push_at(remote->buttons, index_dest - 1, button); - return infrared_remote_store(remote); + InfraredButtonArray_push_at(remote->buttons, index_dest, button); } bool infrared_remote_store(InfraredRemote* remote) { diff --git a/applications/main/infrared/infrared_remote.h b/applications/main/infrared/infrared_remote.h index 2640149a4..47aa77e2e 100644 --- a/applications/main/infrared/infrared_remote.h +++ b/applications/main/infrared/infrared_remote.h @@ -23,7 +23,7 @@ bool infrared_remote_find_button_by_name(InfraredRemote* remote, const char* nam bool infrared_remote_add_button(InfraredRemote* remote, const char* name, InfraredSignal* signal); bool infrared_remote_rename_button(InfraredRemote* remote, const char* new_name, size_t index); bool infrared_remote_delete_button(InfraredRemote* remote, size_t index); -bool infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest); +void infrared_remote_move_button(InfraredRemote* remote, size_t index_orig, size_t index_dest); bool infrared_remote_store(InfraredRemote* remote); bool infrared_remote_load(InfraredRemote* remote, FuriString* path); diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h index 36e6ae252..27ef2f3b1 100644 --- a/applications/main/infrared/scenes/infrared_scene_config.h +++ b/applications/main/infrared/scenes/infrared_scene_config.h @@ -8,7 +8,6 @@ ADD_SCENE(infrared, edit_button_select, EditButtonSelect) ADD_SCENE(infrared, edit_rename, EditRename) ADD_SCENE(infrared, edit_rename_done, EditRenameDone) ADD_SCENE(infrared, edit_move, EditMove) -ADD_SCENE(infrared, edit_move_done, EditMoveDone) ADD_SCENE(infrared, learn, Learn) ADD_SCENE(infrared, learn_done, LearnDone) ADD_SCENE(infrared, learn_enter_name, LearnEnterName) diff --git a/applications/main/infrared/scenes/infrared_scene_edit.c b/applications/main/infrared/scenes/infrared_scene_edit.c index 79de04bda..02bba7a3f 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit.c +++ b/applications/main/infrared/scenes/infrared_scene_edit.c @@ -82,9 +82,7 @@ bool infrared_scene_edit_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); consumed = true; } else if(submenu_index == SubmenuIndexMoveButton) { - infrared->app_state.edit_target = InfraredEditTargetButton; - infrared->app_state.edit_mode = InfraredEditModeMove; - scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); + scene_manager_next_scene(scene_manager, InfraredSceneEditMove); consumed = true; } else if(submenu_index == SubmenuIndexDeleteButton) { infrared->app_state.edit_target = InfraredEditTargetButton; diff --git a/applications/main/infrared/scenes/infrared_scene_edit_button_select.c b/applications/main/infrared/scenes/infrared_scene_edit_button_select.c index 7056a2053..5f5a1d8fa 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_button_select.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_button_select.c @@ -11,23 +11,9 @@ void infrared_scene_edit_button_select_on_enter(void* context) { InfraredRemote* remote = infrared->remote; InfraredAppState* app_state = &infrared->app_state; - const char* header = NULL; - switch(infrared->app_state.edit_mode) { - case InfraredEditModeRename: - header = "Rename Button:"; - break; - case InfraredEditModeDelete: - header = "Delete Button:"; - break; - case InfraredEditModeMove: - header = "Select Button to Move:"; - break; - case InfraredEditModeMoveSelectDest: - case InfraredEditModeNone: - default: - header = "Move Button Before:"; - break; - } + const char* header = infrared->app_state.edit_mode == InfraredEditModeRename ? + "Rename Button:" : + "Delete Button:"; submenu_set_header(submenu, header); const size_t button_count = infrared_remote_get_button_count(remote); @@ -40,14 +26,6 @@ void infrared_scene_edit_button_select_on_enter(void* context) { infrared_scene_edit_button_select_submenu_callback, context); } - if(infrared->app_state.edit_mode == InfraredEditModeMoveSelectDest) { - submenu_add_item( - submenu, - "-- Move to the end --", - button_count, - infrared_scene_edit_button_select_submenu_callback, - context); - } if(button_count && app_state->current_button_index != InfraredButtonIndexNone) { submenu_set_selected_item(submenu, app_state->current_button_index); app_state->current_button_index = InfraredButtonIndexNone; @@ -69,12 +47,6 @@ bool infrared_scene_edit_button_select_on_event(void* context, SceneManagerEvent scene_manager_next_scene(scene_manager, InfraredSceneEditRename); } else if(edit_mode == InfraredEditModeDelete) { scene_manager_next_scene(scene_manager, InfraredSceneEditDelete); - } else if(edit_mode == InfraredEditModeMove) { - app_state->current_button_index_move_orig = event.event; - app_state->edit_mode = InfraredEditModeMoveSelectDest; - scene_manager_next_scene(scene_manager, InfraredSceneEditButtonSelect); - } else if(edit_mode == InfraredEditModeMoveSelectDest) { - scene_manager_next_scene(scene_manager, InfraredSceneEditMove); } else { furi_assert(0); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move.c b/applications/main/infrared/scenes/infrared_scene_edit_move.c index 69c7ec41d..370c352db 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_move.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_move.c @@ -1,103 +1,44 @@ #include "../infrared_i.h" -static void infrared_scene_edit_move_dialog_result_callback(DialogExResult result, void* context) { - Infrared* infrared = context; - view_dispatcher_send_custom_event(infrared->view_dispatcher, result); +static void infrared_scene_move_button(uint32_t index_old, uint32_t index_new, void* context) { + InfraredRemote* remote = context; + furi_assert(remote); + infrared_remote_move_button(remote, index_old, index_new); +} + +static const char* infrared_scene_get_btn_name(uint32_t index, void* context) { + InfraredRemote* remote = context; + furi_assert(remote); + InfraredRemoteButton* button = infrared_remote_get_button(remote, index); + return (infrared_remote_button_get_name(button)); } void infrared_scene_edit_move_on_enter(void* context) { Infrared* infrared = context; - DialogEx* dialog_ex = infrared->dialog_ex; InfraredRemote* remote = infrared->remote; - const InfraredEditTarget edit_target = infrared->app_state.edit_target; - if(edit_target == InfraredEditTargetButton) { - int32_t current_button_index = infrared->app_state.current_button_index_move_orig; - furi_assert(current_button_index != InfraredButtonIndexNone); + infrared_move_view_set_callback(infrared->move_view, infrared_scene_move_button); - dialog_ex_set_header(dialog_ex, "Move Button?", 64, 0, AlignCenter, AlignTop); - InfraredRemoteButton* current_button = - infrared_remote_get_button(remote, current_button_index); - InfraredSignal* signal = infrared_remote_button_get_signal(current_button); + uint32_t btn_count = infrared_remote_get_button_count(remote); + infrared_move_view_list_init( + infrared->move_view, btn_count, infrared_scene_get_btn_name, remote); + infrared_move_view_list_update(infrared->move_view); - if(infrared_signal_is_raw(signal)) { - const InfraredRawSignal* raw = infrared_signal_get_raw_signal(signal); - infrared_text_store_set( - infrared, - 0, - "%s\nRAW\n%ld samples", - infrared_remote_button_get_name(current_button), - raw->timings_size); - - } else { - const InfraredMessage* message = infrared_signal_get_message(signal); - infrared_text_store_set( - infrared, - 0, - "%s\n%s\nA=0x%0*lX C=0x%0*lX", - infrared_remote_button_get_name(current_button), - infrared_get_protocol_name(message->protocol), - ROUND_UP_TO(infrared_get_protocol_address_length(message->protocol), 4), - message->address, - ROUND_UP_TO(infrared_get_protocol_command_length(message->protocol), 4), - message->command); - } - } else { - furi_assert(0); - } - - dialog_ex_set_text(dialog_ex, infrared->text_store[0], 64, 31, AlignCenter, AlignCenter); - dialog_ex_set_icon(dialog_ex, 0, 0, NULL); - dialog_ex_set_left_button_text(dialog_ex, "Cancel"); - dialog_ex_set_right_button_text(dialog_ex, "Move"); - dialog_ex_set_result_callback(dialog_ex, infrared_scene_edit_move_dialog_result_callback); - dialog_ex_set_context(dialog_ex, context); - - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewDialogEx); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewMove); } bool infrared_scene_edit_move_on_event(void* context, SceneManagerEvent event) { Infrared* infrared = context; - SceneManager* scene_manager = infrared->scene_manager; bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DialogExResultLeft) { - scene_manager_previous_scene(scene_manager); - consumed = true; - } else if(event.event == DialogExResultRight) { - bool success = false; - InfraredRemote* remote = infrared->remote; - InfraredAppState* app_state = &infrared->app_state; - const InfraredEditTarget edit_target = app_state->edit_target; - - if(edit_target == InfraredEditTargetButton) { - furi_assert(app_state->current_button_index != InfraredButtonIndexNone); - success = infrared_remote_move_button( - remote, - app_state->current_button_index_move_orig, - app_state->current_button_index); - app_state->current_button_index_move_orig = InfraredButtonIndexNone; - app_state->current_button_index = InfraredButtonIndexNone; - } else { - furi_assert(0); - } - - if(success) { - scene_manager_next_scene(scene_manager, InfraredSceneEditMoveDone); - } else { - const uint32_t possible_scenes[] = {InfraredSceneRemoteList, InfraredSceneStart}; - scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, COUNT_OF(possible_scenes)); - } - consumed = true; - } - } + UNUSED(event); + UNUSED(infrared); return consumed; } void infrared_scene_edit_move_on_exit(void* context) { Infrared* infrared = context; - UNUSED(infrared); + InfraredRemote* remote = infrared->remote; + infrared_remote_store(remote); } diff --git a/applications/main/infrared/scenes/infrared_scene_edit_move_done.c b/applications/main/infrared/scenes/infrared_scene_edit_move_done.c deleted file mode 100644 index 9f9b4b80d..000000000 --- a/applications/main/infrared/scenes/infrared_scene_edit_move_done.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "../infrared_i.h" - -void infrared_scene_edit_move_done_on_enter(void* context) { - Infrared* infrared = context; - Popup* popup = infrared->popup; - - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Moved", 83, 19, AlignLeft, AlignBottom); - - popup_set_callback(popup, infrared_popup_closed_callback); - popup_set_context(popup, context); - popup_set_timeout(popup, 1500); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewPopup); -} - -bool infrared_scene_edit_move_done_on_event(void* context, SceneManagerEvent event) { - Infrared* infrared = context; - SceneManager* scene_manager = infrared->scene_manager; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InfraredCustomEventTypePopupClosed) { - const InfraredEditTarget edit_target = infrared->app_state.edit_target; - if(edit_target == InfraredEditTargetButton) { - scene_manager_search_and_switch_to_previous_scene( - scene_manager, InfraredSceneRemote); - } else if(edit_target == InfraredEditTargetRemote) { - const uint32_t possible_scenes[] = {InfraredSceneStart, InfraredSceneRemoteList}; - if(!scene_manager_search_and_switch_to_previous_scene_one_of( - scene_manager, possible_scenes, COUNT_OF(possible_scenes))) { - view_dispatcher_stop(infrared->view_dispatcher); - } - } else { - furi_assert(0); - } - consumed = true; - } - } - - return consumed; -} - -void infrared_scene_edit_move_done_on_exit(void* context) { - Infrared* infrared = context; - UNUSED(infrared); -} diff --git a/applications/main/infrared/views/infrared_move_view.c b/applications/main/infrared/views/infrared_move_view.c new file mode 100644 index 000000000..d838a5f82 --- /dev/null +++ b/applications/main/infrared/views/infrared_move_view.c @@ -0,0 +1,215 @@ +#include "infrared_move_view.h" + +#include +#include + +#include +#include + +#define LIST_ITEMS 4U +#define LIST_LINE_H 13U +#define HEADER_H 12U +#define MOVE_X_OFFSET 5U + +struct InfraredMoveView { + View* view; + InfraredMoveCallback move_cb; + void* cb_context; +}; + +typedef struct { + const char** btn_names; + uint32_t btn_number; + int32_t list_offset; + int32_t item_idx; + bool is_moving; + + InfraredMoveGetItemCallback get_item_cb; +} InfraredMoveViewModel; + +static void infrared_move_view_draw_callback(Canvas* canvas, void* _model) { + InfraredMoveViewModel* model = _model; + + UNUSED(model); + + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned( + canvas, canvas_width(canvas) / 2, 0, AlignCenter, AlignTop, "Select a button to move"); + + bool show_scrollbar = model->btn_number > LIST_ITEMS; + + canvas_set_font(canvas, FontSecondary); + + for(uint32_t i = 0; i < MIN(model->btn_number, LIST_ITEMS); i++) { + int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->btn_number, 0u); + uint8_t x_offset = (model->is_moving && model->item_idx == idx) ? MOVE_X_OFFSET : 0; + uint8_t y_offset = HEADER_H + i * LIST_LINE_H; + uint8_t box_end_x = canvas_width(canvas) - (show_scrollbar ? 6 : 1); + + canvas_set_color(canvas, ColorBlack); + if(model->item_idx == idx) { + canvas_draw_box(canvas, x_offset, y_offset, box_end_x - x_offset, LIST_LINE_H); + + canvas_set_color(canvas, ColorWhite); + canvas_draw_dot(canvas, x_offset, y_offset); + canvas_draw_dot(canvas, x_offset + 1, y_offset); + canvas_draw_dot(canvas, x_offset, y_offset + 1); + canvas_draw_dot(canvas, x_offset, y_offset + LIST_LINE_H - 1); + canvas_draw_dot(canvas, box_end_x - 1, y_offset); + canvas_draw_dot(canvas, box_end_x - 1, y_offset + LIST_LINE_H - 1); + } + canvas_draw_str_aligned( + canvas, x_offset + 3, y_offset + 3, AlignLeft, AlignTop, model->btn_names[idx]); + } + + if(show_scrollbar) { + elements_scrollbar_pos( + canvas, + canvas_width(canvas), + HEADER_H, + canvas_height(canvas) - HEADER_H, + model->item_idx, + model->btn_number); + } +} + +static void update_list_offset(InfraredMoveViewModel* model) { + int32_t bounds = model->btn_number > (LIST_ITEMS - 1) ? 2 : model->btn_number; + + if((model->btn_number > (LIST_ITEMS - 1)) && + (model->item_idx >= ((int32_t)model->btn_number - 1))) { + model->list_offset = model->item_idx - (LIST_ITEMS - 1); + } else if(model->list_offset < model->item_idx - bounds) { + model->list_offset = CLAMP( + model->item_idx - (int32_t)(LIST_ITEMS - 2), (int32_t)model->btn_number - bounds, 0); + } else if(model->list_offset > model->item_idx - bounds) { + model->list_offset = CLAMP(model->item_idx - 1, (int32_t)model->btn_number - bounds, 0); + } +} + +static bool infrared_move_view_input_callback(InputEvent* event, void* context) { + InfraredMoveView* move_view = context; + + bool consumed = false; + + if(((event->type == InputTypeShort || event->type == InputTypeRepeat)) && + ((event->key == InputKeyUp) || (event->key == InputKeyDown))) { + bool is_moving = false; + uint32_t index_old = 0; + uint32_t index_new = 0; + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + is_moving = model->is_moving; + index_old = model->item_idx; + if(event->key == InputKeyUp) { + if(model->item_idx <= 0) { + model->item_idx = model->btn_number; + } + model->item_idx--; + } else if(event->key == InputKeyDown) { + model->item_idx++; + if(model->item_idx >= (int32_t)(model->btn_number)) { + model->item_idx = 0; + } + } + index_new = model->item_idx; + update_list_offset(model); + }, + !is_moving); + if((is_moving) && (move_view->move_cb)) { + move_view->move_cb(index_old, index_new, move_view->cb_context); + infrared_move_view_list_update(move_view); + } + consumed = true; + } + + if((event->key == InputKeyOk) && (event->type == InputTypeShort)) { + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { model->is_moving = !(model->is_moving); }, + true); + consumed = true; + } + return consumed; +} + +static void infrared_move_view_on_exit(void* context) { + furi_assert(context); + InfraredMoveView* move_view = context; + + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + if(model->btn_names) { + free(model->btn_names); + model->btn_names = NULL; + } + model->btn_number = 0; + model->get_item_cb = NULL; + }, + false); + move_view->cb_context = NULL; +} + +void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback) { + furi_assert(move_view); + move_view->move_cb = callback; +} + +void infrared_move_view_list_init( + InfraredMoveView* move_view, + uint32_t item_count, + InfraredMoveGetItemCallback load_cb, + void* context) { + furi_assert(move_view); + move_view->cb_context = context; + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + furi_assert(model->btn_names == NULL); + model->btn_names = malloc(sizeof(char*) * item_count); + model->btn_number = item_count; + model->get_item_cb = load_cb; + }, + false); +} + +void infrared_move_view_list_update(InfraredMoveView* move_view) { + furi_assert(move_view); + with_view_model( + move_view->view, + InfraredMoveViewModel * model, + { + for(uint32_t i = 0; i < model->btn_number; i++) { + if(!model->get_item_cb) break; + model->btn_names[i] = model->get_item_cb(i, move_view->cb_context); + } + }, + true); +} + +InfraredMoveView* infrared_move_view_alloc(void) { + InfraredMoveView* move_view = malloc(sizeof(InfraredMoveView)); + move_view->view = view_alloc(); + view_allocate_model(move_view->view, ViewModelTypeLocking, sizeof(InfraredMoveViewModel)); + view_set_draw_callback(move_view->view, infrared_move_view_draw_callback); + view_set_input_callback(move_view->view, infrared_move_view_input_callback); + view_set_exit_callback(move_view->view, infrared_move_view_on_exit); + view_set_context(move_view->view, move_view); + return move_view; +} + +void infrared_move_view_free(InfraredMoveView* move_view) { + view_free(move_view->view); + free(move_view); +} + +View* infrared_move_view_get_view(InfraredMoveView* move_view) { + return move_view->view; +} diff --git a/applications/main/infrared/views/infrared_move_view.h b/applications/main/infrared/views/infrared_move_view.h new file mode 100644 index 000000000..b9b0cd864 --- /dev/null +++ b/applications/main/infrared/views/infrared_move_view.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +typedef struct InfraredMoveView InfraredMoveView; + +typedef void (*InfraredMoveCallback)(uint32_t index_old, uint32_t index_new, void* context); + +typedef const char* (*InfraredMoveGetItemCallback)(uint32_t index, void* context); + +InfraredMoveView* infrared_move_view_alloc(void); + +void infrared_move_view_free(InfraredMoveView* debug_view); + +View* infrared_move_view_get_view(InfraredMoveView* debug_view); + +void infrared_move_view_set_callback(InfraredMoveView* move_view, InfraredMoveCallback callback); + +void infrared_move_view_list_init( + InfraredMoveView* move_view, + uint32_t item_count, + InfraredMoveGetItemCallback load_cb, + void* context); + +void infrared_move_view_list_update(InfraredMoveView* move_view); \ No newline at end of file From 1e4af1d550ee21f848cb0f34bbe9c9dfcbbc794f Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Mon, 7 Aug 2023 19:28:47 +0900 Subject: [PATCH 02/99] FDX-B temperature in system units (#2941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FDX-B temperature now uses system units * LF-RFID: wrap floats in fdx-b temperature conversion Co-authored-by: あく --- lib/lfrfid/protocols/protocol_fdx_b.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/lfrfid/protocols/protocol_fdx_b.c b/lib/lfrfid/protocols/protocol_fdx_b.c index dd54cffb0..04386a675 100644 --- a/lib/lfrfid/protocols/protocol_fdx_b.c +++ b/lib/lfrfid/protocols/protocol_fdx_b.c @@ -4,6 +4,7 @@ #include #include #include "lfrfid_protocols.h" +#include #define FDX_B_ENCODED_BIT_SIZE (128) #define FDX_B_ENCODED_BYTE_SIZE (((FDX_B_ENCODED_BIT_SIZE) / 8)) @@ -323,8 +324,12 @@ void protocol_fdx_b_render_brief_data(ProtocolFDXB* protocol, FuriString* result float temperature; if(protocol_fdx_b_get_temp(protocol->data, &temperature)) { - float temperature_c = (temperature - 32) / 1.8; - furi_string_cat_printf(result, "T: %.2fC", (double)temperature_c); + 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); + } else { + furi_string_cat_printf(result, "T: %.2fF", (double)temperature); + } } else { furi_string_cat_printf(result, "T: ---"); } From ab006361ccdbdb090a41b137184f5ced15f81992 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Tue, 8 Aug 2023 15:35:51 +0900 Subject: [PATCH 03/99] Update the application catalog link in the readme (#2959) --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index b60d66fbd..bbaf9603e 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -26,7 +26,7 @@ If you've found an issue and want to report it, please check our [Issues](https: ## I want to contribute code -Before opening a PR, please confirm that your changes must be contained in the firmware. Many ideas can easily be implemented as external applications and published in the Flipper Application Catalog (coming soon). If you are unsure, reach out to us on the [Discord Server](https://flipp.dev/discord) or the [Issues](https://github.com/flipperdevices/flipperzero-firmware/issues) page, and we'll help you find the right place for your code. +Before opening a PR, please confirm that your changes must be contained in the firmware. Many ideas can easily be implemented as external applications and published in the [Flipper Application Catalog](https://github.com/flipperdevices/flipper-application-catalog). If you are unsure, reach out to us on the [Discord Server](https://flipp.dev/discord) or the [Issues](https://github.com/flipperdevices/flipperzero-firmware/issues) page, and we'll help you find the right place for your code. Also, please read our [Contribution Guide](/CONTRIBUTING.md) and our [Coding Style](/CODING_STYLE.md), and make sure your code is compatible with our [Project License](/LICENSE). From e9f1af44f2f6d1fb703a937470c2efe86bdd9e6a Mon Sep 17 00:00:00 2001 From: Derek Jamison Date: Tue, 8 Aug 2023 03:03:39 -0500 Subject: [PATCH 04/99] Fixes #2957 - subghz decode_raw (#2958) * Fixes #2957 - subghz decode_raw * SubGhz: proper decode_raw fix Co-authored-by: Aleksandr Kutuzov --- lib/subghz/subghz_file_encoder_worker.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c index 519ff3fdc..3c045c269 100644 --- a/lib/subghz/subghz_file_encoder_worker.c +++ b/lib/subghz/subghz_file_encoder_worker.c @@ -220,7 +220,9 @@ bool subghz_file_encoder_worker_start( furi_stream_buffer_reset(instance->stream); furi_string_set(instance->file_path, file_path); - instance->device = subghz_devices_get_by_name(radio_device_name); + if(radio_device_name) { + instance->device = subghz_devices_get_by_name(radio_device_name); + } instance->worker_running = true; furi_thread_start(instance->thread); From 8936c024874e04d07269b09b86a8d8eb7f97d3e8 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:16:35 +0300 Subject: [PATCH 05/99] NFC App: Add manual MF Classic custom UID --- .../main/nfc/helpers/nfc_custom_event.h | 7 ++ .../main/nfc/scenes/nfc_scene_save_name.c | 3 + .../main/nfc/scenes/nfc_scene_set_type.c | 80 +++++++++++++++++++ .../main/nfc/scenes/nfc_scene_set_uid.c | 50 +++++++++++- firmware/targets/f7/api_symbols.csv | 1 + lib/nfc/helpers/nfc_generators.c | 21 ++++- lib/nfc/helpers/nfc_generators.h | 15 ++++ 7 files changed, 170 insertions(+), 7 deletions(-) diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index aa932a3d8..b2ad3170c 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -15,3 +15,10 @@ enum NfcCustomEvent { NfcCustomEventUpdateLog, NfcCustomEventSaveShadow, }; + +enum NfcSceneSetUidState { + NfcSceneSetUidStateNotSet, + NfcSceneSetUidStateMFClassic1k, + NfcSceneSetUidStateMFClassic4k, + NfcSceneSetUidStateMFClassicMini, +}; diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index a7b97aac0..327a31292 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -67,6 +67,9 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateNotSet); + dolphin_deed(DolphinDeedNfcAddSave); } else { dolphin_deed(DolphinDeedNfcSave); diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index cadf2eb69..d17b7616f 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -4,9 +4,19 @@ enum SubmenuIndex { SubmenuIndexNFCA4, SubmenuIndexNFCA7, + SubmenuIndexMFC1k4Uid, + SubmenuIndexMFC4k4Uid, + SubmenuIndexMFC1k7Uid, + SubmenuIndexMFC4k7Uid, + SubmenuIndexMFCMini, SubmenuIndexGeneratorsStart, }; +static const NfcGenerator ganeator_gag = { + .name = "Mifare Classic Custom UID", + .generator_func = NULL, +}; + void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) { Nfc* nfc = context; @@ -23,6 +33,36 @@ void nfc_scene_set_type_on_enter(void* context) { submenu, "NFC-A 7-bytes UID", SubmenuIndexNFCA7, nfc_scene_set_type_submenu_callback, nfc); submenu_add_item( submenu, "NFC-A 4-bytes UID", SubmenuIndexNFCA4, nfc_scene_set_type_submenu_callback, nfc); + submenu_add_item( + submenu, + "MFClassic1k4b Custom uid", + SubmenuIndexMFC1k4Uid, + nfc_scene_set_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "MFClassic4k4b Custom uid", + SubmenuIndexMFC4k4Uid, + nfc_scene_set_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "MFClassic1k7b Custom uid", + SubmenuIndexMFC1k7Uid, + nfc_scene_set_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "MFClassic4k7b Custom uid ", + SubmenuIndexMFC4k7Uid, + nfc_scene_set_type_submenu_callback, + nfc); + submenu_add_item( + submenu, + "MFClassic Mini Custom uid ", + SubmenuIndexMFCMini, + nfc_scene_set_type_submenu_callback, + nfc); // Generators int i = SubmenuIndexGeneratorsStart; @@ -49,6 +89,46 @@ bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { nfc->dev->format = NfcDeviceSaveFormatUid; scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); consumed = true; + } else if(event.event == SubmenuIndexMFC1k4Uid) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + nfc->dev->format = NfcDeviceSaveFormatMifareClassic; + nfc->generator = &ganeator_gag; + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassic1k); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; + } else if(event.event == SubmenuIndexMFC1k7Uid) { + nfc->dev->dev_data.nfc_data.uid_len = 7; + nfc->dev->format = NfcDeviceSaveFormatMifareClassic; + nfc->generator = &ganeator_gag; + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassic1k); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; + } else if(event.event == SubmenuIndexMFC4k4Uid) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + nfc->dev->format = NfcDeviceSaveFormatMifareClassic; + nfc->generator = &ganeator_gag; + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassic4k); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; + } else if(event.event == SubmenuIndexMFC4k7Uid) { + nfc->dev->dev_data.nfc_data.uid_len = 7; + nfc->dev->format = NfcDeviceSaveFormatMifareClassic; + nfc->generator = &ganeator_gag; + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassic4k); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; + } else if(event.event == SubmenuIndexMFCMini) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + nfc->dev->format = NfcDeviceSaveFormatMifareClassic; + nfc->generator = &ganeator_gag; + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassicMini); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; } else { nfc_device_clear(nfc->dev); nfc->generator = nfc_generators[event.event - SubmenuIndexGeneratorsStart]; diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 54606b68e..d1df352f1 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -27,7 +27,10 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { + if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateNotSet); + } else if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; @@ -36,8 +39,49 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { consumed = true; } } else { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; + switch(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSetUid)) { + case NfcSceneSetUidStateMFClassic1k: + nfc_generate_mf_classic_ext( + &nfc->dev->dev_data, + nfc->dev_edit_data.uid_len, + MfClassicType1k, + false, + nfc->dev_edit_data.uid); + scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); + consumed = true; + break; + + case NfcSceneSetUidStateMFClassic4k: + nfc_generate_mf_classic_ext( + &nfc->dev->dev_data, + nfc->dev_edit_data.uid_len, + MfClassicType4k, + false, + nfc->dev_edit_data.uid); + scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); + consumed = true; + break; + + case NfcSceneSetUidStateMFClassicMini: + nfc_generate_mf_classic_ext( + &nfc->dev->dev_data, + nfc->dev_edit_data.uid_len, + MfClassicTypeMini, + false, + nfc->dev_edit_data.uid); + scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); + consumed = true; + break; + + case NfcSceneSetUidStateNotSet: + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; + break; + + default: + furi_crash("Nfc unknown type"); + break; + } } } } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a6807e131..86296975d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2138,6 +2138,7 @@ Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* Function,-,nfc_generate_mf_classic,void,"NfcDeviceData*, uint8_t, MfClassicType" +Function,+,nfc_generate_mf_classic_ext,void,"NfcDeviceData*, uint8_t, MfClassicType, _Bool, uint8_t*" Function,+,nfc_get_dev_type,const char*,FuriHalNfcType Function,-,nfc_guess_protocol,const char*,NfcProtocol Function,+,nfc_mf_classic_type,const char*,MfClassicType diff --git a/lib/nfc/helpers/nfc_generators.c b/lib/nfc/helpers/nfc_generators.c index 50c89aba8..c65ffcedb 100644 --- a/lib/nfc/helpers/nfc_generators.c +++ b/lib/nfc/helpers/nfc_generators.c @@ -333,14 +333,22 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { mful->version.storage_size = 0x15; } -void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { +void nfc_generate_mf_classic_ext( + NfcDeviceData* data, + uint8_t uid_len, + MfClassicType type, + bool random_uid, + uint8_t* uid) { nfc_generate_common_start(data); - nfc_generate_mf_classic_uid(data->mf_classic_data.block[0].value, uid_len); + if(random_uid) { + nfc_generate_mf_classic_uid(data->mf_classic_data.block[0].value, uid_len); + } else { + memcpy(data->mf_classic_data.block[0].value, uid, uid_len); + } nfc_generate_mf_classic_common(data, uid_len, type); // Set the UID - data->nfc_data.uid[0] = NXP_MANUFACTURER_ID; - for(int i = 1; i < uid_len; i++) { + for(int i = 0; i < uid_len; i++) { data->nfc_data.uid[i] = data->mf_classic_data.block[0].value[i]; } @@ -395,6 +403,11 @@ void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType mfc->type = type; } +void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { + uint8_t uid = 0; + nfc_generate_mf_classic_ext(data, uid_len, type, true, &uid); +} + static void nfc_generate_mf_mini(NfcDeviceData* data) { nfc_generate_mf_classic(data, 4, MfClassicTypeMini); } diff --git a/lib/nfc/helpers/nfc_generators.h b/lib/nfc/helpers/nfc_generators.h index 8cee67067..5102d0bc3 100644 --- a/lib/nfc/helpers/nfc_generators.h +++ b/lib/nfc/helpers/nfc_generators.h @@ -2,6 +2,10 @@ #include "../nfc_device.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef void (*NfcGeneratorFunc)(NfcDeviceData* data); typedef struct { @@ -12,3 +16,14 @@ typedef struct { extern const NfcGenerator* const nfc_generators[]; void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type); + +void nfc_generate_mf_classic_ext( + NfcDeviceData* data, + uint8_t uid_len, + MfClassicType type, + bool random_uid, + uint8_t* uid); + +#ifdef __cplusplus +} +#endif \ No newline at end of file From 00cdc3d1cb2a575a2004745057f3637f56c3811a Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Wed, 9 Aug 2023 00:34:54 +0300 Subject: [PATCH 06/99] [FL-3461] RPC: md5 in storage list (#2929) * Protobuf: update * Toolbox: md5 for file. Unit-Tests: test md5_calc. * Storage RPC, CLI, unit tests: use new md5_calc * Protobuf: update * RPC, StorageList: append md5 info to file * fbt: attempt to fix shallow submodule checkouts * pvs: make happy * Protobuf: update to latest release Co-authored-by: hedger Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- .gitmodules | 1 + applications/debug/unit_tests/rpc/rpc_test.c | 132 +++++++++++------- .../debug/unit_tests/storage/storage_test.c | 48 +++++++ applications/services/rpc/rpc_storage.c | 56 ++++---- applications/services/storage/storage_cli.c | 32 +---- assets/protobuf | 2 +- assets/unit_tests/storage/md5.txt | 1 + fbt | 2 +- furi/core/thread.c | 4 +- lib/flipper_application/elf/elf_file.c | 2 +- lib/toolbox/md5_calc.c | 44 ++++++ lib/toolbox/md5_calc.h | 16 +++ 12 files changed, 233 insertions(+), 107 deletions(-) create mode 100644 assets/unit_tests/storage/md5.txt create mode 100644 lib/toolbox/md5_calc.c create mode 100644 lib/toolbox/md5_calc.h diff --git a/.gitmodules b/.gitmodules index 671e7e2c4..4fba0483e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,6 +10,7 @@ [submodule "assets/protobuf"] path = assets/protobuf url = https://github.com/flipperdevices/flipperzero-protobuf.git + shallow = false [submodule "lib/libusb_stm32"] path = lib/libusb_stm32 url = https://github.com/flipperdevices/libusb_stm32.git diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index 167266a84..533a8a9ca 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -287,7 +287,8 @@ static void test_rpc_create_simple_message( PB_Main* message, uint16_t tag, const char* str, - uint32_t command_id) { + uint32_t command_id, + bool flag) { furi_check(message); char* str_copy = NULL; @@ -308,6 +309,7 @@ static void test_rpc_create_simple_message( break; case PB_Main_storage_list_request_tag: message->content.storage_list_request.path = str_copy; + message->content.storage_list_request.include_md5 = flag; break; case PB_Main_storage_mkdir_request_tag: message->content.storage_mkdir_request.path = str_copy; @@ -419,6 +421,7 @@ static void } mu_check(result_msg_file->size == expected_msg_file->size); mu_check(result_msg_file->type == expected_msg_file->type); + mu_assert_string_eq(expected_msg_file->md5sum, result_msg_file->md5sum); if(result_msg_file->data && result_msg_file->type != PB_Storage_File_FileType_DIR) { mu_check(!result_msg_file->data == !expected_msg_file->data); // Zlo: WTF??? @@ -430,10 +433,10 @@ static void } static void test_rpc_compare_messages(PB_Main* result, PB_Main* expected) { - mu_check(result->command_id == expected->command_id); - mu_check(result->command_status == expected->command_status); - mu_check(result->has_next == expected->has_next); - mu_check(result->which_content == expected->which_content); + mu_assert_int_eq(expected->command_id, result->command_id); + mu_assert_int_eq(expected->command_status, result->command_status); + mu_assert_int_eq(expected->has_next, result->has_next); + mu_assert_int_eq(expected->which_content, result->which_content); if(result->command_status != PB_CommandStatus_OK) { mu_check(result->which_content == PB_Main_empty_tag); } @@ -573,10 +576,15 @@ static void static void test_rpc_storage_list_create_expected_list( MsgList_t msg_list, const char* path, - uint32_t command_id) { + uint32_t command_id, + bool append_md5) { Storage* fs_api = furi_record_open(RECORD_STORAGE); File* dir = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + PB_Main response = { .command_id = command_id, .has_next = false, @@ -614,6 +622,17 @@ static void test_rpc_storage_list_create_expected_list( list->file[i].data = NULL; /* memory free inside rpc_encode_and_send() -> pb_release() */ list->file[i].name = name; + + if(append_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf(md5_path, "%s/%s", path, name); + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } } else { @@ -626,6 +645,10 @@ static void test_rpc_storage_list_create_expected_list( response.has_next = false; MsgList_push_back(msg_list, response); + furi_string_free(md5); + furi_string_free(md5_path); + storage_file_free(file); + storage_dir_close(dir); storage_file_free(dir); @@ -675,16 +698,17 @@ static void test_rpc_free_msg_list(MsgList_t msg_list) { MsgList_clear(msg_list); } -static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { +static void test_rpc_storage_list_run(const char* path, uint32_t command_id, bool md5) { PB_Main request; MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_list_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_list_request_tag, path, command_id, md5); if(!strcmp(path, "/")) { test_rpc_storage_list_create_expected_list_root(expected_msg_list, command_id); } else { - test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id); + test_rpc_storage_list_create_expected_list(expected_msg_list, path, command_id, md5); } test_rpc_encode_and_feed_one(&request, 0); test_rpc_decode_and_compare(expected_msg_list, 0); @@ -694,15 +718,25 @@ static void test_rpc_storage_list_run(const char* path, uint32_t command_id) { } MU_TEST(test_storage_list) { - test_rpc_storage_list_run("/", ++command_id); - test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id); + test_rpc_storage_list_run("/", ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, false); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, false); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, false); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, false); + test_rpc_storage_list_run("error_path", ++command_id, false); +} - test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id); - test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id); - test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id); - test_rpc_storage_list_run("error_path", ++command_id); +MU_TEST(test_storage_list_md5) { + test_rpc_storage_list_run("/", ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("nfc"), ++command_id, true); + test_rpc_storage_list_run(STORAGE_INT_PATH_PREFIX, ++command_id, true); + test_rpc_storage_list_run(STORAGE_EXT_PATH_PREFIX, ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("infrared"), ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("ibutton"), ++command_id, true); + test_rpc_storage_list_run(EXT_PATH("lfrfid"), ++command_id, true); + test_rpc_storage_list_run("error_path", ++command_id, true); } static void @@ -770,7 +804,8 @@ static void test_storage_read_run(const char* path, uint32_t command_id) { MsgList_init(expected_msg_list); test_rpc_add_read_to_list_by_reading_real_file(expected_msg_list, path, command_id); - test_rpc_create_simple_message(&request, PB_Main_storage_read_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_read_request_tag, path, command_id, false); test_rpc_encode_and_feed_one(&request, 0); test_rpc_decode_and_compare(expected_msg_list, 0); @@ -824,7 +859,8 @@ static void test_rpc_storage_info_run(const char* path, uint32_t command_id) { MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_info_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_info_request_tag, path, command_id, false); PB_Main* response = MsgList_push_new(expected_msg_list); response->command_id = command_id; @@ -856,7 +892,8 @@ static void test_rpc_storage_stat_run(const char* path, uint32_t command_id) { MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_stat_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_stat_request_tag, path, command_id, false); Storage* fs_api = furi_record_open(RECORD_STORAGE); FileInfo fileinfo; @@ -968,7 +1005,11 @@ static void test_storage_write_read_run( test_rpc_add_empty_to_list(expected_msg_list, PB_CommandStatus_OK, *command_id); test_rpc_create_simple_message( - MsgList_push_raw(input_msg_list), PB_Main_storage_read_request_tag, path, ++*command_id); + MsgList_push_raw(input_msg_list), + PB_Main_storage_read_request_tag, + path, + ++*command_id, + false); test_rpc_add_read_or_write_to_list( expected_msg_list, READ_RESPONSE, @@ -1041,7 +1082,8 @@ MU_TEST(test_storage_interrupt_continuous_same_system) { MsgList_push_new(input_msg_list), PB_Main_storage_mkdir_request_tag, TEST_DIR "dir1", - command_id + 1); + command_id + 1, + false); test_rpc_add_read_or_write_to_list( input_msg_list, WRITE_REQUEST, @@ -1121,7 +1163,8 @@ static void test_storage_delete_run( MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_delete_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_delete_request_tag, path, command_id, false); request.content.storage_delete_request.recursive = recursive; test_rpc_add_empty_to_list(expected_msg_list, status, command_id); @@ -1202,7 +1245,8 @@ static void test_storage_mkdir_run(const char* path, size_t command_id, PB_Comma MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_mkdir_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_mkdir_request_tag, path, command_id, false); test_rpc_add_empty_to_list(expected_msg_list, status, command_id); test_rpc_encode_and_feed_one(&request, 0); @@ -1229,33 +1273,15 @@ MU_TEST(test_storage_mkdir) { static void test_storage_calculate_md5sum(const char* path, char* md5sum, size_t md5sum_size) { Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); - if(storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t once_read_size = 512; - const uint8_t hash_size = MD5SUM_SIZE; - uint8_t* data = malloc(once_read_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, once_read_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } - - free(hash); - free(data); + if(md5_string_calc_file(file, path, md5, NULL)) { + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); } else { furi_check(0); } + furi_string_free(md5); storage_file_close(file); storage_file_free(file); @@ -1271,11 +1297,12 @@ static void test_storage_md5sum_run( MsgList_t expected_msg_list; MsgList_init(expected_msg_list); - test_rpc_create_simple_message(&request, PB_Main_storage_md5sum_request_tag, path, command_id); + test_rpc_create_simple_message( + &request, PB_Main_storage_md5sum_request_tag, path, command_id, false); if(status == PB_CommandStatus_OK) { PB_Main* response = MsgList_push_new(expected_msg_list); test_rpc_create_simple_message( - response, PB_Main_storage_md5sum_response_tag, md5sum, command_id); + response, PB_Main_storage_md5sum_response_tag, md5sum, command_id, false); response->command_status = status; } else { test_rpc_add_empty_to_list(expected_msg_list, status, command_id); @@ -1433,6 +1460,7 @@ MU_TEST_SUITE(test_rpc_storage) { MU_RUN_TEST(test_storage_info); MU_RUN_TEST(test_storage_stat); MU_RUN_TEST(test_storage_list); + MU_RUN_TEST(test_storage_list_md5); MU_RUN_TEST(test_storage_read); MU_RUN_TEST(test_storage_write_read); MU_RUN_TEST(test_storage_write); @@ -1731,7 +1759,8 @@ MU_TEST(test_rpc_multisession_storage) { MsgList_push_raw(input_0), PB_Main_storage_read_request_tag, TEST_DIR "file0.txt", - ++command_id); + ++command_id, + false); test_rpc_add_read_or_write_to_list( expected_0, READ_RESPONSE, TEST_DIR "file0.txt", pattern, sizeof(pattern), 1, command_id); @@ -1739,7 +1768,8 @@ MU_TEST(test_rpc_multisession_storage) { MsgList_push_raw(input_1), PB_Main_storage_read_request_tag, TEST_DIR "file1.txt", - ++command_id); + ++command_id, + false); test_rpc_add_read_or_write_to_list( expected_1, READ_RESPONSE, TEST_DIR "file1.txt", pattern, sizeof(pattern), 1, command_id); diff --git a/applications/debug/unit_tests/storage/storage_test.c b/applications/debug/unit_tests/storage/storage_test.c index f0b45c598..13188e5e0 100644 --- a/applications/debug/unit_tests/storage/storage_test.c +++ b/applications/debug/unit_tests/storage/storage_test.c @@ -582,6 +582,49 @@ MU_TEST(test_storage_common_migrate) { furi_record_close(RECORD_STORAGE); } +#define MD5_HASH_SIZE (16) +#include + +MU_TEST(test_md5_calc) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + const char* path = UNIT_TESTS_PATH("storage/md5.txt"); + const char* md5_cstr = "2a456fa43e75088fdde41c93159d62a2"; + const uint8_t md5[MD5_HASH_SIZE] = { + 0x2a, + 0x45, + 0x6f, + 0xa4, + 0x3e, + 0x75, + 0x08, + 0x8f, + 0xdd, + 0xe4, + 0x1c, + 0x93, + 0x15, + 0x9d, + 0x62, + 0xa2, + }; + + uint8_t md5_output[MD5_HASH_SIZE]; + FuriString* md5_output_str = furi_string_alloc(); + memset(md5_output, 0, MD5_HASH_SIZE); + + mu_check(md5_calc_file(file, path, md5_output, NULL)); + mu_check(md5_string_calc_file(file, path, md5_output_str, NULL)); + + mu_assert_mem_eq(md5, md5_output, MD5_HASH_SIZE); + mu_assert_string_eq(md5_cstr, furi_string_get_cstr(md5_output_str)); + + storage_file_free(file); + furi_string_free(md5_output_str); + furi_record_close(RECORD_STORAGE); +} + MU_TEST_SUITE(test_data_path) { MU_RUN_TEST(test_storage_data_path); MU_RUN_TEST(test_storage_data_path_apps); @@ -591,11 +634,16 @@ MU_TEST_SUITE(test_storage_common) { MU_RUN_TEST(test_storage_common_migrate); } +MU_TEST_SUITE(test_md5_calc_suite) { + MU_RUN_TEST(test_md5_calc); +} + int run_minunit_test_storage() { MU_RUN_SUITE(storage_file); MU_RUN_SUITE(storage_dir); MU_RUN_SUITE(storage_rename); MU_RUN_SUITE(test_data_path); MU_RUN_SUITE(test_storage_common); + MU_RUN_SUITE(test_md5_calc_suite); return MU_EXIT_CODE; } diff --git a/applications/services/rpc/rpc_storage.c b/applications/services/rpc/rpc_storage.c index c3a4a0470..93c7043e8 100644 --- a/applications/services/rpc/rpc_storage.c +++ b/applications/services/rpc/rpc_storage.c @@ -9,7 +9,7 @@ #include "storage/filesystem_api_defines.h" #include "storage/storage.h" #include -#include +#include #include #include @@ -271,6 +271,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex }; PB_Storage_ListResponse* list = &response.content.storage_list_response; + bool include_md5 = request->content.storage_list_request.include_md5; + FuriString* md5 = furi_string_alloc(); + FuriString* md5_path = furi_string_alloc(); + File* file = storage_file_alloc(fs_api); + bool finish = false; int i = 0; @@ -296,6 +301,21 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex list->file[i].size = fileinfo.size; list->file[i].data = NULL; list->file[i].name = name; + + if(include_md5 && !file_info_is_dir(&fileinfo)) { + furi_string_printf( //-V576 + md5_path, + "%s/%s", + request->content.storage_list_request.path, + name); + + if(md5_string_calc_file(file, furi_string_get_cstr(md5_path), md5, NULL)) { + char* md5sum = list->file[i].md5sum; + size_t md5sum_size = sizeof(list->file[i].md5sum); + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); + } + } + ++i; } else { free(name); @@ -310,8 +330,11 @@ static void rpc_system_storage_list_process(const PB_Main* request, void* contex response.has_next = false; rpc_send_and_release(session, &response); + furi_string_free(md5); + furi_string_free(md5_path); storage_dir_close(dir); storage_file_free(dir); + storage_file_free(file); furi_record_close(RECORD_STORAGE); } @@ -569,23 +592,10 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont Storage* fs_api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(fs_api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; - if(storage_file_open(file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t size_to_read = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(size_to_read); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, size_to_read); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - + if(md5_string_calc_file(file, filename, md5, &file_error)) { PB_Main response = { .command_id = request->command_id, .command_status = PB_CommandStatus_OK, @@ -595,21 +605,15 @@ static void rpc_system_storage_md5sum_process(const PB_Main* request, void* cont char* md5sum = response.content.storage_md5sum_response.md5sum; size_t md5sum_size = sizeof(response.content.storage_md5sum_response.md5sum); - (void)md5sum_size; - furi_assert(hash_size <= ((md5sum_size - 1) / 2)); //-V547 - for(uint8_t i = 0; i < hash_size; i++) { - md5sum += snprintf(md5sum, md5sum_size, "%02x", hash[i]); - } + snprintf(md5sum, md5sum_size, "%s", furi_string_get_cstr(md5)); - free(hash); - free(data); - storage_file_close(file); rpc_send_and_release(session, &response); } else { rpc_send_and_release_empty( - session, request->command_id, rpc_system_storage_get_file_error(file)); + session, request->command_id, rpc_system_storage_get_error(file_error)); } + furi_string_free(md5); storage_file_free(file); furi_record_close(RECORD_STORAGE); diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index 8e2dcdbbb..74bcf2d92 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include #include @@ -482,34 +482,16 @@ static void storage_cli_md5(Cli* cli, FuriString* path) { UNUSED(cli); Storage* api = furi_record_open(RECORD_STORAGE); File* file = storage_file_alloc(api); + FuriString* md5 = furi_string_alloc(); + FS_Error file_error; - if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - const uint16_t buffer_size = 512; - const uint8_t hash_size = 16; - uint8_t* data = malloc(buffer_size); - uint8_t* hash = malloc(sizeof(uint8_t) * hash_size); - md5_context* md5_ctx = malloc(sizeof(md5_context)); - - md5_starts(md5_ctx); - while(true) { - uint16_t read_size = storage_file_read(file, data, buffer_size); - if(read_size == 0) break; - md5_update(md5_ctx, data, read_size); - } - md5_finish(md5_ctx, hash); - free(md5_ctx); - - for(uint8_t i = 0; i < hash_size; i++) { - printf("%02x", hash[i]); - } - printf("\r\n"); - - free(hash); - free(data); + if(md5_string_calc_file(file, furi_string_get_cstr(path), md5, &file_error)) { + printf("%s\r\n", furi_string_get_cstr(md5)); } else { - storage_cli_print_error(storage_file_get_error(file)); + storage_cli_print_error(file_error); } + furi_string_free(md5); storage_file_close(file); storage_file_free(file); diff --git a/assets/protobuf b/assets/protobuf index 08a907d95..7e011a958 160000 --- a/assets/protobuf +++ b/assets/protobuf @@ -1 +1 @@ -Subproject commit 08a907d95733600becc41c0602ef5ee4c4baf782 +Subproject commit 7e011a95863716e72e7c6b5d552bca241d688304 diff --git a/assets/unit_tests/storage/md5.txt b/assets/unit_tests/storage/md5.txt new file mode 100644 index 000000000..777e390be --- /dev/null +++ b/assets/unit_tests/storage/md5.txt @@ -0,0 +1 @@ +Yo dawg, I heard you like md5... \ No newline at end of file diff --git a/fbt b/fbt index ef41cc056..471285a76 100755 --- a/fbt +++ b/fbt @@ -29,7 +29,7 @@ if [ -z "$FBT_NO_SYNC" ]; then echo "\".git\" directory not found, please clone repo via \"git clone\""; exit 1; fi - git submodule update --init --depth 1 --jobs "$N_GIT_THREADS"; + git submodule update --init --jobs "$N_GIT_THREADS"; fi $SCONS_EP $SCONS_DEFAULT_FLAGS "$@" diff --git a/furi/core/thread.c b/furi/core/thread.c index 657b867d1..de50bde7a 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -92,10 +92,10 @@ static void furi_thread_body(void* context) { if(thread->heap_trace_enabled == true) { furi_delay_ms(33); thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle); - furi_log_print_format( //-V576 + furi_log_print_format( thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo, TAG, - "%s allocation balance: %u", + "%s allocation balance: %zu", thread->name ? thread->name : "Thread", thread->heap_size); memmgr_heap_disable_thread_trace((FuriThreadId)task_handle); diff --git a/lib/flipper_application/elf/elf_file.c b/lib/flipper_application/elf/elf_file.c index fc9dd06ba..539a48c85 100644 --- a/lib/flipper_application/elf/elf_file.c +++ b/lib/flipper_application/elf/elf_file.c @@ -892,7 +892,7 @@ ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) { ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it); total_size += itref->value.size; } - FURI_LOG_I(TAG, "Total size of loaded sections: %u", total_size); //-V576 + FURI_LOG_I(TAG, "Total size of loaded sections: %zu", total_size); } return status; diff --git a/lib/toolbox/md5_calc.c b/lib/toolbox/md5_calc.c new file mode 100644 index 000000000..b050295a1 --- /dev/null +++ b/lib/toolbox/md5_calc.c @@ -0,0 +1,44 @@ +#include "md5.h" +#include "md5_calc.h" + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error) { + bool result = storage_file_open(file, path, FSAM_READ, FSOM_OPEN_EXISTING); + + if(result) { + const uint16_t size_to_read = 512; + uint8_t* data = malloc(size_to_read); + md5_context* md5_ctx = malloc(sizeof(md5_context)); + + md5_starts(md5_ctx); + while(true) { + uint16_t read_size = storage_file_read(file, data, size_to_read); + if(read_size == 0) break; + md5_update(md5_ctx, data, read_size); + } + md5_finish(md5_ctx, output); + free(md5_ctx); + free(data); + } + + if(file_error != NULL) { + *file_error = storage_file_get_error(file); + } + + storage_file_close(file); + return result; +} + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error) { + const size_t hash_size = 16; + unsigned char hash[hash_size]; + bool result = md5_calc_file(file, path, hash, file_error); + + if(result) { + furi_string_set(output, ""); + for(size_t i = 0; i < hash_size; i++) { + furi_string_cat_printf(output, "%02x", hash[i]); + } + } + + return result; +} \ No newline at end of file diff --git a/lib/toolbox/md5_calc.h b/lib/toolbox/md5_calc.h new file mode 100644 index 000000000..cf82e3718 --- /dev/null +++ b/lib/toolbox/md5_calc.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool md5_calc_file(File* file, const char* path, unsigned char output[16], FS_Error* file_error); + +bool md5_string_calc_file(File* file, const char* path, FuriString* output, FS_Error* file_error); + +#ifdef __cplusplus +} +#endif From 98d4309b61c063af5480d7bbc9ba2d429d35d6d6 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Tue, 8 Aug 2023 23:44:45 +0200 Subject: [PATCH 07/99] IR Universal Audio Remote: add NAD C316BEE (#2954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remote control "NAD Amp1" https://nad.de/product/nad-c316bee-v2-vollverstaerker/ Co-authored-by: あく --- assets/resources/infrared/assets/audio.ir | 48 +++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 3b9e626ba..5cdc9048a 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -377,3 +377,51 @@ type: parsed protocol: Kaseikyo address: A0 02 20 00 command: 20 03 00 00 +# +# Model: NAD C316BEE +name: Power +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 25 DA 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 94 6B 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 88 77 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 8C 73 00 00 +# +name: Play +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 01 FE 00 00 +# +name: Pause +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 4A B5 00 00 +# +name: Prev +type: parsed +protocol: NECext +address: 87 7C 00 00 +command: 05 FA 00 00 +# +name: Next +type: parsed +protocol: NECext +address: 87 7C 00 0 From d9e931b7b7540ae81b9077cd6fefdcd9b9c84dea Mon Sep 17 00:00:00 2001 From: Alexandre L Date: Wed, 9 Aug 2023 02:46:07 +0200 Subject: [PATCH 08/99] fbt: Fix building using path with space (#2948) * fbt: Fix building on Windows using path with space * scripts: Fixed formatting --------- Co-authored-by: hedger Co-authored-by: hedger --- scripts/fbt/util.py | 4 +++- scripts/fbt_tools/fbt_assets.py | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index ae850a8c3..57e60aecf 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -59,7 +59,9 @@ def extract_abs_dir_path(node): if abs_dir_node is None: raise StopError(f"Can't find absolute path for {node.name}") - return abs_dir_node.abspath + # Don't return abspath attribute (type is str), it will break in + # OverrideEnvironment.subst_list() by splitting path on spaces + return abs_dir_node def path_as_posix(path): diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 68617c254..b2b9310ba 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -137,14 +137,14 @@ def generate(env): BUILDERS={ "IconBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" icons "${ABSPATHGETTERFUNC(SOURCE)}" "${TARGET.dir}" --filename ${ICON_FILE_NAME}', + '${PYTHON3} ${ASSETS_COMPILER} icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename "${ICON_FILE_NAME}"', "${ICONSCOMSTR}", ), emitter=icons_emitter, ), "ProtoBuilder": Builder( action=Action( - '${PYTHON3} "${NANOPB_COMPILER}" -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}', + "${PYTHON3} ${NANOPB_COMPILER} -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}", "${PROTOCOMSTR}", ), emitter=proto_emitter, @@ -153,14 +153,14 @@ def generate(env): ), "DolphinSymBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" dolphin -s dolphin_${DOLPHIN_RES_TYPE} "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', + "${PYTHON3} ${ASSETS_COMPILER} dolphin -s dolphin_${DOLPHIN_RES_TYPE} ${SOURCE} ${_DOLPHIN_OUT_DIR}", "${DOLPHINCOMSTR}", ), emitter=dolphin_emitter, ), "DolphinExtBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" dolphin "${SOURCE}" "${_DOLPHIN_OUT_DIR}"', + "${PYTHON3} ${ASSETS_COMPILER} dolphin ${SOURCE} ${_DOLPHIN_OUT_DIR}", "${DOLPHINCOMSTR}", ), emitter=dolphin_emitter, From a39ef50fdbf97b8bf4f895c2bbc33beb40c743d3 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Wed, 9 Aug 2023 16:52:41 +0900 Subject: [PATCH 09/99] [FL-3433] Add compressor.h to the SDK (#2962) --- firmware/targets/f18/api_symbols.csv | 10 +++++++++- firmware/targets/f7/api_symbols.csv | 10 +++++++++- lib/toolbox/SConscript | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 8b04f441a..529477b50 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,34.3,, +Version,+,34.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -164,6 +164,7 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,, Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,, Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/compress.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -628,6 +629,13 @@ Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiI Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,+,compress_alloc,Compress*,uint16_t +Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_free,void,Compress* +Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" +Function,+,compress_icon_free,void,CompressIcon* Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index dc0720ee2..6fc4356c5 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,34.3,, +Version,+,34.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,, @@ -205,6 +205,7 @@ Header,+,lib/subghz/subghz_worker.h,, Header,+,lib/subghz/transmitter.h,, Header,+,lib/toolbox/api_lock.h,, Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/compress.h,, Header,+,lib/toolbox/crc32_calc.h,, Header,+,lib/toolbox/dir_walk.h,, Header,+,lib/toolbox/float_tools.h,, @@ -689,6 +690,13 @@ Function,+,composite_api_resolver_add,void,"CompositeApiResolver*, const ElfApiI Function,+,composite_api_resolver_alloc,CompositeApiResolver*, Function,+,composite_api_resolver_free,void,CompositeApiResolver* Function,+,composite_api_resolver_get,const ElfApiInterface*,CompositeApiResolver* +Function,+,compress_alloc,Compress*,uint16_t +Function,+,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,+,compress_free,void,Compress* +Function,+,compress_icon_alloc,CompressIcon*, +Function,+,compress_icon_decode,void,"CompressIcon*, const uint8_t*, uint8_t**" +Function,+,compress_icon_free,void,CompressIcon* Function,-,copysign,double,"double, double" Function,-,copysignf,float,"float, float" Function,-,copysignl,long double,"long double, long double" diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 6084969c4..761755d2d 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -9,6 +9,7 @@ env.Append( ], SDK_HEADERS=[ File("api_lock.h"), + File("compress.h"), File("manchester_decoder.h"), File("manchester_encoder.h"), File("path.h"), From 107862577590c166aa28e4abd3ea33f2622edb2b Mon Sep 17 00:00:00 2001 From: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> Date: Wed, 9 Aug 2023 23:10:04 +0200 Subject: [PATCH 10/99] BadUSB: qFlipper install script for MacOS (#2915) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create Install_qFlipper_macOS.txt * BadUSB: qFlipper mac install routine improvements Co-authored-by: あく --- .../resources/badusb/Install_qFlipper_macOS.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 assets/resources/badusb/Install_qFlipper_macOS.txt diff --git a/assets/resources/badusb/Install_qFlipper_macOS.txt b/assets/resources/badusb/Install_qFlipper_macOS.txt new file mode 100644 index 000000000..252954ecb --- /dev/null +++ b/assets/resources/badusb/Install_qFlipper_macOS.txt @@ -0,0 +1,16 @@ +ID 05ac:021e Apple:Keyboard +REM Keep these 3 lines IF (and only if) it's the first time you are performing a badKB attack against a specific macOS target. +REM In fact, it helps Flipper Zero bypass the macOS keyboard setup assistant. Otherwise the attack will not start. +REM Author: 47LeCoste +REM Version 1.0 (Flipper Ducky) +REM Target: macOS +DELAY 3000 +F4 +DELAY 2500 +STRING Terminal +DELAY 2500 +ENTER +DELAY 1500 +STRING (cd /tmp && curl -L -o qFlipper.dmg https://update.flipperzero.one/qFlipper/release/macos-amd64/dmg && hdiutil attach qFlipper.dmg && app_volume=$(ls /Volumes | grep -i "qFlipper") && (test -e /Applications/qFlipper.app && rm -rf /Applications/qFlipper.app ); cp -R "/Volumes/$app_volume/qFlipper.app" /Applications/ && hdiutil detach "/Volumes/$app_volume" && rm qFlipper.dmg && open /Applications/qFlipper.app) +DELAY 1000 +ENTER From fb63e53d9ac2c1bc53223eb2d7c0f519e828e0cc Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Thu, 10 Aug 2023 00:18:40 +0300 Subject: [PATCH 11/99] [FL-3435] External apps removed (#2934) Co-authored-by: Aleksandr Kutuzov --- .github/CODEOWNERS | 3 - .github/workflows/build.yml | 2 +- .gitmodules | 3 - .pvsoptions | 2 +- applications/ReadMe.md | 14 +- applications/external/application.fam | 6 - .../avr_isp_programmer/application.fam | 17 - .../avr_isp_programmer/avr_app_icon_10x10.png | Bin 3614 -> 0 bytes .../external/avr_isp_programmer/avr_isp_app.c | 179 --- .../avr_isp_programmer/avr_isp_app_i.c | 31 - .../avr_isp_programmer/avr_isp_app_i.h | 44 - .../avr_isp_programmer/helpers/avr_isp.c | 496 ------ .../avr_isp_programmer/helpers/avr_isp.h | 70 - .../helpers/avr_isp_event.h | 23 - .../helpers/avr_isp_types.h | 32 - .../helpers/avr_isp_worker.c | 266 ---- .../helpers/avr_isp_worker.h | 49 - .../helpers/avr_isp_worker_rw.c | 1157 -------------- .../helpers/avr_isp_worker_rw.h | 99 -- .../helpers/flipper_i32hex_file.c | 321 ---- .../helpers/flipper_i32hex_file.h | 55 - .../images/avr_app_icon_10x10.png | Bin 3614 -> 0 bytes .../avr_isp_programmer/images/avr_wiring.png | Bin 4513 -> 0 bytes .../images/chif_not_found_83x37.png | Bin 3742 -> 0 bytes .../images/chip_error_70x22.png | Bin 3688 -> 0 bytes .../images/chip_long_70x22.png | Bin 3656 -> 0 bytes .../images/chip_not_found_83x37.png | Bin 3779 -> 0 bytes .../images/dolphin_nice_96x59.png | Bin 2459 -> 0 bytes .../images/isp_active_128x53.png | Bin 3961 -> 0 bytes .../images/link_waiting_77x56.png | Bin 3883 -> 0 bytes .../lib/driver/avr_isp_chip_arr.c | 386 ----- .../lib/driver/avr_isp_chip_arr.h | 33 - .../lib/driver/avr_isp_prog.c | 639 -------- .../lib/driver/avr_isp_prog.h | 16 - .../lib/driver/avr_isp_prog_cmd.h | 97 -- .../lib/driver/avr_isp_spi_sw.c | 71 - .../lib/driver/avr_isp_spi_sw.h | 24 - .../avr_isp_programmer/lib/driver/clock.png | Bin 3649 -> 0 bytes .../avr_isp_programmer/scenes/avr_isp_scene.c | 30 - .../avr_isp_programmer/scenes/avr_isp_scene.h | 29 - .../scenes/avr_isp_scene_about.c | 99 -- .../scenes/avr_isp_scene_chip_detect.c | 72 - .../scenes/avr_isp_scene_config.h | 10 - .../scenes/avr_isp_scene_input_name.c | 89 -- .../scenes/avr_isp_scene_load.c | 22 - .../scenes/avr_isp_scene_programmer.c | 28 - .../scenes/avr_isp_scene_reader.c | 64 - .../scenes/avr_isp_scene_start.c | 75 - .../scenes/avr_isp_scene_success.c | 44 - .../scenes/avr_isp_scene_wiring.c | 21 - .../scenes/avr_isp_scene_writer.c | 69 - .../views/avr_isp_view_chip_detect.c | 213 --- .../views/avr_isp_view_chip_detect.h | 32 - .../views/avr_isp_view_programmer.c | 134 -- .../views/avr_isp_view_programmer.h | 27 - .../views/avr_isp_view_reader.c | 215 --- .../views/avr_isp_view_reader.h | 35 - .../views/avr_isp_view_writer.c | 268 ---- .../views/avr_isp_view_writer.h | 37 - applications/external/dap_link/README.md | 105 -- .../external/dap_link/application.fam | 24 - applications/external/dap_link/dap_config.h | 234 --- applications/external/dap_link/dap_link.c | 527 ------- applications/external/dap_link/dap_link.h | 55 - applications/external/dap_link/dap_link.png | Bin 143 -> 0 bytes applications/external/dap_link/gui/dap_gui.c | 92 -- applications/external/dap_link/gui/dap_gui.h | 4 - .../dap_link/gui/dap_gui_custom_event.h | 7 - .../external/dap_link/gui/dap_gui_i.h | 34 - .../dap_link/gui/scenes/config/dap_scene.c | 30 - .../dap_link/gui/scenes/config/dap_scene.h | 29 - .../gui/scenes/config/dap_scene_config.h | 4 - .../dap_link/gui/scenes/dap_scene_about.c | 68 - .../dap_link/gui/scenes/dap_scene_config.c | 107 -- .../dap_link/gui/scenes/dap_scene_help.c | 102 -- .../dap_link/gui/scenes/dap_scene_main.c | 154 -- .../dap_link/gui/views/dap_main_view.c | 189 --- .../dap_link/gui/views/dap_main_view.h | 45 - .../dap_link/icons/ActiveConnection_50x64.png | Bin 3842 -> 0 bytes .../dap_link/icons/ArrowUpEmpty_12x18.png | Bin 159 -> 0 bytes .../dap_link/icons/ArrowUpFilled_12x18.png | Bin 173 -> 0 bytes applications/external/dap_link/lib/free-dap | 1 - .../external/dap_link/usb/dap_v2_usb.c | 977 ------------ .../external/dap_link/usb/dap_v2_usb.h | 53 - .../external/dap_link/usb/usb_winusb.h | 143 -- applications/external/hid_app/application.fam | 24 - .../external/hid_app/assets/Arr_dwn_7x9.png | Bin 3602 -> 0 bytes .../external/hid_app/assets/Arr_up_7x9.png | Bin 3605 -> 0 bytes .../hid_app/assets/Ble_connected_15x15.png | Bin 3634 -> 0 bytes .../hid_app/assets/Ble_disconnected_15x15.png | Bin 657 -> 0 bytes .../hid_app/assets/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../external/hid_app/assets/ButtonF10_5x8.png | Bin 172 -> 0 bytes .../external/hid_app/assets/ButtonF11_5x8.png | Bin 173 -> 0 bytes .../external/hid_app/assets/ButtonF12_5x8.png | Bin 180 -> 0 bytes .../external/hid_app/assets/ButtonF1_5x8.png | Bin 177 -> 0 bytes .../external/hid_app/assets/ButtonF2_5x8.png | Bin 179 -> 0 bytes .../external/hid_app/assets/ButtonF3_5x8.png | Bin 178 -> 0 bytes .../external/hid_app/assets/ButtonF4_5x8.png | Bin 177 -> 0 bytes .../external/hid_app/assets/ButtonF5_5x8.png | Bin 178 -> 0 bytes .../external/hid_app/assets/ButtonF6_5x8.png | Bin 177 -> 0 bytes .../external/hid_app/assets/ButtonF7_5x8.png | Bin 176 -> 0 bytes .../external/hid_app/assets/ButtonF8_5x8.png | Bin 176 -> 0 bytes .../external/hid_app/assets/ButtonF9_5x8.png | Bin 179 -> 0 bytes .../hid_app/assets/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../hid_app/assets/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../external/hid_app/assets/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../external/hid_app/assets/Button_18x18.png | Bin 3609 -> 0 bytes .../external/hid_app/assets/Circles_47x47.png | Bin 3712 -> 0 bytes .../hid_app/assets/Left_mouse_icon_9x9.png | Bin 3622 -> 0 bytes .../external/hid_app/assets/Like_def_11x9.png | Bin 3616 -> 0 bytes .../hid_app/assets/Like_pressed_17x17.png | Bin 3643 -> 0 bytes .../external/hid_app/assets/Ok_btn_9x9.png | Bin 3605 -> 0 bytes .../hid_app/assets/Ok_btn_pressed_13x13.png | Bin 3625 -> 0 bytes .../hid_app/assets/Pin_arrow_down_7x9.png | Bin 3607 -> 0 bytes .../hid_app/assets/Pin_arrow_left_9x7.png | Bin 3603 -> 0 bytes .../hid_app/assets/Pin_arrow_right_9x7.png | Bin 3602 -> 0 bytes .../hid_app/assets/Pin_arrow_up_7x9.png | Bin 3603 -> 0 bytes .../hid_app/assets/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes .../hid_app/assets/Pressed_Button_13x13.png | Bin 3606 -> 0 bytes .../hid_app/assets/Right_mouse_icon_9x9.png | Bin 3622 -> 0 bytes .../external/hid_app/assets/Space_60x18.png | Bin 2871 -> 0 bytes .../external/hid_app/assets/Space_65x18.png | Bin 3619 -> 0 bytes .../external/hid_app/assets/Voldwn_6x6.png | Bin 3593 -> 0 bytes .../external/hid_app/assets/Volup_8x6.png | Bin 3595 -> 0 bytes applications/external/hid_app/hid.c | 447 ------ applications/external/hid_app/hid.h | 67 - .../external/hid_app/hid_ble_10px.png | Bin 151 -> 0 bytes .../external/hid_app/hid_usb_10px.png | Bin 969 -> 0 bytes applications/external/hid_app/views.h | 11 - .../external/hid_app/views/hid_keyboard.c | 411 ----- .../external/hid_app/views/hid_keyboard.h | 14 - .../external/hid_app/views/hid_keynote.c | 312 ---- .../external/hid_app/views/hid_keynote.h | 16 - .../external/hid_app/views/hid_media.c | 218 --- .../external/hid_app/views/hid_media.h | 13 - .../external/hid_app/views/hid_mouse.c | 226 --- .../external/hid_app/views/hid_mouse.h | 17 - .../hid_app/views/hid_mouse_clicker.c | 214 --- .../hid_app/views/hid_mouse_clicker.h | 14 - .../hid_app/views/hid_mouse_jiggler.c | 159 -- .../hid_app/views/hid_mouse_jiggler.h | 17 - .../external/hid_app/views/hid_tiktok.c | 241 --- .../external/hid_app/views/hid_tiktok.h | 14 - applications/external/mfkey32/application.fam | 17 - .../external/mfkey32/images/mfkey.png | Bin 9060 -> 0 bytes applications/external/mfkey32/mfkey.png | Bin 9060 -> 0 bytes applications/external/mfkey32/mfkey32.c | 1349 ---------------- .../external/nfc_magic/application.fam | 21 - .../nfc_magic/assets/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes .../nfc_magic/assets/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../external/nfc_magic/assets/Loading_24.png | Bin 3649 -> 0 bytes .../nfc_magic/assets/NFC_manual_60x50.png | Bin 3804 -> 0 bytes .../nfc_magic/lib/magic/classic_gen1.c | 145 -- .../nfc_magic/lib/magic/classic_gen1.h | 11 - .../external/nfc_magic/lib/magic/common.c | 33 - .../external/nfc_magic/lib/magic/common.h | 19 - .../external/nfc_magic/lib/magic/gen4.c | 199 --- .../external/nfc_magic/lib/magic/gen4.h | 48 - .../external/nfc_magic/lib/magic/types.c | 23 - .../external/nfc_magic/lib/magic/types.h | 5 - applications/external/nfc_magic/nfc_magic.c | 184 --- applications/external/nfc_magic/nfc_magic.h | 5 - applications/external/nfc_magic/nfc_magic_i.h | 94 -- .../external/nfc_magic/nfc_magic_worker.c | 480 ------ .../external/nfc_magic/nfc_magic_worker.h | 42 - .../external/nfc_magic/nfc_magic_worker_i.h | 29 - .../nfc_magic/scenes/nfc_magic_scene.c | 30 - .../nfc_magic/scenes/nfc_magic_scene.h | 29 - .../scenes/nfc_magic_scene_actions.c | 50 - .../nfc_magic/scenes/nfc_magic_scene_check.c | 89 -- .../nfc_magic/scenes/nfc_magic_scene_config.h | 18 - .../scenes/nfc_magic_scene_file_select.c | 76 - .../scenes/nfc_magic_scene_gen4_actions.c | 70 - .../scenes/nfc_magic_scene_key_input.c | 45 - .../scenes/nfc_magic_scene_magic_info.c | 59 - .../scenes/nfc_magic_scene_new_key_input.c | 45 - .../scenes/nfc_magic_scene_not_magic.c | 43 - .../nfc_magic/scenes/nfc_magic_scene_rekey.c | 95 -- .../scenes/nfc_magic_scene_rekey_fail.c | 50 - .../nfc_magic/scenes/nfc_magic_scene_start.c | 56 - .../scenes/nfc_magic_scene_success.c | 42 - .../nfc_magic/scenes/nfc_magic_scene_wipe.c | 97 -- .../scenes/nfc_magic_scene_wipe_fail.c | 41 - .../nfc_magic/scenes/nfc_magic_scene_write.c | 97 -- .../scenes/nfc_magic_scene_write_confirm.c | 64 - .../scenes/nfc_magic_scene_write_fail.c | 58 - .../scenes/nfc_magic_scene_wrong_card.c | 53 - .../nfc_rfid_detector/application.fam | 13 - .../helpers/nfc_rfid_detector_event.h | 7 - .../helpers/nfc_rfid_detector_types.h | 15 - .../images/Modern_reader_18x34.png | Bin 3670 -> 0 bytes .../images/Move_flipper_26x39.png | Bin 3698 -> 0 bytes .../images/NFC_detect_45x30.png | Bin 168 -> 0 bytes .../images/Rfid_detect_45x30.png | Bin 158 -> 0 bytes .../nfc_rfid_detector_10px.png | Bin 124 -> 0 bytes .../nfc_rfid_detector/nfc_rfid_detector_app.c | 108 -- .../nfc_rfid_detector_app_i.c | 40 - .../nfc_rfid_detector_app_i.h | 30 - .../scenes/nfc_rfid_detector_scene.c | 31 - .../scenes/nfc_rfid_detector_scene.h | 29 - .../scenes/nfc_rfid_detector_scene_about.c | 69 - .../scenes/nfc_rfid_detector_scene_config.h | 3 - .../nfc_rfid_detector_scene_field_presence.c | 60 - .../scenes/nfc_rfid_detector_scene_start.c | 58 - .../nfc_rfid_detector_view_field_presence.c | 164 -- .../nfc_rfid_detector_view_field_presence.h | 19 - applications/external/picopass/125_10px.png | Bin 308 -> 0 bytes .../external/picopass/application.fam | 22 - .../picopass/helpers/iclass_elite_dict.c | 164 -- .../picopass/helpers/iclass_elite_dict.h | 29 - .../picopass/icons/DolphinMafia_115x62.png | Bin 2504 -> 0 bytes .../picopass/icons/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../external/picopass/icons/Nfc_10px.png | Bin 304 -> 0 bytes .../icons/RFIDDolphinReceive_97x61.png | Bin 1421 -> 0 bytes .../picopass/icons/RFIDDolphinSend_97x61.png | Bin 1418 -> 0 bytes .../picopass/lib/loclass/optimized_cipher.c | 299 ---- .../picopass/lib/loclass/optimized_cipher.h | 98 -- .../lib/loclass/optimized_cipherutils.c | 136 -- .../lib/loclass/optimized_cipherutils.h | 64 - .../picopass/lib/loclass/optimized_elite.c | 232 --- .../picopass/lib/loclass/optimized_elite.h | 58 - .../picopass/lib/loclass/optimized_ikeys.c | 320 ---- .../picopass/lib/loclass/optimized_ikeys.h | 66 - applications/external/picopass/picopass.c | 212 --- applications/external/picopass/picopass.h | 3 - .../external/picopass/picopass_device.c | 380 ----- .../external/picopass/picopass_device.h | 117 -- applications/external/picopass/picopass_i.h | 99 -- .../external/picopass/picopass_keys.c | 8 - .../external/picopass/picopass_keys.h | 10 - .../external/picopass/picopass_worker.c | 752 --------- .../external/picopass/picopass_worker.h | 52 - .../external/picopass/picopass_worker_i.h | 34 - .../external/picopass/rfal_picopass.c | 214 --- .../external/picopass/rfal_picopass.h | 48 - .../external/picopass/scenes/picopass_scene.c | 30 - .../external/picopass/scenes/picopass_scene.h | 29 - .../scenes/picopass_scene_card_menu.c | 78 - .../picopass/scenes/picopass_scene_config.h | 17 - .../picopass/scenes/picopass_scene_delete.c | 58 - .../scenes/picopass_scene_delete_success.c | 40 - .../scenes/picopass_scene_device_info.c | 114 -- .../scenes/picopass_scene_elite_dict_attack.c | 172 -- .../scenes/picopass_scene_file_select.c | 25 - .../picopass/scenes/picopass_scene_key_menu.c | 100 -- .../scenes/picopass_scene_read_card.c | 61 - .../scenes/picopass_scene_read_card_success.c | 172 -- .../picopass_scene_read_factory_success.c | 80 - .../scenes/picopass_scene_save_name.c | 81 - .../scenes/picopass_scene_save_success.c | 47 - .../scenes/picopass_scene_saved_menu.c | 64 - .../picopass/scenes/picopass_scene_start.c | 64 - .../scenes/picopass_scene_write_card.c | 53 - .../picopass_scene_write_card_success.c | 70 - .../scenes/picopass_scene_write_key.c | 53 - .../external/picopass/views/dict_attack.c | 281 ---- .../external/picopass/views/dict_attack.h | 44 - .../external/signal_generator/application.fam | 12 - .../icons/SmallArrowDown_3x5.png | Bin 3592 -> 0 bytes .../icons/SmallArrowUp_3x5.png | Bin 7976 -> 0 bytes .../scenes/signal_gen_scene.c | 30 - .../scenes/signal_gen_scene.h | 29 - .../scenes/signal_gen_scene_config.h | 3 - .../scenes/signal_gen_scene_mco.c | 145 -- .../scenes/signal_gen_scene_pwm.c | 79 - .../scenes/signal_gen_scene_start.c | 55 - .../signal_generator/signal_gen_10px.png | Bin 6082 -> 0 bytes .../signal_generator/signal_gen_app.c | 93 -- .../signal_generator/signal_gen_app_i.h | 46 - .../signal_generator/views/signal_gen_pwm.c | 310 ---- .../signal_generator/views/signal_gen_pwm.h | 21 - .../external/spi_mem_manager/application.fam | 17 - .../images/ChipLooking_64x64/frame_01.png | Bin 7231 -> 0 bytes .../images/ChipLooking_64x64/frame_02.png | Bin 6909 -> 0 bytes .../images/ChipLooking_64x64/frame_03.png | Bin 7308 -> 0 bytes .../images/ChipLooking_64x64/frame_rate | 1 - .../spi_mem_manager/images/Dip8_10px.png | Bin 195 -> 0 bytes .../spi_mem_manager/images/Dip8_32x36.png | Bin 977 -> 0 bytes .../images/DolphinMafia_115x62.png | Bin 2504 -> 0 bytes .../images/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../images/SDQuestion_35x43.png | Bin 1950 -> 0 bytes .../images/Wiring_SPI_128x64.png | Bin 4446 -> 0 bytes .../spi_mem_manager/lib/spi/spi_mem_chip.c | 105 -- .../spi_mem_manager/lib/spi/spi_mem_chip.h | 34 - .../lib/spi/spi_mem_chip_arr.c | 1399 ----------------- .../spi_mem_manager/lib/spi/spi_mem_chip_i.h | 85 - .../spi_mem_manager/lib/spi/spi_mem_tools.c | 152 -- .../spi_mem_manager/lib/spi/spi_mem_tools.h | 14 - .../spi_mem_manager/lib/spi/spi_mem_worker.c | 129 -- .../spi_mem_manager/lib/spi/spi_mem_worker.h | 54 - .../lib/spi/spi_mem_worker_i.h | 24 - .../lib/spi/spi_mem_worker_modes.c | 214 --- .../spi_mem_manager/scenes/spi_mem_scene.c | 30 - .../spi_mem_manager/scenes/spi_mem_scene.h | 29 - .../scenes/spi_mem_scene_about.c | 42 - .../scenes/spi_mem_scene_chip_detect.c | 37 - .../scenes/spi_mem_scene_chip_detect_fail.c | 57 - .../scenes/spi_mem_scene_chip_detected.c | 94 -- .../scenes/spi_mem_scene_chip_error.c | 52 - .../scenes/spi_mem_scene_config.h | 21 - .../scenes/spi_mem_scene_delete_confirm.c | 62 - .../scenes/spi_mem_scene_erase.c | 65 - .../scenes/spi_mem_scene_file_info.c | 29 - .../scenes/spi_mem_scene_read.c | 57 - .../scenes/spi_mem_scene_read_filename.c | 46 - .../scenes/spi_mem_scene_saved_file_menu.c | 76 - .../scenes/spi_mem_scene_select_file.c | 22 - .../scenes/spi_mem_scene_select_model.c | 45 - .../scenes/spi_mem_scene_select_vendor.c | 70 - .../scenes/spi_mem_scene_start.c | 84 - .../scenes/spi_mem_scene_storage_error.c | 56 - .../scenes/spi_mem_scene_success.c | 40 - .../scenes/spi_mem_scene_verify.c | 59 - .../scenes/spi_mem_scene_verify_error.c | 43 - .../scenes/spi_mem_scene_wiring.c | 18 - .../scenes/spi_mem_scene_write.c | 58 - .../external/spi_mem_manager/spi_mem_app.c | 112 -- .../external/spi_mem_manager/spi_mem_app.h | 3 - .../external/spi_mem_manager/spi_mem_app_i.h | 78 - .../external/spi_mem_manager/spi_mem_files.c | 68 - .../external/spi_mem_manager/spi_mem_files.h | 13 - .../external/spi_mem_manager/tools/README.md | 7 - .../spi_mem_manager/tools/chiplist/LICENSE | 22 - .../tools/chiplist/chiplist.xml | 984 ------------ .../spi_mem_manager/tools/chiplist_convert.py | 109 -- .../views/spi_mem_view_detect.c | 64 - .../views/spi_mem_view_detect.h | 9 - .../views/spi_mem_view_progress.c | 230 --- .../views/spi_mem_view_progress.h | 26 - .../external/weather_station/application.fam | 13 - .../helpers/weather_station_event.h | 14 - .../helpers/weather_station_types.h | 49 - .../weather_station/images/Humid_10x15.png | Bin 3624 -> 0 bytes .../weather_station/images/Humid_8x13.png | Bin 3618 -> 0 bytes .../weather_station/images/Lock_7x8.png | Bin 3597 -> 0 bytes .../images/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes .../weather_station/images/Quest_7x8.png | Bin 3675 -> 0 bytes .../images/Scanning_123x52.png | Bin 1690 -> 0 bytes .../weather_station/images/Therm_7x16.png | Bin 3611 -> 0 bytes .../weather_station/images/Timer_11x11.png | Bin 3616 -> 0 bytes .../weather_station/images/Unlock_7x8.png | Bin 3598 -> 0 bytes .../images/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../weather_station/images/station_icon.png | Bin 3607 -> 0 bytes .../protocols/acurite_592txr.c | 298 ---- .../protocols/acurite_592txr.h | 80 - .../weather_station/protocols/acurite_606tx.c | 239 --- .../weather_station/protocols/acurite_606tx.h | 80 - .../protocols/acurite_609txc.c | 239 --- .../protocols/acurite_609txc.h | 80 - .../protocols/ambient_weather.c | 268 ---- .../protocols/ambient_weather.h | 80 - .../protocols/auriol_hg0601a.c | 248 --- .../protocols/auriol_hg0601a.h | 80 - .../weather_station/protocols/gt_wt_02.c | 255 --- .../weather_station/protocols/gt_wt_02.h | 80 - .../weather_station/protocols/gt_wt_03.c | 330 ---- .../weather_station/protocols/gt_wt_03.h | 80 - .../weather_station/protocols/infactory.c | 286 ---- .../weather_station/protocols/infactory.h | 80 - .../weather_station/protocols/lacrosse_tx.c | 319 ---- .../weather_station/protocols/lacrosse_tx.h | 80 - .../protocols/lacrosse_tx141thbv2.c | 294 ---- .../protocols/lacrosse_tx141thbv2.h | 81 - .../weather_station/protocols/nexus_th.c | 254 --- .../weather_station/protocols/nexus_th.h | 80 - .../weather_station/protocols/oregon2.c | 429 ----- .../weather_station/protocols/oregon2.h | 6 - .../weather_station/protocols/oregon3.c | 365 ----- .../weather_station/protocols/oregon3.h | 6 - .../weather_station/protocols/oregon_v1.c | 321 ---- .../weather_station/protocols/oregon_v1.h | 80 - .../protocols/protocol_items.c | 25 - .../protocols/protocol_items.h | 22 - .../weather_station/protocols/thermopro_tx4.c | 251 --- .../weather_station/protocols/thermopro_tx4.h | 80 - .../weather_station/protocols/tx_8300.c | 284 ---- .../weather_station/protocols/tx_8300.h | 80 - .../weather_station/protocols/wendox_w6726.c | 299 ---- .../weather_station/protocols/wendox_w6726.h | 80 - .../weather_station/protocols/ws_generic.c | 256 --- .../weather_station/protocols/ws_generic.h | 81 - .../scenes/weather_station_receiver.c | 211 --- .../scenes/weather_station_scene.c | 30 - .../scenes/weather_station_scene.h | 29 - .../scenes/weather_station_scene_about.c | 78 - .../scenes/weather_station_scene_config.h | 5 - .../weather_station_scene_receiver_config.c | 223 --- .../weather_station_scene_receiver_info.c | 50 - .../scenes/weather_station_scene_start.c | 58 - .../views/weather_station_receiver.c | 464 ------ .../views/weather_station_receiver.h | 38 - .../views/weather_station_receiver_info.c | 245 --- .../views/weather_station_receiver_info.h | 16 - .../weather_station/weather_station_10px.png | Bin 175 -> 0 bytes .../weather_station/weather_station_app.c | 178 --- .../weather_station/weather_station_app_i.c | 156 -- .../weather_station/weather_station_app_i.h | 73 - .../weather_station/weather_station_history.c | 245 --- .../weather_station/weather_station_history.h | 112 -- applications/main/application.fam | 3 + .../{external => main}/clock/application.fam | 0 .../{external => main}/clock/clock.png | Bin .../{external => main}/clock/clock_app.c | 0 .../music_player/application.fam | 0 .../music_player/icons/music_10px.png | Bin .../music_player/music_player.c | 0 .../snake_game/application.fam | 0 .../snake_game/snake_10px.png | Bin .../snake_game/snake_game.c | 0 documentation/Doxyfile | 3 +- site_scons/commandline.scons | 1 - 411 files changed, 7 insertions(+), 35705 deletions(-) delete mode 100644 applications/external/application.fam delete mode 100644 applications/external/avr_isp_programmer/application.fam delete mode 100644 applications/external/avr_isp_programmer/avr_app_icon_10x10.png delete mode 100644 applications/external/avr_isp_programmer/avr_isp_app.c delete mode 100644 applications/external/avr_isp_programmer/avr_isp_app_i.c delete mode 100644 applications/external/avr_isp_programmer/avr_isp_app_i.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp.c delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_event.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_types.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker.c delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h delete mode 100644 applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c delete mode 100644 applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h delete mode 100644 applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png delete mode 100644 applications/external/avr_isp_programmer/images/avr_wiring.png delete mode 100644 applications/external/avr_isp_programmer/images/chif_not_found_83x37.png delete mode 100644 applications/external/avr_isp_programmer/images/chip_error_70x22.png delete mode 100644 applications/external/avr_isp_programmer/images/chip_long_70x22.png delete mode 100644 applications/external/avr_isp_programmer/images/chip_not_found_83x37.png delete mode 100644 applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png delete mode 100644 applications/external/avr_isp_programmer/images/isp_active_128x53.png delete mode 100644 applications/external/avr_isp_programmer/images/link_waiting_77x56.png delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.c delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h delete mode 100644 applications/external/avr_isp_programmer/lib/driver/clock.png delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene.h delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_reader.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_reader.h delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_writer.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_writer.h delete mode 100644 applications/external/dap_link/README.md delete mode 100644 applications/external/dap_link/application.fam delete mode 100644 applications/external/dap_link/dap_config.h delete mode 100644 applications/external/dap_link/dap_link.c delete mode 100644 applications/external/dap_link/dap_link.h delete mode 100644 applications/external/dap_link/dap_link.png delete mode 100644 applications/external/dap_link/gui/dap_gui.c delete mode 100644 applications/external/dap_link/gui/dap_gui.h delete mode 100644 applications/external/dap_link/gui/dap_gui_custom_event.h delete mode 100644 applications/external/dap_link/gui/dap_gui_i.h delete mode 100644 applications/external/dap_link/gui/scenes/config/dap_scene.c delete mode 100644 applications/external/dap_link/gui/scenes/config/dap_scene.h delete mode 100644 applications/external/dap_link/gui/scenes/config/dap_scene_config.h delete mode 100644 applications/external/dap_link/gui/scenes/dap_scene_about.c delete mode 100644 applications/external/dap_link/gui/scenes/dap_scene_config.c delete mode 100644 applications/external/dap_link/gui/scenes/dap_scene_help.c delete mode 100644 applications/external/dap_link/gui/scenes/dap_scene_main.c delete mode 100644 applications/external/dap_link/gui/views/dap_main_view.c delete mode 100644 applications/external/dap_link/gui/views/dap_main_view.h delete mode 100644 applications/external/dap_link/icons/ActiveConnection_50x64.png delete mode 100644 applications/external/dap_link/icons/ArrowUpEmpty_12x18.png delete mode 100644 applications/external/dap_link/icons/ArrowUpFilled_12x18.png delete mode 160000 applications/external/dap_link/lib/free-dap delete mode 100644 applications/external/dap_link/usb/dap_v2_usb.c delete mode 100644 applications/external/dap_link/usb/dap_v2_usb.h delete mode 100644 applications/external/dap_link/usb/usb_winusb.h delete mode 100644 applications/external/hid_app/application.fam delete mode 100644 applications/external/hid_app/assets/Arr_dwn_7x9.png delete mode 100644 applications/external/hid_app/assets/Arr_up_7x9.png delete mode 100644 applications/external/hid_app/assets/Ble_connected_15x15.png delete mode 100644 applications/external/hid_app/assets/Ble_disconnected_15x15.png delete mode 100644 applications/external/hid_app/assets/ButtonDown_7x4.png delete mode 100644 applications/external/hid_app/assets/ButtonF10_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF11_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF12_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF1_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF2_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF3_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF4_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF5_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF6_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF7_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF8_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF9_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonLeft_4x7.png delete mode 100644 applications/external/hid_app/assets/ButtonRight_4x7.png delete mode 100644 applications/external/hid_app/assets/ButtonUp_7x4.png delete mode 100644 applications/external/hid_app/assets/Button_18x18.png delete mode 100644 applications/external/hid_app/assets/Circles_47x47.png delete mode 100644 applications/external/hid_app/assets/Left_mouse_icon_9x9.png delete mode 100644 applications/external/hid_app/assets/Like_def_11x9.png delete mode 100644 applications/external/hid_app/assets/Like_pressed_17x17.png delete mode 100644 applications/external/hid_app/assets/Ok_btn_9x9.png delete mode 100644 applications/external/hid_app/assets/Ok_btn_pressed_13x13.png delete mode 100644 applications/external/hid_app/assets/Pin_arrow_down_7x9.png delete mode 100644 applications/external/hid_app/assets/Pin_arrow_left_9x7.png delete mode 100644 applications/external/hid_app/assets/Pin_arrow_right_9x7.png delete mode 100644 applications/external/hid_app/assets/Pin_arrow_up_7x9.png delete mode 100644 applications/external/hid_app/assets/Pin_back_arrow_10x8.png delete mode 100644 applications/external/hid_app/assets/Pressed_Button_13x13.png delete mode 100644 applications/external/hid_app/assets/Right_mouse_icon_9x9.png delete mode 100644 applications/external/hid_app/assets/Space_60x18.png delete mode 100644 applications/external/hid_app/assets/Space_65x18.png delete mode 100644 applications/external/hid_app/assets/Voldwn_6x6.png delete mode 100644 applications/external/hid_app/assets/Volup_8x6.png delete mode 100644 applications/external/hid_app/hid.c delete mode 100644 applications/external/hid_app/hid.h delete mode 100644 applications/external/hid_app/hid_ble_10px.png delete mode 100644 applications/external/hid_app/hid_usb_10px.png delete mode 100644 applications/external/hid_app/views.h delete mode 100644 applications/external/hid_app/views/hid_keyboard.c delete mode 100644 applications/external/hid_app/views/hid_keyboard.h delete mode 100644 applications/external/hid_app/views/hid_keynote.c delete mode 100644 applications/external/hid_app/views/hid_keynote.h delete mode 100644 applications/external/hid_app/views/hid_media.c delete mode 100644 applications/external/hid_app/views/hid_media.h delete mode 100644 applications/external/hid_app/views/hid_mouse.c delete mode 100644 applications/external/hid_app/views/hid_mouse.h delete mode 100644 applications/external/hid_app/views/hid_mouse_clicker.c delete mode 100644 applications/external/hid_app/views/hid_mouse_clicker.h delete mode 100644 applications/external/hid_app/views/hid_mouse_jiggler.c delete mode 100644 applications/external/hid_app/views/hid_mouse_jiggler.h delete mode 100644 applications/external/hid_app/views/hid_tiktok.c delete mode 100644 applications/external/hid_app/views/hid_tiktok.h delete mode 100644 applications/external/mfkey32/application.fam delete mode 100644 applications/external/mfkey32/images/mfkey.png delete mode 100644 applications/external/mfkey32/mfkey.png delete mode 100644 applications/external/mfkey32/mfkey32.c delete mode 100644 applications/external/nfc_magic/application.fam delete mode 100644 applications/external/nfc_magic/assets/DolphinCommon_56x48.png delete mode 100644 applications/external/nfc_magic/assets/DolphinNice_96x59.png delete mode 100644 applications/external/nfc_magic/assets/Loading_24.png delete mode 100644 applications/external/nfc_magic/assets/NFC_manual_60x50.png delete mode 100644 applications/external/nfc_magic/lib/magic/classic_gen1.c delete mode 100644 applications/external/nfc_magic/lib/magic/classic_gen1.h delete mode 100644 applications/external/nfc_magic/lib/magic/common.c delete mode 100644 applications/external/nfc_magic/lib/magic/common.h delete mode 100644 applications/external/nfc_magic/lib/magic/gen4.c delete mode 100644 applications/external/nfc_magic/lib/magic/gen4.h delete mode 100644 applications/external/nfc_magic/lib/magic/types.c delete mode 100644 applications/external/nfc_magic/lib/magic/types.h delete mode 100644 applications/external/nfc_magic/nfc_magic.c delete mode 100644 applications/external/nfc_magic/nfc_magic.h delete mode 100644 applications/external/nfc_magic/nfc_magic_i.h delete mode 100644 applications/external/nfc_magic/nfc_magic_worker.c delete mode 100644 applications/external/nfc_magic/nfc_magic_worker.h delete mode 100644 applications/external/nfc_magic/nfc_magic_worker_i.h delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene.h delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_actions.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_check.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_config.h delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_gen4_actions.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_key_input.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_new_key_input.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_rekey.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_rekey_fail.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_start.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_success.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_write.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_write_confirm.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_write_fail.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c delete mode 100644 applications/external/nfc_rfid_detector/application.fam delete mode 100644 applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h delete mode 100644 applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h delete mode 100644 applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png delete mode 100644 applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png delete mode 100644 applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png delete mode 100644 applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png delete mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png delete mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c delete mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c delete mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c delete mode 100644 applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c delete mode 100644 applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h delete mode 100644 applications/external/picopass/125_10px.png delete mode 100644 applications/external/picopass/application.fam delete mode 100644 applications/external/picopass/helpers/iclass_elite_dict.c delete mode 100644 applications/external/picopass/helpers/iclass_elite_dict.h delete mode 100644 applications/external/picopass/icons/DolphinMafia_115x62.png delete mode 100644 applications/external/picopass/icons/DolphinNice_96x59.png delete mode 100644 applications/external/picopass/icons/Nfc_10px.png delete mode 100644 applications/external/picopass/icons/RFIDDolphinReceive_97x61.png delete mode 100644 applications/external/picopass/icons/RFIDDolphinSend_97x61.png delete mode 100644 applications/external/picopass/lib/loclass/optimized_cipher.c delete mode 100644 applications/external/picopass/lib/loclass/optimized_cipher.h delete mode 100644 applications/external/picopass/lib/loclass/optimized_cipherutils.c delete mode 100644 applications/external/picopass/lib/loclass/optimized_cipherutils.h delete mode 100644 applications/external/picopass/lib/loclass/optimized_elite.c delete mode 100644 applications/external/picopass/lib/loclass/optimized_elite.h delete mode 100644 applications/external/picopass/lib/loclass/optimized_ikeys.c delete mode 100644 applications/external/picopass/lib/loclass/optimized_ikeys.h delete mode 100644 applications/external/picopass/picopass.c delete mode 100644 applications/external/picopass/picopass.h delete mode 100644 applications/external/picopass/picopass_device.c delete mode 100644 applications/external/picopass/picopass_device.h delete mode 100644 applications/external/picopass/picopass_i.h delete mode 100644 applications/external/picopass/picopass_keys.c delete mode 100644 applications/external/picopass/picopass_keys.h delete mode 100644 applications/external/picopass/picopass_worker.c delete mode 100644 applications/external/picopass/picopass_worker.h delete mode 100644 applications/external/picopass/picopass_worker_i.h delete mode 100644 applications/external/picopass/rfal_picopass.c delete mode 100644 applications/external/picopass/rfal_picopass.h delete mode 100644 applications/external/picopass/scenes/picopass_scene.c delete mode 100644 applications/external/picopass/scenes/picopass_scene.h delete mode 100644 applications/external/picopass/scenes/picopass_scene_card_menu.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_config.h delete mode 100644 applications/external/picopass/scenes/picopass_scene_delete.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_delete_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_device_info.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_file_select.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_key_menu.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_read_card.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_read_card_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_read_factory_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_save_name.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_save_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_saved_menu.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_start.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_write_card.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_write_card_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_write_key.c delete mode 100644 applications/external/picopass/views/dict_attack.c delete mode 100644 applications/external/picopass/views/dict_attack.h delete mode 100644 applications/external/signal_generator/application.fam delete mode 100644 applications/external/signal_generator/icons/SmallArrowDown_3x5.png delete mode 100644 applications/external/signal_generator/icons/SmallArrowUp_3x5.png delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene.c delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene.h delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene_config.h delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene_mco.c delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene_pwm.c delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene_start.c delete mode 100644 applications/external/signal_generator/signal_gen_10px.png delete mode 100644 applications/external/signal_generator/signal_gen_app.c delete mode 100644 applications/external/signal_generator/signal_gen_app_i.h delete mode 100644 applications/external/signal_generator/views/signal_gen_pwm.c delete mode 100644 applications/external/signal_generator/views/signal_gen_pwm.h delete mode 100644 applications/external/spi_mem_manager/application.fam delete mode 100644 applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_01.png delete mode 100644 applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_02.png delete mode 100644 applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_03.png delete mode 100644 applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_rate delete mode 100644 applications/external/spi_mem_manager/images/Dip8_10px.png delete mode 100644 applications/external/spi_mem_manager/images/Dip8_32x36.png delete mode 100644 applications/external/spi_mem_manager/images/DolphinMafia_115x62.png delete mode 100644 applications/external/spi_mem_manager/images/DolphinNice_96x59.png delete mode 100644 applications/external/spi_mem_manager/images/SDQuestion_35x43.png delete mode 100644 applications/external/spi_mem_manager/images/Wiring_SPI_128x64.png delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_chip.c delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_chip.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_chip_arr.c delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_chip_i.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_tools.c delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_tools.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_worker.c delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_worker.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_worker_i.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_worker_modes.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene.h delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_about.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_error.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_config.h delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_erase.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_file_info.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_read.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_read_filename.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_select_file.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_select_model.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_start.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_storage_error.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_success.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_verify.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_verify_error.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_wiring.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_write.c delete mode 100644 applications/external/spi_mem_manager/spi_mem_app.c delete mode 100644 applications/external/spi_mem_manager/spi_mem_app.h delete mode 100644 applications/external/spi_mem_manager/spi_mem_app_i.h delete mode 100644 applications/external/spi_mem_manager/spi_mem_files.c delete mode 100644 applications/external/spi_mem_manager/spi_mem_files.h delete mode 100644 applications/external/spi_mem_manager/tools/README.md delete mode 100644 applications/external/spi_mem_manager/tools/chiplist/LICENSE delete mode 100644 applications/external/spi_mem_manager/tools/chiplist/chiplist.xml delete mode 100755 applications/external/spi_mem_manager/tools/chiplist_convert.py delete mode 100644 applications/external/spi_mem_manager/views/spi_mem_view_detect.c delete mode 100644 applications/external/spi_mem_manager/views/spi_mem_view_detect.h delete mode 100644 applications/external/spi_mem_manager/views/spi_mem_view_progress.c delete mode 100644 applications/external/spi_mem_manager/views/spi_mem_view_progress.h delete mode 100644 applications/external/weather_station/application.fam delete mode 100644 applications/external/weather_station/helpers/weather_station_event.h delete mode 100644 applications/external/weather_station/helpers/weather_station_types.h delete mode 100644 applications/external/weather_station/images/Humid_10x15.png delete mode 100644 applications/external/weather_station/images/Humid_8x13.png delete mode 100644 applications/external/weather_station/images/Lock_7x8.png delete mode 100644 applications/external/weather_station/images/Pin_back_arrow_10x8.png delete mode 100644 applications/external/weather_station/images/Quest_7x8.png delete mode 100644 applications/external/weather_station/images/Scanning_123x52.png delete mode 100644 applications/external/weather_station/images/Therm_7x16.png delete mode 100644 applications/external/weather_station/images/Timer_11x11.png delete mode 100644 applications/external/weather_station/images/Unlock_7x8.png delete mode 100644 applications/external/weather_station/images/WarningDolphin_45x42.png delete mode 100644 applications/external/weather_station/images/station_icon.png delete mode 100644 applications/external/weather_station/protocols/acurite_592txr.c delete mode 100644 applications/external/weather_station/protocols/acurite_592txr.h delete mode 100644 applications/external/weather_station/protocols/acurite_606tx.c delete mode 100644 applications/external/weather_station/protocols/acurite_606tx.h delete mode 100644 applications/external/weather_station/protocols/acurite_609txc.c delete mode 100644 applications/external/weather_station/protocols/acurite_609txc.h delete mode 100644 applications/external/weather_station/protocols/ambient_weather.c delete mode 100644 applications/external/weather_station/protocols/ambient_weather.h delete mode 100644 applications/external/weather_station/protocols/auriol_hg0601a.c delete mode 100644 applications/external/weather_station/protocols/auriol_hg0601a.h delete mode 100644 applications/external/weather_station/protocols/gt_wt_02.c delete mode 100644 applications/external/weather_station/protocols/gt_wt_02.h delete mode 100644 applications/external/weather_station/protocols/gt_wt_03.c delete mode 100644 applications/external/weather_station/protocols/gt_wt_03.h delete mode 100644 applications/external/weather_station/protocols/infactory.c delete mode 100644 applications/external/weather_station/protocols/infactory.h delete mode 100644 applications/external/weather_station/protocols/lacrosse_tx.c delete mode 100644 applications/external/weather_station/protocols/lacrosse_tx.h delete mode 100644 applications/external/weather_station/protocols/lacrosse_tx141thbv2.c delete mode 100644 applications/external/weather_station/protocols/lacrosse_tx141thbv2.h delete mode 100644 applications/external/weather_station/protocols/nexus_th.c delete mode 100644 applications/external/weather_station/protocols/nexus_th.h delete mode 100644 applications/external/weather_station/protocols/oregon2.c delete mode 100644 applications/external/weather_station/protocols/oregon2.h delete mode 100644 applications/external/weather_station/protocols/oregon3.c delete mode 100644 applications/external/weather_station/protocols/oregon3.h delete mode 100644 applications/external/weather_station/protocols/oregon_v1.c delete mode 100644 applications/external/weather_station/protocols/oregon_v1.h delete mode 100644 applications/external/weather_station/protocols/protocol_items.c delete mode 100644 applications/external/weather_station/protocols/protocol_items.h delete mode 100644 applications/external/weather_station/protocols/thermopro_tx4.c delete mode 100644 applications/external/weather_station/protocols/thermopro_tx4.h delete mode 100644 applications/external/weather_station/protocols/tx_8300.c delete mode 100644 applications/external/weather_station/protocols/tx_8300.h delete mode 100644 applications/external/weather_station/protocols/wendox_w6726.c delete mode 100644 applications/external/weather_station/protocols/wendox_w6726.h delete mode 100644 applications/external/weather_station/protocols/ws_generic.c delete mode 100644 applications/external/weather_station/protocols/ws_generic.h delete mode 100644 applications/external/weather_station/scenes/weather_station_receiver.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene.h delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_about.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_config.h delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_receiver_config.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_receiver_info.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_start.c delete mode 100644 applications/external/weather_station/views/weather_station_receiver.c delete mode 100644 applications/external/weather_station/views/weather_station_receiver.h delete mode 100644 applications/external/weather_station/views/weather_station_receiver_info.c delete mode 100644 applications/external/weather_station/views/weather_station_receiver_info.h delete mode 100644 applications/external/weather_station/weather_station_10px.png delete mode 100644 applications/external/weather_station/weather_station_app.c delete mode 100644 applications/external/weather_station/weather_station_app_i.c delete mode 100644 applications/external/weather_station/weather_station_app_i.h delete mode 100644 applications/external/weather_station/weather_station_history.c delete mode 100644 applications/external/weather_station/weather_station_history.h rename applications/{external => main}/clock/application.fam (100%) rename applications/{external => main}/clock/clock.png (100%) rename applications/{external => main}/clock/clock_app.c (100%) rename applications/{external => main}/music_player/application.fam (100%) rename applications/{external => main}/music_player/icons/music_10px.png (100%) rename applications/{external => main}/music_player/music_player.c (100%) rename applications/{external => main}/snake_game/application.fam (100%) rename applications/{external => main}/snake_game/snake_10px.png (100%) rename applications/{external => main}/snake_game/snake_game.c (100%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bed54dfa0..6626f9ae2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -22,9 +22,6 @@ /applications/main/subghz/ @skotopes @DrZlo13 @hedger @Skorpionm /applications/main/u2f/ @skotopes @DrZlo13 @hedger @nminaylov -/applications/external/bt_hid_app/ @skotopes @DrZlo13 @hedger @gornekich -/applications/external/picopass/ @skotopes @DrZlo13 @hedger @gornekich - /applications/services/bt/ @skotopes @DrZlo13 @hedger @gornekich /applications/services/cli/ @skotopes @DrZlo13 @hedger @nminaylov /applications/services/crypto/ @skotopes @DrZlo13 @hedger @nminaylov diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9fbcb16eb..b9eb4d709 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -201,7 +201,7 @@ jobs: - name: Build example & external apps with uFBT run: | - for appdir in 'applications/external' 'applications/examples'; do + for appdir in 'applications/examples'; do for app in $(find "$appdir" -maxdepth 1 -mindepth 1 -type d); do pushd $app TARGETS_FAM=$(grep "targets" application.fam || echo "${{ matrix.target }}") diff --git a/.gitmodules b/.gitmodules index 4fba0483e..52cf4a207 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,9 +26,6 @@ [submodule "lib/cxxheaderparser"] path = lib/cxxheaderparser url = https://github.com/robotpy/cxxheaderparser.git -[submodule "applications/external/dap_link/lib/free-dap"] - path = applications/external/dap_link/lib/free-dap - url = https://github.com/ataradov/free-dap.git [submodule "lib/heatshrink"] path = lib/heatshrink url = https://github.com/flipperdevices/heatshrink.git diff --git a/.pvsoptions b/.pvsoptions index b25d4633e..2e0b2fb8d 100644 --- a/.pvsoptions +++ b/.pvsoptions @@ -1 +1 @@ ---ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* -e applications/external/dap_link/lib/free-dap +--ignore-ccache -C gccarm --rules-config .pvsconfig -e lib/cmsis_core -e lib/fatfs -e lib/fnv1a-hash -e lib/FreeRTOS-Kernel -e lib/heatshrink -e lib/libusb_stm32 -e lib/littlefs -e lib/mbedtls -e lib/micro-ecc -e lib/microtar -e lib/mlib -e lib/qrcode -e lib/ST25RFAL002 -e lib/stm32wb_cmsis -e lib/stm32wb_copro -e lib/stm32wb_hal -e lib/u8g2 -e lib/nanopb -e */arm-none-eabi/* diff --git a/applications/ReadMe.md b/applications/ReadMe.md index 10e54ce22..91ef02bbb 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -33,22 +33,10 @@ Applications for main Flipper menu. - `nfc` - NFC application, HF rfid, EMV and etc - `subghz` - SubGhz application, 433 fobs and etc - `u2f` - U2F Application - - -## External - -External applications deployed to SD Card - - `clock` - Clock application -- `dap_link` - DAP Link OnChip debugger -- `hid_app` - USB/BT Remote controller - `music_player` - Music player app (demo) -- `nfc_magic` - NFC MFC Magic card application -- `picopass` - Picopass reader / writer -- `signal_generator` - Signal generator app: PWM and clock generator - `snake_game` - Snake game application -- `spi_mem_manager` - SPI Memory reader / flasher -- `weather_station` - SubGHz weather station + ## services diff --git a/applications/external/application.fam b/applications/external/application.fam deleted file mode 100644 index 12dc1cc1a..000000000 --- a/applications/external/application.fam +++ /dev/null @@ -1,6 +0,0 @@ -# Placeholder -App( - appid="external_apps", - name="External apps bundle", - apptype=FlipperAppType.METAPACKAGE, -) diff --git a/applications/external/avr_isp_programmer/application.fam b/applications/external/avr_isp_programmer/application.fam deleted file mode 100644 index 19556d03d..000000000 --- a/applications/external/avr_isp_programmer/application.fam +++ /dev/null @@ -1,17 +0,0 @@ -App( - appid="avr_isp", - name="AVR Flasher", - apptype=FlipperAppType.EXTERNAL, - entry_point="avr_isp_app", - requires=["gui"], - stack_size=4 * 1024, - order=20, - fap_icon="avr_app_icon_10x10.png", - fap_category="GPIO", - fap_icon_assets="images", - fap_private_libs=[ - Lib( - name="driver", - ), - ], -) diff --git a/applications/external/avr_isp_programmer/avr_app_icon_10x10.png b/applications/external/avr_isp_programmer/avr_app_icon_10x10.png deleted file mode 100644 index 533787fe3569f70196d3f71e8373102dfd3967a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3614 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 Vpqb^9w@m;7?5&;gRaoD1{{|C}E&c!i diff --git a/applications/external/avr_isp_programmer/avr_isp_app.c b/applications/external/avr_isp_programmer/avr_isp_app.c deleted file mode 100644 index 740dc3610..000000000 --- a/applications/external/avr_isp_programmer/avr_isp_app.c +++ /dev/null @@ -1,179 +0,0 @@ -#include "avr_isp_app_i.h" - -static bool avr_isp_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - AvrIspApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool avr_isp_app_back_event_callback(void* context) { - furi_assert(context); - AvrIspApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void avr_isp_app_tick_event_callback(void* context) { - furi_assert(context); - AvrIspApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -AvrIspApp* avr_isp_app_alloc() { - AvrIspApp* app = malloc(sizeof(AvrIspApp)); - - app->file_path = furi_string_alloc(); - furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); - app->error = AvrIspErrorNoError; - - // GUI - app->gui = furi_record_open(RECORD_GUI); - - // View Dispatcher - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&avr_isp_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, avr_isp_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, avr_isp_app_back_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, avr_isp_app_tick_event_callback, 100); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - // Open Notification record - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - // SubMenu - app->submenu = submenu_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, AvrIspViewSubmenu, submenu_get_view(app->submenu)); - - // Widget - app->widget = widget_alloc(); - view_dispatcher_add_view(app->view_dispatcher, AvrIspViewWidget, widget_get_view(app->widget)); - - // Text Input - app->text_input = text_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, AvrIspViewTextInput, text_input_get_view(app->text_input)); - - // Popup - app->popup = popup_alloc(); - view_dispatcher_add_view(app->view_dispatcher, AvrIspViewPopup, popup_get_view(app->popup)); - - //Dialog - app->dialogs = furi_record_open(RECORD_DIALOGS); - - // Programmer view - app->avr_isp_programmer_view = avr_isp_programmer_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - AvrIspViewProgrammer, - avr_isp_programmer_view_get_view(app->avr_isp_programmer_view)); - - // Reader view - app->avr_isp_reader_view = avr_isp_reader_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - AvrIspViewReader, - avr_isp_reader_view_get_view(app->avr_isp_reader_view)); - - // Writer view - app->avr_isp_writer_view = avr_isp_writer_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - AvrIspViewWriter, - avr_isp_writer_view_get_view(app->avr_isp_writer_view)); - - // Chip detect view - app->avr_isp_chip_detect_view = avr_isp_chip_detect_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - AvrIspViewChipDetect, - avr_isp_chip_detect_view_get_view(app->avr_isp_chip_detect_view)); - - // Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); - } - - scene_manager_next_scene(app->scene_manager, AvrIspSceneStart); - - return app; -} //-V773 - -void avr_isp_app_free(AvrIspApp* app) { - furi_assert(app); - - // Disable 5v power - if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } - - // Submenu - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewSubmenu); - submenu_free(app->submenu); - - // Widget - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWidget); - widget_free(app->widget); - - // TextInput - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewTextInput); - text_input_free(app->text_input); - - // Popup - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewPopup); - popup_free(app->popup); - - //Dialog - furi_record_close(RECORD_DIALOGS); - - // Programmer view - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewProgrammer); - avr_isp_programmer_view_free(app->avr_isp_programmer_view); - - // Reader view - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewReader); - avr_isp_reader_view_free(app->avr_isp_reader_view); - - // Writer view - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWriter); - avr_isp_writer_view_free(app->avr_isp_writer_view); - - // Chip detect view - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewChipDetect); - avr_isp_chip_detect_view_free(app->avr_isp_chip_detect_view); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Notifications - furi_record_close(RECORD_NOTIFICATION); - app->notifications = NULL; - - // Close records - furi_record_close(RECORD_GUI); - - // Path strings - furi_string_free(app->file_path); - - free(app); -} - -int32_t avr_isp_app(void* p) { - UNUSED(p); - AvrIspApp* avr_isp_app = avr_isp_app_alloc(); - - view_dispatcher_run(avr_isp_app->view_dispatcher); - - avr_isp_app_free(avr_isp_app); - - return 0; -} diff --git a/applications/external/avr_isp_programmer/avr_isp_app_i.c b/applications/external/avr_isp_programmer/avr_isp_app_i.c deleted file mode 100644 index 7a7fa6d7f..000000000 --- a/applications/external/avr_isp_programmer/avr_isp_app_i.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "avr_isp_app_i.h" -#include -#include - -#define TAG "AvrIsp" - -bool avr_isp_load_from_file(AvrIspApp* app) { - furi_assert(app); - - FuriString* file_path = furi_string_alloc(); - FuriString* file_name = furi_string_alloc(); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, AVR_ISP_APP_EXTENSION, &I_avr_app_icon_10x10); - browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; - - // Input events and views are managed by file_select - bool res = dialog_file_browser_show(app->dialogs, file_path, app->file_path, &browser_options); - - if(res) { - path_extract_dirname(furi_string_get_cstr(file_path), app->file_path); - path_extract_filename(file_path, file_name, true); - strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME); - } - - furi_string_free(file_name); - furi_string_free(file_path); - - return res; -} diff --git a/applications/external/avr_isp_programmer/avr_isp_app_i.h b/applications/external/avr_isp_programmer/avr_isp_app_i.h deleted file mode 100644 index 17c69f8f2..000000000 --- a/applications/external/avr_isp_programmer/avr_isp_app_i.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "helpers/avr_isp_types.h" -#include - -#include "scenes/avr_isp_scene.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "views/avr_isp_view_programmer.h" -#include "views/avr_isp_view_reader.h" -#include "views/avr_isp_view_writer.h" -#include "views/avr_isp_view_chip_detect.h" - -#define AVR_ISP_MAX_LEN_NAME 64 - -typedef struct { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Popup* popup; - Submenu* submenu; - Widget* widget; - TextInput* text_input; - FuriString* file_path; - char file_name_tmp[AVR_ISP_MAX_LEN_NAME]; - AvrIspProgrammerView* avr_isp_programmer_view; - AvrIspReaderView* avr_isp_reader_view; - AvrIspWriterView* avr_isp_writer_view; - AvrIspChipDetectView* avr_isp_chip_detect_view; - AvrIspError error; -} AvrIspApp; - -bool avr_isp_load_from_file(AvrIspApp* app); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c deleted file mode 100644 index 283c17bfd..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ /dev/null @@ -1,496 +0,0 @@ -#include "avr_isp.h" -#include "../lib/driver/avr_isp_prog_cmd.h" -#include "../lib/driver/avr_isp_spi_sw.h" - -#include - -#define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 -#define TAG "AvrIsp" - -struct AvrIsp { - AvrIspSpiSw* spi; - bool pmode; - AvrIspCallback callback; - void* context; -}; - -AvrIsp* avr_isp_alloc(void) { - AvrIsp* instance = malloc(sizeof(AvrIsp)); - return instance; -} - -void avr_isp_free(AvrIsp* instance) { - furi_assert(instance); - - if(instance->spi) avr_isp_end_pmode(instance); - free(instance); -} - -void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context) { - furi_assert(instance); - furi_assert(context); - - instance->callback = callback; - instance->context = context; -} - -uint8_t avr_isp_spi_transaction( - AvrIsp* instance, - uint8_t cmd, - uint8_t addr_hi, - uint8_t addr_lo, - uint8_t data) { - furi_assert(instance); - - avr_isp_spi_sw_txrx(instance->spi, cmd); - avr_isp_spi_sw_txrx(instance->spi, addr_hi); - avr_isp_spi_sw_txrx(instance->spi, addr_lo); - return avr_isp_spi_sw_txrx(instance->spi, data); -} - -static bool avr_isp_set_pmode(AvrIsp* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { - furi_assert(instance); - - uint8_t res = 0; - avr_isp_spi_sw_txrx(instance->spi, a); - avr_isp_spi_sw_txrx(instance->spi, b); - res = avr_isp_spi_sw_txrx(instance->spi, c); - avr_isp_spi_sw_txrx(instance->spi, d); - return res == 0x53; -} - -void avr_isp_end_pmode(AvrIsp* instance) { - furi_assert(instance); - - if(instance->pmode) { - avr_isp_spi_sw_res_set(instance->spi, true); - // We're about to take the target out of reset - // so configure SPI pins as input - if(instance->spi) avr_isp_spi_sw_free(instance->spi); - instance->spi = NULL; - } - - instance->pmode = false; -} - -static bool avr_isp_start_pmode(AvrIsp* instance, AvrIspSpiSwSpeed spi_speed) { - furi_assert(instance); - - // Reset target before driving PIN_SCK or PIN_MOSI - - // SPI.begin() will configure SS as output, - // so SPI master mode is selected. - // We have defined RESET as pin 10, - // which for many arduino's is not the SS pin. - // So we have to configure RESET as output here, - // (reset_target() first sets the correct level) - if(instance->spi) avr_isp_spi_sw_free(instance->spi); - instance->spi = avr_isp_spi_sw_init(spi_speed); - - avr_isp_spi_sw_res_set(instance->spi, false); - // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm": - - // Pulse RESET after PIN_SCK is low: - avr_isp_spi_sw_sck_set(instance->spi, false); - - // discharge PIN_SCK, value arbitrally chosen - furi_delay_ms(20); - avr_isp_spi_sw_res_set(instance->spi, true); - - // Pulse must be minimum 2 target CPU speed cycles - // so 100 usec is ok for CPU speeds above 20KHz - furi_delay_ms(1); - - avr_isp_spi_sw_res_set(instance->spi, false); - - // Send the enable programming command: - // datasheet: must be > 20 msec - furi_delay_ms(50); - if(avr_isp_set_pmode(instance, AVR_ISP_SET_PMODE)) { - instance->pmode = true; - return true; - } - return false; -} - -bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { - furi_assert(instance); - - AvrIspSpiSwSpeed spi_speed[] = { - AvrIspSpiSwSpeed1Mhz, - AvrIspSpiSwSpeed400Khz, - AvrIspSpiSwSpeed250Khz, - AvrIspSpiSwSpeed125Khz, - AvrIspSpiSwSpeed60Khz, - AvrIspSpiSwSpeed40Khz, - AvrIspSpiSwSpeed20Khz, - AvrIspSpiSwSpeed10Khz, - AvrIspSpiSwSpeed5Khz, - AvrIspSpiSwSpeed1Khz, - }; - for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { - if(avr_isp_start_pmode(instance, spi_speed[i])) { - AvrIspSignature sig = avr_isp_read_signature(instance); - AvrIspSignature sig_examination = avr_isp_read_signature(instance); //-V656 - uint8_t y = 0; - while(y < 8) { - if(memcmp((uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspSignature)) != - 0) - break; - sig_examination = avr_isp_read_signature(instance); - y++; - } - if(y == 8) { - if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { - if(i < (COUNT_OF(spi_speed) - 1)) { - avr_isp_end_pmode(instance); - i++; - return avr_isp_start_pmode(instance, spi_speed[i]); - } - } - return true; - } - } - } - - if(instance->spi) { - avr_isp_spi_sw_free(instance->spi); - instance->spi = NULL; - } - - return false; -} - -static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) { - furi_assert(instance); - - avr_isp_spi_transaction(instance, AVR_ISP_COMMIT(addr)); - /* polling flash */ - if(data == 0xFF) { - furi_delay_ms(5); - } else { - /* polling flash */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { - break; - }; - } - } -} - -static uint16_t avr_isp_current_page(AvrIsp* instance, uint32_t addr, uint16_t page_size) { - furi_assert(instance); - - uint16_t page = 0; - switch(page_size) { - case 32: - page = addr & 0xFFFFFFF0; - break; - case 64: - page = addr & 0xFFFFFFE0; - break; - case 128: - page = addr & 0xFFFFFFC0; - break; - case 256: - page = addr & 0xFFFFFF80; - break; - - default: - page = addr; - break; - } - - return page; -} - -static bool avr_isp_flash_write_pages( - AvrIsp* instance, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - size_t x = 0; - uint16_t page = avr_isp_current_page(instance, addr, page_size); - - while(x < data_size) { - if(page != avr_isp_current_page(instance, addr, page_size)) { - avr_isp_commit(instance, page, data[x - 1]); - page = avr_isp_current_page(instance, addr, page_size); - } - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_LO(addr, data[x++])); - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_HI(addr, data[x++])); - addr++; - } - avr_isp_commit(instance, page, data[x - 1]); - return true; -} - -bool avr_isp_erase_chip(AvrIsp* instance) { - furi_assert(instance); - - bool ret = false; - if(!instance->pmode) avr_isp_auto_set_spi_speed_start_pmode(instance); - if(instance->pmode) { - avr_isp_spi_transaction(instance, AVR_ISP_ERASE_CHIP); - furi_delay_ms(100); - avr_isp_end_pmode(instance); - ret = true; - } - return ret; -} - -static bool - avr_isp_eeprom_write(AvrIsp* instance, uint16_t addr, uint8_t* data, uint32_t data_size) { - furi_assert(instance); - - for(uint16_t i = 0; i < data_size; i++) { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, data[i])); - furi_delay_ms(10); - addr++; - } - return true; -} - -bool avr_isp_write_page( - AvrIsp* instance, - uint32_t mem_type, - uint32_t mem_size, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - bool ret = false; - switch(mem_type) { - case STK_SET_FLASH_TYPE: - if((addr + data_size / 2) <= mem_size) { - ret = avr_isp_flash_write_pages(instance, addr, page_size, data, data_size); - } - break; - - case STK_SET_EEPROM_TYPE: - if((addr + data_size) <= mem_size) { - ret = avr_isp_eeprom_write(instance, addr, data, data_size); - } - break; - - default: - furi_crash(TAG " Incorrect mem type."); - break; - } - - return ret; -} - -static bool avr_isp_flash_read_page( - AvrIsp* instance, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - if(page_size > data_size) return false; - for(uint16_t i = 0; i < page_size; i += 2) { - data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(addr)); - data[i + 1] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)); - addr++; - } - return true; -} - -static bool avr_isp_eeprom_read_page( - AvrIsp* instance, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - if(page_size > data_size) return false; - for(uint16_t i = 0; i < page_size; i++) { - data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr)); - addr++; - } - return true; -} - -bool avr_isp_read_page( - AvrIsp* instance, - uint32_t mem_type, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - bool res = false; - if(mem_type == STK_SET_FLASH_TYPE) - res = avr_isp_flash_read_page(instance, addr, page_size, data, data_size); - if(mem_type == STK_SET_EEPROM_TYPE) - res = avr_isp_eeprom_read_page(instance, addr, page_size, data, data_size); - - return res; -} - -AvrIspSignature avr_isp_read_signature(AvrIsp* instance) { - furi_assert(instance); - - AvrIspSignature signature; - signature.vendor = avr_isp_spi_transaction(instance, AVR_ISP_READ_VENDOR); - signature.part_family = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); - signature.part_number = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); - return signature; -} - -uint8_t avr_isp_read_lock_byte(AvrIsp* instance) { - furi_assert(instance); - - uint8_t data = 0; - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 300) { - data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE); - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) { - break; - }; - data = 0x00; - } - return data; -} - -bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) { - furi_assert(instance); - - bool ret = false; - if(avr_isp_read_lock_byte(instance) == lock) { - ret = true; - } else { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_LOCK_BYTE(lock)); - /* polling lock byte */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) { - ret = true; - break; - }; - } - } - return ret; -} - -uint8_t avr_isp_read_fuse_low(AvrIsp* instance) { - furi_assert(instance); - - uint8_t data = 0; - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 300) { - data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW); - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) { - break; - }; - data = 0x00; - } - return data; -} - -bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) { - furi_assert(instance); - - bool ret = false; - if(avr_isp_read_fuse_low(instance) == lfuse) { - ret = true; - } else { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_LOW(lfuse)); - /* polling fuse */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) { - ret = true; - break; - }; - } - } - return ret; -} - -uint8_t avr_isp_read_fuse_high(AvrIsp* instance) { - furi_assert(instance); - - uint8_t data = 0; - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 300) { - data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH); - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) { - break; - }; - data = 0x00; - } - return data; -} - -bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) { - furi_assert(instance); - - bool ret = false; - if(avr_isp_read_fuse_high(instance) == hfuse) { - ret = true; - } else { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_HIGH(hfuse)); - /* polling fuse */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) { - ret = true; - break; - }; - } - } - return ret; -} - -uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) { - furi_assert(instance); - - uint8_t data = 0; - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 300) { - data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED); - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) { - break; - }; - data = 0x00; - } - return data; -} - -bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) { - furi_assert(instance); - - bool ret = false; - if(avr_isp_read_fuse_extended(instance) == efuse) { - ret = true; - } else { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_EXTENDED(efuse)); - /* polling fuse */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) { - ret = true; - break; - }; - } - } - return ret; -} - -void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr) { - furi_assert(instance); - - avr_isp_spi_transaction(instance, AVR_ISP_EXTENDED_ADDR(extended_addr)); - furi_delay_ms(10); -} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.h b/applications/external/avr_isp_programmer/helpers/avr_isp.h deleted file mode 100644 index 476fc3d64..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include - -typedef struct AvrIsp AvrIsp; -typedef void (*AvrIspCallback)(void* context); - -struct AvrIspSignature { - uint8_t vendor; - uint8_t part_family; - uint8_t part_number; -}; - -typedef struct AvrIspSignature AvrIspSignature; - -AvrIsp* avr_isp_alloc(void); - -void avr_isp_free(AvrIsp* instance); - -void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context); - -bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance); - -AvrIspSignature avr_isp_read_signature(AvrIsp* instance); - -void avr_isp_end_pmode(AvrIsp* instance); - -bool avr_isp_erase_chip(AvrIsp* instance); - -uint8_t avr_isp_spi_transaction( - AvrIsp* instance, - uint8_t cmd, - uint8_t addr_hi, - uint8_t addr_lo, - uint8_t data); - -bool avr_isp_read_page( - AvrIsp* instance, - uint32_t memtype, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size); - -bool avr_isp_write_page( - AvrIsp* instance, - uint32_t mem_type, - uint32_t mem_size, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size); - -uint8_t avr_isp_read_lock_byte(AvrIsp* instance); - -bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock); - -uint8_t avr_isp_read_fuse_low(AvrIsp* instance); - -bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse); - -uint8_t avr_isp_read_fuse_high(AvrIsp* instance); - -bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse); - -uint8_t avr_isp_read_fuse_extended(AvrIsp* instance); - -bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse); - -void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_event.h b/applications/external/avr_isp_programmer/helpers/avr_isp_event.h deleted file mode 100644 index c704b5b35..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_event.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -typedef enum { - //SubmenuIndex - SubmenuIndexAvrIspProgrammer = 10, - SubmenuIndexAvrIspReader, - SubmenuIndexAvrIspWriter, - SubmenuIndexAvrIsWiring, - SubmenuIndexAvrIspAbout, - - //AvrIspCustomEvent - AvrIspCustomEventSceneChipDetectOk = 100, - AvrIspCustomEventSceneReadingOk, - AvrIspCustomEventSceneWritingOk, - AvrIspCustomEventSceneErrorVerification, - AvrIspCustomEventSceneErrorReading, - AvrIspCustomEventSceneErrorWriting, - AvrIspCustomEventSceneErrorWritingFuse, - AvrIspCustomEventSceneInputName, - AvrIspCustomEventSceneSuccess, - AvrIspCustomEventSceneExit, - AvrIspCustomEventSceneExitStartMenu, -} AvrIspCustomEvent; diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_types.h b/applications/external/avr_isp_programmer/helpers/avr_isp_types.h deleted file mode 100644 index 5e174ec3b..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_types.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -#define AVR_ISP_VERSION_APP "0.1" -#define AVR_ISP_DEVELOPED "SkorP" -#define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" - -#define AVR_ISP_APP_FILE_VERSION 1 -#define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR" -#define AVR_ISP_APP_EXTENSION ".avr" - -typedef enum { - //AvrIspViewVariableItemList, - AvrIspViewSubmenu, - AvrIspViewProgrammer, - AvrIspViewReader, - AvrIspViewWriter, - AvrIspViewWidget, - AvrIspViewPopup, - AvrIspViewTextInput, - AvrIspViewChipDetect, -} AvrIspView; - -typedef enum { - AvrIspErrorNoError, - AvrIspErrorReading, - AvrIspErrorWriting, - AvrIspErrorVerification, - AvrIspErrorWritingFuse, -} AvrIspError; \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.c deleted file mode 100644 index dfe1f43c2..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.c +++ /dev/null @@ -1,266 +0,0 @@ -#include "avr_isp_worker.h" -#include -#include "../lib/driver/avr_isp_prog.h" -#include "../lib/driver/avr_isp_prog_cmd.h" -#include "../lib/driver/avr_isp_chip_arr.h" - -#include - -#define TAG "AvrIspWorker" - -typedef enum { - AvrIspWorkerEvtStop = (1 << 0), - - AvrIspWorkerEvtRx = (1 << 1), - AvrIspWorkerEvtTxCoplete = (1 << 2), - AvrIspWorkerEvtTx = (1 << 3), - AvrIspWorkerEvtState = (1 << 4), - - //AvrIspWorkerEvtCfg = (1 << 5), - -} AvrIspWorkerEvt; - -struct AvrIspWorker { - FuriThread* thread; - volatile bool worker_running; - uint8_t connect_usb; - AvrIspWorkerCallback callback; - void* context; -}; - -#define AVR_ISP_WORKER_PROG_ALL_EVENTS (AvrIspWorkerEvtStop) -#define AVR_ISP_WORKER_ALL_EVENTS \ - (AvrIspWorkerEvtTx | AvrIspWorkerEvtTxCoplete | AvrIspWorkerEvtRx | AvrIspWorkerEvtStop | \ - AvrIspWorkerEvtState) - -//########################/* VCP CDC */############################################# -#include "usb_cdc.h" -#include -#include -#include - -#define AVR_ISP_VCP_CDC_CH 1 -#define AVR_ISP_VCP_CDC_PKT_LEN CDC_DATA_SZ -#define AVR_ISP_VCP_UART_RX_BUF_SIZE (AVR_ISP_VCP_CDC_PKT_LEN * 5) - -static void vcp_on_cdc_tx_complete(void* context); -static void vcp_on_cdc_rx(void* context); -static void vcp_state_callback(void* context, uint8_t state); -static void vcp_on_cdc_control_line(void* context, uint8_t state); -static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config); - -static const CdcCallbacks cdc_cb = { - vcp_on_cdc_tx_complete, - vcp_on_cdc_rx, - vcp_state_callback, - vcp_on_cdc_control_line, - vcp_on_line_config, -}; - -/* VCP callbacks */ - -static void vcp_on_cdc_tx_complete(void* context) { - furi_assert(context); - AvrIspWorker* instance = context; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTxCoplete); -} - -static void vcp_on_cdc_rx(void* context) { - furi_assert(context); - AvrIspWorker* instance = context; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); -} - -static void vcp_state_callback(void* context, uint8_t state) { - UNUSED(context); - - AvrIspWorker* instance = context; - instance->connect_usb = state; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtState); -} - -static void vcp_on_cdc_control_line(void* context, uint8_t state) { - UNUSED(context); - UNUSED(state); -} - -static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) { - UNUSED(context); - UNUSED(config); -} - -static void avr_isp_worker_vcp_cdc_init(void* context) { - furi_hal_usb_unlock(); - Cli* cli = furi_record_open(RECORD_CLI); - //close cli - cli_session_close(cli); - //disable callbacks VCP_CDC=0 - furi_hal_cdc_set_callbacks(0, NULL, NULL); - //set 2 cdc - furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); - //open cli VCP_CDC=0 - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); - - furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, (CdcCallbacks*)&cdc_cb, context); -} - -static void avr_isp_worker_vcp_cdc_deinit(void) { - //disable callbacks AVR_ISP_VCP_CDC_CH - furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, NULL, NULL); - - Cli* cli = furi_record_open(RECORD_CLI); - //close cli - cli_session_close(cli); - furi_hal_usb_unlock(); - //set 1 cdc - furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); - //open cli VCP_CDC=0 - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); -} - -//################################################################################# - -static int32_t avr_isp_worker_prog_thread(void* context) { - AvrIspProg* prog = context; - FURI_LOG_D(TAG, "AvrIspProgWorker Start"); - while(1) { - if(furi_thread_flags_get() & AvrIspWorkerEvtStop) break; - avr_isp_prog_avrisp(prog); - } - FURI_LOG_D(TAG, "AvrIspProgWorker Stop"); - return 0; -} - -static void avr_isp_worker_prog_tx_data(void* context) { - furi_assert(context); - AvrIspWorker* instance = context; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTx); -} - -/** Worker thread - * - * @param context - * @return exit code - */ -static int32_t avr_isp_worker_thread(void* context) { - AvrIspWorker* instance = context; - avr_isp_worker_vcp_cdc_init(instance); - - /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); - - AvrIspProg* prog = avr_isp_prog_init(); - avr_isp_prog_set_tx_callback(prog, avr_isp_worker_prog_tx_data, instance); - - uint8_t buf[AVR_ISP_VCP_UART_RX_BUF_SIZE]; - size_t len = 0; - - FuriThread* prog_thread = - furi_thread_alloc_ex("AvrIspProgWorker", 1024, avr_isp_worker_prog_thread, prog); - furi_thread_start(prog_thread); - - FURI_LOG_D(TAG, "Start"); - - while(instance->worker_running) { - uint32_t events = - furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); - - if(events & AvrIspWorkerEvtRx) { - if(avr_isp_prog_spaces_rx(prog) >= AVR_ISP_VCP_CDC_PKT_LEN) { - len = furi_hal_cdc_receive(AVR_ISP_VCP_CDC_CH, buf, AVR_ISP_VCP_CDC_PKT_LEN); - // for(uint8_t i = 0; i < len; i++) { - // FURI_LOG_I(TAG, "--> %X", buf[i]); - // } - avr_isp_prog_rx(prog, buf, len); - } else { - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); - } - } - - if((events & AvrIspWorkerEvtTxCoplete) || (events & AvrIspWorkerEvtTx)) { - len = avr_isp_prog_tx(prog, buf, AVR_ISP_VCP_CDC_PKT_LEN); - - // for(uint8_t i = 0; i < len; i++) { - // FURI_LOG_I(TAG, "<-- %X", buf[i]); - // } - - if(len > 0) furi_hal_cdc_send(AVR_ISP_VCP_CDC_CH, buf, len); - } - - if(events & AvrIspWorkerEvtStop) { - break; - } - - if(events & AvrIspWorkerEvtState) { - if(instance->callback) - instance->callback(instance->context, (bool)instance->connect_usb); - } - } - - FURI_LOG_D(TAG, "Stop"); - - furi_thread_flags_set(furi_thread_get_id(prog_thread), AvrIspWorkerEvtStop); - avr_isp_prog_exit(prog); - furi_delay_ms(10); - furi_thread_join(prog_thread); - furi_thread_free(prog_thread); - - avr_isp_prog_free(prog); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - avr_isp_worker_vcp_cdc_deinit(); - return 0; -} - -AvrIspWorker* avr_isp_worker_alloc(void* context) { - furi_assert(context); - UNUSED(context); - AvrIspWorker* instance = malloc(sizeof(AvrIspWorker)); - - instance->thread = furi_thread_alloc_ex("AvrIspWorker", 2048, avr_isp_worker_thread, instance); - return instance; -} - -void avr_isp_worker_free(AvrIspWorker* instance) { - furi_assert(instance); - - furi_check(!instance->worker_running); - furi_thread_free(instance->thread); - free(instance); -} - -void avr_isp_worker_set_callback( - AvrIspWorker* instance, - AvrIspWorkerCallback callback, - void* context) { - furi_assert(instance); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_worker_start(AvrIspWorker* instance) { - furi_assert(instance); - furi_assert(!instance->worker_running); - - instance->worker_running = true; - - furi_thread_start(instance->thread); -} - -void avr_isp_worker_stop(AvrIspWorker* instance) { - furi_assert(instance); - furi_assert(instance->worker_running); - - instance->worker_running = false; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtStop); - - furi_thread_join(instance->thread); -} - -bool avr_isp_worker_is_running(AvrIspWorker* instance) { - furi_assert(instance); - - return instance->worker_running; -} diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h deleted file mode 100644 index cd9897dff..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include - -typedef struct AvrIspWorker AvrIspWorker; - -typedef void (*AvrIspWorkerCallback)(void* context, bool connect_usb); - -/** Allocate AvrIspWorker - * - * @param context AvrIsp* context - * @return AvrIspWorker* - */ -AvrIspWorker* avr_isp_worker_alloc(void* context); - -/** Free AvrIspWorker - * - * @param instance AvrIspWorker instance - */ -void avr_isp_worker_free(AvrIspWorker* instance); - -/** Callback AvrIspWorker - * - * @param instance AvrIspWorker instance - * @param callback AvrIspWorkerOverrunCallback callback - * @param context - */ -void avr_isp_worker_set_callback( - AvrIspWorker* instance, - AvrIspWorkerCallback callback, - void* context); - -/** Start AvrIspWorker - * - * @param instance AvrIspWorker instance - */ -void avr_isp_worker_start(AvrIspWorker* instance); - -/** Stop AvrIspWorker - * - * @param instance AvrIspWorker instance - */ -void avr_isp_worker_stop(AvrIspWorker* instance); - -/** Check if worker is running - * @param instance AvrIspWorker instance - * @return bool - true if running - */ -bool avr_isp_worker_is_running(AvrIspWorker* instance); diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c deleted file mode 100644 index b4c12cbc3..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ /dev/null @@ -1,1157 +0,0 @@ -#include "avr_isp_worker_rw.h" -#include -#include "avr_isp_types.h" -#include "avr_isp.h" -#include "../lib/driver/avr_isp_prog_cmd.h" -#include "../lib/driver/avr_isp_chip_arr.h" - -#include "flipper_i32hex_file.h" -#include - -#include - -#define TAG "AvrIspWorkerRW" - -#define NAME_PATERN_FLASH_FILE "flash.hex" -#define NAME_PATERN_EEPROM_FILE "eeprom.hex" - -struct AvrIspWorkerRW { - AvrIsp* avr_isp; - FuriThread* thread; - volatile bool worker_running; - - uint32_t chip_arr_ind; - bool chip_detect; - uint8_t lfuse; - uint8_t hfuse; - uint8_t efuse; - uint8_t lock; - float progress_flash; - float progress_eeprom; - const char* file_path; - const char* file_name; - AvrIspSignature signature; - AvrIspWorkerRWCallback callback; - void* context; - - AvrIspWorkerRWStatusCallback callback_status; - void* context_status; -}; - -typedef enum { - AvrIspWorkerRWEvtStop = (1 << 0), - - AvrIspWorkerRWEvtReading = (1 << 1), - AvrIspWorkerRWEvtVerification = (1 << 2), - AvrIspWorkerRWEvtWriting = (1 << 3), - AvrIspWorkerRWEvtWritingFuse = (1 << 4), - -} AvrIspWorkerRWEvt; -#define AVR_ISP_WORKER_ALL_EVENTS \ - (AvrIspWorkerRWEvtWritingFuse | AvrIspWorkerRWEvtWriting | AvrIspWorkerRWEvtVerification | \ - AvrIspWorkerRWEvtReading | AvrIspWorkerRWEvtStop) - -/** Worker thread - * - * @param context - * @return exit code - */ -static int32_t avr_isp_worker_rw_thread(void* context) { - AvrIspWorkerRW* instance = context; - - /* start PWM on &gpio_ext_pa4 */ - if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); - } - - FURI_LOG_D(TAG, "Start"); - - while(1) { - uint32_t events = - furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); - - if(events & AvrIspWorkerRWEvtStop) { - break; - } - - if(events & AvrIspWorkerRWEvtWritingFuse) { - if(avr_isp_worker_rw_write_fuse(instance, instance->file_path, instance->file_name)) { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusEndWritingFuse); - } else { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusErrorWritingFuse); - } - } - - if(events & AvrIspWorkerRWEvtWriting) { - if(avr_isp_worker_rw_write_dump(instance, instance->file_path, instance->file_name)) { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusEndWriting); - } else { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusErrorWriting); - } - } - - if(events & AvrIspWorkerRWEvtVerification) { - if(avr_isp_worker_rw_verification(instance, instance->file_path, instance->file_name)) { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusEndVerification); - } else { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusErrorVerification); - } - } - - if(events & AvrIspWorkerRWEvtReading) { - if(avr_isp_worker_rw_read_dump(instance, instance->file_path, instance->file_name)) { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusEndReading); - } else { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusErrorReading); - } - } - } - FURI_LOG_D(TAG, "Stop"); - - if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - } - - return 0; -} - -bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { - furi_assert(instance); - - FURI_LOG_D(TAG, "Detecting AVR chip"); - - instance->chip_detect = false; - instance->chip_arr_ind = avr_isp_chip_arr_size + 1; - - /* start PWM on &gpio_ext_pa4 */ - bool was_pwm_enabled = false; - if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); - } else { - was_pwm_enabled = true; - } - - do { - if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); - break; - } - instance->signature = avr_isp_read_signature(instance->avr_isp); - - if(instance->signature.vendor != 0x1E) { - //No detect chip - } else { - for(uint32_t ind = 0; ind < avr_isp_chip_arr_size; ind++) { - if(avr_isp_chip_arr[ind].avrarch != F_AVR8) continue; - if(avr_isp_chip_arr[ind].sigs[1] == instance->signature.part_family) { - if(avr_isp_chip_arr[ind].sigs[2] == instance->signature.part_number) { - FURI_LOG_D(TAG, "Detect AVR chip = \"%s\"", avr_isp_chip_arr[ind].name); - FURI_LOG_D( - TAG, - "Signature = 0x%02X 0x%02X 0x%02X", - instance->signature.vendor, - instance->signature.part_family, - instance->signature.part_number); - - switch(avr_isp_chip_arr[ind].nfuses) { - case 1: - instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); - FURI_LOG_D(TAG, "Lfuse = %02X", instance->lfuse); - break; - case 2: - instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); - instance->hfuse = avr_isp_read_fuse_high(instance->avr_isp); - FURI_LOG_D( - TAG, "Lfuse = %02X Hfuse = %02X", instance->lfuse, instance->hfuse); - break; - case 3: - instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); - instance->hfuse = avr_isp_read_fuse_high(instance->avr_isp); - instance->efuse = avr_isp_read_fuse_extended(instance->avr_isp); - FURI_LOG_D( - TAG, - "Lfuse = %02X Hfuse = %02X Efuse = %02X", - instance->lfuse, - instance->hfuse, - instance->efuse); - break; - default: - break; - } - if(avr_isp_chip_arr[ind].nlocks == 1) { - instance->lock = avr_isp_read_lock_byte(instance->avr_isp); - FURI_LOG_D(TAG, "Lock = %02X", instance->lock); - } - instance->chip_detect = true; - instance->chip_arr_ind = ind; - break; - } - } - } - } - avr_isp_end_pmode(instance->avr_isp); - - } while(0); - - if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4) && !was_pwm_enabled) { - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - } - - if(instance->callback) { - if(instance->chip_arr_ind > avr_isp_chip_arr_size) { - instance->callback(instance->context, "No detect", instance->chip_detect, 0); - } else if(instance->chip_arr_ind < avr_isp_chip_arr_size) { - instance->callback( - instance->context, - avr_isp_chip_arr[instance->chip_arr_ind].name, - instance->chip_detect, - avr_isp_chip_arr[instance->chip_arr_ind].flashsize); - } else { - instance->callback(instance->context, "Unknown", instance->chip_detect, 0); - } - } - - return instance->chip_detect; -} - -AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context) { - furi_assert(context); - UNUSED(context); - - AvrIspWorkerRW* instance = malloc(sizeof(AvrIspWorkerRW)); - instance->avr_isp = avr_isp_alloc(); - - instance->thread = - furi_thread_alloc_ex("AvrIspWorkerRW", 4096, avr_isp_worker_rw_thread, instance); - - instance->chip_detect = false; - instance->lfuse = 0; - instance->hfuse = 0; - instance->efuse = 0; - instance->lock = 0; - instance->progress_flash = 0.0f; - instance->progress_eeprom = 0.0f; - - return instance; -} - -void avr_isp_worker_rw_free(AvrIspWorkerRW* instance) { - furi_assert(instance); - - avr_isp_free(instance->avr_isp); - - furi_check(!instance->worker_running); - furi_thread_free(instance->thread); - - free(instance); -} - -void avr_isp_worker_rw_start(AvrIspWorkerRW* instance) { - furi_assert(instance); - furi_assert(!instance->worker_running); - - instance->worker_running = true; - - furi_thread_start(instance->thread); -} - -void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance) { - furi_assert(instance); - furi_assert(instance->worker_running); - - instance->worker_running = false; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtStop); - - furi_thread_join(instance->thread); -} - -bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance) { - furi_assert(instance); - - return instance->worker_running; -} - -void avr_isp_worker_rw_set_callback( - AvrIspWorkerRW* instance, - AvrIspWorkerRWCallback callback, - void* context) { - furi_assert(instance); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_worker_rw_set_callback_status( - AvrIspWorkerRW* instance, - AvrIspWorkerRWStatusCallback callback_status, - void* context_status) { - furi_assert(instance); - - instance->callback_status = callback_status; - instance->context_status = context_status; -} - -float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance) { - furi_assert(instance); - - return instance->progress_flash; -} - -float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance) { - furi_assert(instance); - - return instance->progress_eeprom; -} - -static void avr_isp_worker_rw_get_dump_flash(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_check(instance->avr_isp); - - FURI_LOG_D(TAG, "Dump FLASH %s", file_path); - - FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_write( - file_path, avr_isp_chip_arr[instance->chip_arr_ind].flashoffset); - - uint8_t data[272] = {0}; - bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); - uint8_t extended_addr = 0; - - for(int32_t i = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; - i < avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2; - i += avr_isp_chip_arr[instance->chip_arr_ind].pagesize / 2) { - if(send_extended_addr) { - if(extended_addr <= ((i >> 16) & 0xFF)) { - avr_isp_write_extended_addr(instance->avr_isp, extended_addr); - extended_addr = ((i >> 16) & 0xFF) + 1; - } - } - avr_isp_read_page( - instance->avr_isp, - STK_SET_FLASH_TYPE, - (uint16_t)i, - avr_isp_chip_arr[instance->chip_arr_ind].pagesize, - data, - sizeof(data)); - flipper_i32hex_file_bin_to_i32hex_set_data( - flipper_hex_flash, data, avr_isp_chip_arr[instance->chip_arr_ind].pagesize); - FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); - instance->progress_flash = - (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); - } - flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_flash); - FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); - flipper_i32hex_file_close(flipper_hex_flash); - instance->progress_flash = 1.0f; -} - -static void avr_isp_worker_rw_get_dump_eeprom(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_check(instance->avr_isp); - - FURI_LOG_D(TAG, "Dump EEPROM %s", file_path); - - FlipperI32HexFile* flipper_hex_eeprom = flipper_i32hex_file_open_write( - file_path, avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset); - - int32_t size_data = 32; - uint8_t data[256] = {0}; - - if(size_data > avr_isp_chip_arr[instance->chip_arr_ind].eepromsize) - size_data = avr_isp_chip_arr[instance->chip_arr_ind].eepromsize; - - for(int32_t i = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; - i < avr_isp_chip_arr[instance->chip_arr_ind].eepromsize; - i += size_data) { - avr_isp_read_page( - instance->avr_isp, STK_SET_EEPROM_TYPE, (uint16_t)i, size_data, data, sizeof(data)); - flipper_i32hex_file_bin_to_i32hex_set_data(flipper_hex_eeprom, data, size_data); - FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_eeprom)); - instance->progress_eeprom = - (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); - } - flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_eeprom); - FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_eeprom)); - flipper_i32hex_file_close(flipper_hex_eeprom); - instance->progress_eeprom = 1.0f; -} - -bool avr_isp_worker_rw_read_dump( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - furi_assert(file_path); - furi_assert(file_name); - - FURI_LOG_D(TAG, "Read dump chip"); - - instance->progress_flash = 0.0f; - instance->progress_eeprom = 0.0f; - bool ret = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - FuriString* file_path_name = furi_string_alloc(); - - if(!avr_isp_worker_rw_detect_chip(instance)) { - FURI_LOG_E(TAG, "No detect AVR chip"); - } else { - do { - furi_string_printf( - file_path_name, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); - if(!flipper_format_file_open_always( - flipper_format, furi_string_get_cstr(file_path_name))) { - FURI_LOG_E(TAG, "flipper_format_file_open_always"); - break; - } - if(!flipper_format_write_header_cstr( - flipper_format, AVR_ISP_APP_FILE_TYPE, AVR_ISP_APP_FILE_VERSION)) { - FURI_LOG_E(TAG, "flipper_format_write_header_cstr"); - break; - } - if(!flipper_format_write_string_cstr( - flipper_format, "Chip name", avr_isp_chip_arr[instance->chip_arr_ind].name)) { - FURI_LOG_E(TAG, "Chip name"); - break; - } - if(!flipper_format_write_hex( - flipper_format, - "Signature", - (uint8_t*)&instance->signature, - sizeof(AvrIspSignature))) { - FURI_LOG_E(TAG, "Unable to add Signature"); - break; - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { - if(!flipper_format_write_hex(flipper_format, "Lfuse", &instance->lfuse, 1)) { - FURI_LOG_E(TAG, "Unable to add Lfuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { - if(!flipper_format_write_hex(flipper_format, "Hfuse", &instance->hfuse, 1)) { - FURI_LOG_E(TAG, "Unable to add Hfuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { - if(!flipper_format_write_hex(flipper_format, "Efuse", &instance->efuse, 1)) { - FURI_LOG_E(TAG, "Unable to add Efuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { - if(!flipper_format_write_hex(flipper_format, "Lock", &instance->lock, 1)) { - FURI_LOG_E(TAG, "Unable to add Lock"); - break; - } - } - furi_string_printf(file_path_name, "%s_%s", file_name, NAME_PATERN_FLASH_FILE); - if(!flipper_format_write_string_cstr( - flipper_format, "Dump_flash", furi_string_get_cstr(file_path_name))) { - FURI_LOG_E(TAG, "Unable to add Dump_flash"); - break; - } - - if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { - furi_string_printf(file_path_name, "%s_%s", file_name, NAME_PATERN_EEPROM_FILE); - if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { - if(!flipper_format_write_string_cstr( - flipper_format, "Dump_eeprom", furi_string_get_cstr(file_path_name))) { - FURI_LOG_E(TAG, "Unable to add Dump_eeprom"); - break; - } - } - } - ret = true; - } while(false); - } - - flipper_format_free(flipper_format); - furi_record_close(RECORD_STORAGE); - - if(ret) { - if(avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - //Dump flash - furi_string_printf( - file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_FLASH_FILE); - avr_isp_worker_rw_get_dump_flash(instance, furi_string_get_cstr(file_path_name)); - //Dump eeprom - if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { - furi_string_printf( - file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_EEPROM_FILE); - avr_isp_worker_rw_get_dump_eeprom(instance, furi_string_get_cstr(file_path_name)); - } - - avr_isp_end_pmode(instance->avr_isp); - } - } - - furi_string_free(file_path_name); - - return true; -} - -void avr_isp_worker_rw_read_dump_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtReading); -} - -static bool avr_isp_worker_rw_verification_flash(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - FURI_LOG_D(TAG, "Verification flash %s", file_path); - - instance->progress_flash = 0.0; - bool ret = true; - - FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_read(file_path); - - uint8_t data_read_flash[272] = {0}; - uint8_t data_read_hex[272] = {0}; - - uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; - bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); - uint8_t extended_addr = 0; - - FlipperI32HexFileRet flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_flash, data_read_hex, sizeof(data_read_hex)); - - while(((flipper_hex_ret.status == FlipperI32HexFileStatusData) || - (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) && - ret) { - switch(flipper_hex_ret.status) { - case FlipperI32HexFileStatusData: - - if(send_extended_addr) { - if(extended_addr <= ((addr >> 16) & 0xFF)) { - avr_isp_write_extended_addr(instance->avr_isp, extended_addr); - extended_addr = ((addr >> 16) & 0xFF) + 1; - } - } - - avr_isp_read_page( - instance->avr_isp, - STK_SET_FLASH_TYPE, - (uint16_t)addr, - flipper_hex_ret.data_size, - data_read_flash, - sizeof(data_read_flash)); - - if(memcmp(data_read_hex, data_read_flash, flipper_hex_ret.data_size) != 0) { - ret = false; - - FURI_LOG_E(TAG, "Verification flash error"); - FURI_LOG_E(TAG, "Addr: 0x%04lX", addr); - for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { - FURI_LOG_RAW_E("%02X ", data_read_hex[i]); - } - FURI_LOG_RAW_E("\r\n"); - for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { - FURI_LOG_RAW_E("%02X ", data_read_flash[i]); - } - FURI_LOG_RAW_E("\r\n"); - } - - addr += flipper_hex_ret.data_size / 2; - instance->progress_flash = - (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); - break; - - case FlipperI32HexFileStatusUdateAddr: - addr = (data_read_hex[0] << 24 | data_read_hex[1] << 16) / 2; - break; - - default: - furi_crash(TAG " Incorrect status."); - break; - } - - flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_flash, data_read_hex, sizeof(data_read_hex)); - } - - flipper_i32hex_file_close(flipper_hex_flash); - instance->progress_flash = 1.0f; - - return ret; -} - -static bool - avr_isp_worker_rw_verification_eeprom(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - FURI_LOG_D(TAG, "Verification eeprom %s", file_path); - - instance->progress_eeprom = 0.0; - bool ret = true; - - FlipperI32HexFile* flipper_hex_eeprom = flipper_i32hex_file_open_read(file_path); - - uint8_t data_read_eeprom[272] = {0}; - uint8_t data_read_hex[272] = {0}; - - uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; - - FlipperI32HexFileRet flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_eeprom, data_read_hex, sizeof(data_read_hex)); - - while(((flipper_hex_ret.status == FlipperI32HexFileStatusData) || - (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) && - ret) { - switch(flipper_hex_ret.status) { - case FlipperI32HexFileStatusData: - avr_isp_read_page( - instance->avr_isp, - STK_SET_EEPROM_TYPE, - (uint16_t)addr, - flipper_hex_ret.data_size, - data_read_eeprom, - sizeof(data_read_eeprom)); - - if(memcmp(data_read_hex, data_read_eeprom, flipper_hex_ret.data_size) != 0) { - ret = false; - FURI_LOG_E(TAG, "Verification eeprom error"); - FURI_LOG_E(TAG, "Addr: 0x%04lX", addr); - for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { - FURI_LOG_RAW_E("%02X ", data_read_hex[i]); - } - FURI_LOG_RAW_E("\r\n"); - for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { - FURI_LOG_RAW_E("%02X ", data_read_eeprom[i]); - } - FURI_LOG_RAW_E("\r\n"); - } - - addr += flipper_hex_ret.data_size; - instance->progress_eeprom = - (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); - break; - - case FlipperI32HexFileStatusUdateAddr: - addr = (data_read_hex[0] << 24 | data_read_hex[1] << 16); - break; - - default: - furi_crash(TAG " Incorrect status."); - break; - } - - flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_eeprom, data_read_hex, sizeof(data_read_hex)); - } - - flipper_i32hex_file_close(flipper_hex_eeprom); - instance->progress_eeprom = 1.0f; - - return ret; -} - -bool avr_isp_worker_rw_verification( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - furi_assert(file_path); - furi_assert(file_name); - - FURI_LOG_D(TAG, "Verification chip"); - - instance->progress_flash = 0.0f; - instance->progress_eeprom = 0.0f; - FuriString* file_path_name = furi_string_alloc(); - - bool ret = false; - - if(avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - do { - furi_string_printf( - file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_FLASH_FILE); - if(!avr_isp_worker_rw_verification_flash( - instance, furi_string_get_cstr(file_path_name))) - break; - - if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { - furi_string_printf( - file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_EEPROM_FILE); - - if(!avr_isp_worker_rw_verification_eeprom( - instance, furi_string_get_cstr(file_path_name))) - break; - } - ret = true; - } while(false); - avr_isp_end_pmode(instance->avr_isp); - furi_string_free(file_path_name); - } - return ret; -} - -void avr_isp_worker_rw_verification_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtVerification); -} - -static void avr_isp_worker_rw_write_flash(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_check(instance->avr_isp); - - instance->progress_flash = 0.0; - - FURI_LOG_D(TAG, "Write Flash %s", file_path); - - uint8_t data[288] = {0}; - - FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_read(file_path); - - uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; - bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); - uint8_t extended_addr = 0; - - FlipperI32HexFileRet flipper_hex_ret = - flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_flash, data, sizeof(data)); - - while((flipper_hex_ret.status == FlipperI32HexFileStatusData) || - (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) { - switch(flipper_hex_ret.status) { - case FlipperI32HexFileStatusData: - - if(send_extended_addr) { - if(extended_addr <= ((addr >> 16) & 0xFF)) { - avr_isp_write_extended_addr(instance->avr_isp, extended_addr); - extended_addr = ((addr >> 16) & 0xFF) + 1; - } - } - - if(!avr_isp_write_page( - instance->avr_isp, - STK_SET_FLASH_TYPE, - avr_isp_chip_arr[instance->chip_arr_ind].flashsize, - (uint16_t)addr, - avr_isp_chip_arr[instance->chip_arr_ind].pagesize, - data, - flipper_hex_ret.data_size)) { - break; - } - addr += flipper_hex_ret.data_size / 2; - instance->progress_flash = - (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); - break; - - case FlipperI32HexFileStatusUdateAddr: - addr = (data[0] << 24 | data[1] << 16) / 2; - break; - - default: - furi_crash(TAG " Incorrect status."); - break; - } - - flipper_hex_ret = - flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_flash, data, sizeof(data)); - } - - flipper_i32hex_file_close(flipper_hex_flash); - instance->progress_flash = 1.0f; -} - -static void avr_isp_worker_rw_write_eeprom(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_check(instance->avr_isp); - - instance->progress_eeprom = 0.0; - uint8_t data[288] = {0}; - - FURI_LOG_D(TAG, "Write EEPROM %s", file_path); - - FlipperI32HexFile* flipper_hex_eeprom_read = flipper_i32hex_file_open_read(file_path); - - uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; - FlipperI32HexFileRet flipper_hex_ret = - flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_eeprom_read, data, sizeof(data)); - - while((flipper_hex_ret.status == FlipperI32HexFileStatusData) || - (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) { - switch(flipper_hex_ret.status) { - case FlipperI32HexFileStatusData: - if(!avr_isp_write_page( - instance->avr_isp, - STK_SET_EEPROM_TYPE, - avr_isp_chip_arr[instance->chip_arr_ind].eepromsize, - (uint16_t)addr, - avr_isp_chip_arr[instance->chip_arr_ind].eeprompagesize, - data, - flipper_hex_ret.data_size)) { - break; - } - addr += flipper_hex_ret.data_size; - instance->progress_eeprom = - (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); - break; - - case FlipperI32HexFileStatusUdateAddr: - addr = data[0] << 24 | data[1] << 16; - break; - - default: - furi_crash(TAG " Incorrect status."); - break; - } - - flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_eeprom_read, data, sizeof(data)); - } - - flipper_i32hex_file_close(flipper_hex_eeprom_read); - instance->progress_eeprom = 1.0f; -} - -bool avr_isp_worker_rw_write_dump( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - furi_assert(file_path); - furi_assert(file_name); - - FURI_LOG_D(TAG, "Write dump chip"); - - instance->progress_flash = 0.0f; - instance->progress_eeprom = 0.0f; - bool ret = false; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - FuriString* file_path_name = furi_string_alloc(); - - FuriString* temp_str_1 = furi_string_alloc(); - FuriString* temp_str_2 = furi_string_alloc(); - uint32_t temp_data32; - - if(!avr_isp_worker_rw_detect_chip(instance)) { - FURI_LOG_E(TAG, "No detect AVR chip"); - } else { - //upload file with description - do { - furi_string_printf( - file_path_name, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); - if(!flipper_format_file_open_existing( - flipper_format, furi_string_get_cstr(file_path_name))) { - FURI_LOG_E(TAG, "Error open file %s", furi_string_get_cstr(file_path_name)); - break; - } - - if(!flipper_format_read_header(flipper_format, temp_str_1, &temp_data32)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - break; - } - - if((!strcmp(furi_string_get_cstr(temp_str_1), AVR_ISP_APP_FILE_TYPE)) && - temp_data32 == AVR_ISP_APP_FILE_VERSION) { - } else { - FURI_LOG_E(TAG, "Type or version mismatch"); - break; - } - - AvrIspSignature sig_read = {0}; - - if(!flipper_format_read_hex( - flipper_format, "Signature", (uint8_t*)&sig_read, sizeof(AvrIspSignature))) { - FURI_LOG_E(TAG, "Missing Signature"); - break; - } - - if(memcmp( - (uint8_t*)&instance->signature, (uint8_t*)&sig_read, sizeof(AvrIspSignature)) != - 0) { - FURI_LOG_E( - TAG, - "Wrong chip. Connected (%02X %02X %02X), read from file (%02X %02X %02X)", - instance->signature.vendor, - instance->signature.part_family, - instance->signature.part_number, - sig_read.vendor, - sig_read.part_family, - sig_read.part_number); - break; - } - - if(!flipper_format_read_string(flipper_format, "Dump_flash", temp_str_1)) { - FURI_LOG_E(TAG, "Missing Dump_flash"); - break; - } - - //may not be - flipper_format_read_string(flipper_format, "Dump_eeprom", temp_str_2); - ret = true; - } while(false); - } - flipper_format_free(flipper_format); - furi_record_close(RECORD_STORAGE); - - if(ret) { - do { - //checking .hex files for errors - - furi_string_printf( - file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_1)); - - FURI_LOG_D(TAG, "Check flash file"); - FlipperI32HexFile* flipper_hex_flash_read = - flipper_i32hex_file_open_read(furi_string_get_cstr(file_path_name)); - if(flipper_i32hex_file_check(flipper_hex_flash_read)) { - FURI_LOG_D(TAG, "Check flash file: OK"); - } else { - FURI_LOG_E(TAG, "Check flash file: Error"); - ret = false; - } - flipper_i32hex_file_close(flipper_hex_flash_read); - - if(furi_string_size(temp_str_2) > 4) { - furi_string_printf( - file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_2)); - - FURI_LOG_D(TAG, "Check eeprom file"); - FlipperI32HexFile* flipper_hex_eeprom_read = - flipper_i32hex_file_open_read(furi_string_get_cstr(file_path_name)); - if(flipper_i32hex_file_check(flipper_hex_eeprom_read)) { - FURI_LOG_D(TAG, "Check eeprom file: OK"); - } else { - FURI_LOG_E(TAG, "Check eeprom file: Error"); - ret = false; - } - flipper_i32hex_file_close(flipper_hex_eeprom_read); - } - - if(!ret) break; - ret = false; - - //erase chip - FURI_LOG_D(TAG, "Erase chip"); - if(!avr_isp_erase_chip(instance->avr_isp)) { - FURI_LOG_E(TAG, "Erase chip: Error"); - break; - } - - if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); - break; - } - - //write flash - furi_string_printf( - file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_1)); - avr_isp_worker_rw_write_flash(instance, furi_string_get_cstr(file_path_name)); - - //write eeprom - if(furi_string_size(temp_str_2) > 4) { - furi_string_printf( - file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_2)); - avr_isp_worker_rw_write_eeprom(instance, furi_string_get_cstr(file_path_name)); - } - ret = true; - avr_isp_end_pmode(instance->avr_isp); - } while(false); - } - - furi_string_free(file_path_name); - furi_string_free(temp_str_1); - furi_string_free(temp_str_2); - - return ret; -} - -void avr_isp_worker_rw_write_dump_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtWriting); -} - -bool avr_isp_worker_rw_write_fuse( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - furi_assert(file_path); - furi_assert(file_name); - - FURI_LOG_D(TAG, "Write fuse chip"); - - bool ret = false; - uint8_t lfuse; - uint8_t hfuse; - uint8_t efuse; - uint8_t lock; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - FuriString* temp_str = furi_string_alloc(); - - uint32_t temp_data32; - - if(!avr_isp_worker_rw_detect_chip(instance)) { - FURI_LOG_E(TAG, "No detect AVR chip"); - } else { - //upload file with description - do { - furi_string_printf(temp_str, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); - if(!flipper_format_file_open_existing(flipper_format, furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Error open file %s", furi_string_get_cstr(temp_str)); - break; - } - - if(!flipper_format_read_header(flipper_format, temp_str, &temp_data32)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - break; - } - - if((!strcmp(furi_string_get_cstr(temp_str), AVR_ISP_APP_FILE_TYPE)) && - temp_data32 == AVR_ISP_APP_FILE_VERSION) { - } else { - FURI_LOG_E(TAG, "Type or version mismatch"); - break; - } - - AvrIspSignature sig_read = {0}; - - if(!flipper_format_read_hex( - flipper_format, "Signature", (uint8_t*)&sig_read, sizeof(AvrIspSignature))) { - FURI_LOG_E(TAG, "Missing Signature"); - break; - } - - if(memcmp( - (uint8_t*)&instance->signature, (uint8_t*)&sig_read, sizeof(AvrIspSignature)) != - 0) { - FURI_LOG_E( - TAG, - "Wrong chip. Connected (%02X %02X %02X), read from file (%02X %02X %02X)", - instance->signature.vendor, - instance->signature.part_family, - instance->signature.part_number, - sig_read.vendor, - sig_read.part_family, - sig_read.part_number); - break; - } - - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { - if(!flipper_format_read_hex(flipper_format, "Lfuse", &lfuse, 1)) { - FURI_LOG_E(TAG, "Missing Lfuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { - if(!flipper_format_read_hex(flipper_format, "Hfuse", &hfuse, 1)) { - FURI_LOG_E(TAG, "Missing Hfuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { - if(!flipper_format_read_hex(flipper_format, "Efuse", &efuse, 1)) { - FURI_LOG_E(TAG, "Missing Efuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { - if(!flipper_format_read_hex(flipper_format, "Lock", &lock, 1)) { - FURI_LOG_E(TAG, "Missing Lock"); - break; - } - } - - if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); - break; - } - - ret = true; - - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { - if(instance->lfuse != lfuse) { - if(!avr_isp_write_fuse_low(instance->avr_isp, lfuse)) { - FURI_LOG_E(TAG, "Write Lfuse: error"); - ret = false; - } - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { - if(instance->hfuse != hfuse) { - if(!avr_isp_write_fuse_high(instance->avr_isp, hfuse)) { - FURI_LOG_E(TAG, "Write Hfuse: error"); - ret = false; - } - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { - if(instance->efuse != efuse) { - if(!avr_isp_write_fuse_extended(instance->avr_isp, efuse)) { - FURI_LOG_E(TAG, "Write Efuse: error"); - ret = false; - } - } - } - - if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { - FURI_LOG_D(TAG, "Write lock byte"); - if(instance->lock != lock) { - if(!avr_isp_write_lock_byte(instance->avr_isp, lock)) { - FURI_LOG_E(TAG, "Write Lock byte: error"); - ret = false; - } - } - } - avr_isp_end_pmode(instance->avr_isp); - } while(false); - } - - flipper_format_free(flipper_format); - furi_record_close(RECORD_STORAGE); - furi_string_free(temp_str); - return ret; -} - -void avr_isp_worker_rw_write_fuse_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtWritingFuse); -} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h deleted file mode 100644 index 2c52a8700..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -#include - -typedef struct AvrIspWorkerRW AvrIspWorkerRW; - -typedef void (*AvrIspWorkerRWCallback)( - void* context, - const char* name, - bool detect_chip, - uint32_t flash_size); - -typedef enum { - AvrIspWorkerRWStatusILDE = 0, - AvrIspWorkerRWStatusEndReading = 1, - AvrIspWorkerRWStatusEndVerification = 2, - AvrIspWorkerRWStatusEndWriting = 3, - AvrIspWorkerRWStatusEndWritingFuse = 4, - - AvrIspWorkerRWStatusErrorReading = (-1), - AvrIspWorkerRWStatusErrorVerification = (-2), - AvrIspWorkerRWStatusErrorWriting = (-3), - AvrIspWorkerRWStatusErrorWritingFuse = (-4), - - AvrIspWorkerRWStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization. -} AvrIspWorkerRWStatus; - -typedef void (*AvrIspWorkerRWStatusCallback)(void* context, AvrIspWorkerRWStatus status); - -AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context); - -void avr_isp_worker_rw_free(AvrIspWorkerRW* instance); - -void avr_isp_worker_rw_start(AvrIspWorkerRW* instance); - -void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance); - -bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance); - -void avr_isp_worker_rw_set_callback( - AvrIspWorkerRW* instance, - AvrIspWorkerRWCallback callback, - void* context); - -void avr_isp_worker_rw_set_callback_status( - AvrIspWorkerRW* instance, - AvrIspWorkerRWStatusCallback callback_status, - void* context_status); - -bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance); - -float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance); - -float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance); - -bool avr_isp_worker_rw_read_dump( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -void avr_isp_worker_rw_read_dump_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -bool avr_isp_worker_rw_verification( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -void avr_isp_worker_rw_verification_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -bool avr_isp_worker_rw_check_hex( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -bool avr_isp_worker_rw_write_dump( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -void avr_isp_worker_rw_write_dump_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -bool avr_isp_worker_rw_write_fuse( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -void avr_isp_worker_rw_write_fuse_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c deleted file mode 100644 index a8c20a0bd..000000000 --- a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c +++ /dev/null @@ -1,321 +0,0 @@ -#include "flipper_i32hex_file.h" -#include -#include -#include -#include -#include - -//https://en.wikipedia.org/wiki/Intel_HEX - -#define TAG "FlipperI32HexFile" - -#define COUNT_BYTE_PAYLOAD 32 //how much payload will be used - -#define I32HEX_TYPE_DATA 0x00 -#define I32HEX_TYPE_END_OF_FILE 0x01 -#define I32HEX_TYPE_EXT_LINEAR_ADDR 0x04 -#define I32HEX_TYPE_START_LINEAR_ADDR 0x05 - -struct FlipperI32HexFile { - uint32_t addr; - uint32_t addr_last; - Storage* storage; - Stream* stream; - FuriString* str_data; - FlipperI32HexFileStatus file_open; -}; - -FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr) { - furi_assert(name); - - FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); - instance->addr = start_addr; - instance->addr_last = 0; - instance->storage = furi_record_open(RECORD_STORAGE); - instance->stream = file_stream_alloc(instance->storage); - - if(file_stream_open(instance->stream, name, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - instance->file_open = FlipperI32HexFileStatusOpenFileWrite; - FURI_LOG_D(TAG, "Open write file %s", name); - } else { - FURI_LOG_E(TAG, "Failed to open file %s", name); - instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; - } - instance->str_data = furi_string_alloc(instance->storage); - - return instance; -} - -FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name) { - furi_assert(name); - - FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); - instance->addr = 0; - instance->addr_last = 0; - instance->storage = furi_record_open(RECORD_STORAGE); - instance->stream = file_stream_alloc(instance->storage); - - if(file_stream_open(instance->stream, name, FSAM_READ, FSOM_OPEN_EXISTING)) { - instance->file_open = FlipperI32HexFileStatusOpenFileRead; - FURI_LOG_D(TAG, "Open read file %s", name); - } else { - FURI_LOG_E(TAG, "Failed to open file %s", name); - instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; - } - instance->str_data = furi_string_alloc(instance->storage); - - return instance; -} - -void flipper_i32hex_file_close(FlipperI32HexFile* instance) { - furi_assert(instance); - - furi_string_free(instance->str_data); - file_stream_close(instance->stream); - stream_free(instance->stream); - furi_record_close(RECORD_STORAGE); -} - -FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( - FlipperI32HexFile* instance, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - furi_assert(data); - - FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; - if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { - ret.status = FlipperI32HexFileStatusErrorFileWrite; - } - uint8_t count_byte = 0; - uint32_t ind = 0; - uint8_t crc = 0; - - furi_string_reset(instance->str_data); - - if((instance->addr_last & 0xFF0000) < (instance->addr & 0xFF0000)) { - crc = 0x02 + 0x04 + ((instance->addr >> 24) & 0xFF) + ((instance->addr >> 16) & 0xFF); - crc = 0x01 + ~crc; - //I32HEX_TYPE_EXT_LINEAR_ADDR - furi_string_cat_printf( - instance->str_data, ":02000004%04lX%02X\r\n", (instance->addr >> 16), crc); - instance->addr_last = instance->addr; - } - - while(ind < data_size) { - if((ind + COUNT_BYTE_PAYLOAD) > data_size) { - count_byte = data_size - ind; - } else { - count_byte = COUNT_BYTE_PAYLOAD; - } - //I32HEX_TYPE_DATA - furi_string_cat_printf( - instance->str_data, ":%02X%04lX00", count_byte, (instance->addr & 0xFFFF)); - crc = count_byte + ((instance->addr >> 8) & 0xFF) + (instance->addr & 0xFF); - - for(uint32_t i = 0; i < count_byte; i++) { - furi_string_cat_printf(instance->str_data, "%02X", *data); - crc += *data++; - } - crc = 0x01 + ~crc; - furi_string_cat_printf(instance->str_data, "%02X\r\n", crc); - - ind += count_byte; - instance->addr += count_byte; - } - if(instance->file_open) stream_write_string(instance->stream, instance->str_data); - return ret; -} - -FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance) { - furi_assert(instance); - - FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; - if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { - ret.status = FlipperI32HexFileStatusErrorFileWrite; - } - furi_string_reset(instance->str_data); - //I32HEX_TYPE_END_OF_FILE - furi_string_cat_printf(instance->str_data, ":00000001FF\r\n"); - if(instance->file_open) stream_write_string(instance->stream, instance->str_data); - return ret; -} - -void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr) { - furi_assert(instance); - - instance->addr = addr; -} - -const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance) { - furi_assert(instance); - - return furi_string_get_cstr(instance->str_data); -} - -static FlipperI32HexFileRet flipper_i32hex_file_parse_line( - FlipperI32HexFile* instance, - const char* str, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - furi_assert(data); - - char* str1; - uint32_t data_wrire_ind = 0; - uint32_t data_len = 0; - FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusErrorData, .data_size = 0}; - - //Search for start of data I32HEX - str1 = strstr(str, ":"); - do { - if(str1 == NULL) { - ret.status = FlipperI32HexFileStatusErrorData; - break; - } - str1++; - if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { - ret.status = FlipperI32HexFileStatusErrorData; - break; - } - str1++; - if(++data_wrire_ind > data_size) { - ret.status = FlipperI32HexFileStatusErrorOverflow; - break; - } - data_len = 5 + data[0]; // +5 bytes per header and crc - while(data_len > data_wrire_ind) { - str1++; - if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { - ret.status = FlipperI32HexFileStatusErrorData; - break; - } - str1++; - if(++data_wrire_ind > data_size) { - ret.status = FlipperI32HexFileStatusErrorOverflow; - break; - } - } - ret.status = FlipperI32HexFileStatusOK; - ret.data_size = data_wrire_ind; - - } while(0); - return ret; -} - -static bool flipper_i32hex_file_check_data(uint8_t* data, uint32_t data_size) { - furi_assert(data); - - uint8_t crc = 0; - uint32_t data_read_ind = 0; - if(data[0] > data_size) return false; - while(data_read_ind < data_size - 1) { - crc += data[data_read_ind++]; - } - return data[data_size - 1] == ((1 + ~crc) & 0xFF); -} - -static FlipperI32HexFileRet flipper_i32hex_file_parse( - FlipperI32HexFile* instance, - const char* str, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - furi_assert(data); - - FlipperI32HexFileRet ret = flipper_i32hex_file_parse_line(instance, str, data, data_size); - - if((ret.status == FlipperI32HexFileStatusOK) && (ret.data_size > 4)) { - switch(data[3]) { - case I32HEX_TYPE_DATA: - if(flipper_i32hex_file_check_data(data, ret.data_size)) { - ret.data_size -= 5; - memcpy(data, data + 4, ret.data_size); - ret.status = FlipperI32HexFileStatusData; - } else { - ret.status = FlipperI32HexFileStatusErrorCrc; - ret.data_size = 0; - } - break; - case I32HEX_TYPE_END_OF_FILE: - if(flipper_i32hex_file_check_data(data, ret.data_size)) { - ret.status = FlipperI32HexFileStatusEofFile; - ret.data_size = 0; - } else { - ret.status = FlipperI32HexFileStatusErrorCrc; - ret.data_size = 0; - } - break; - case I32HEX_TYPE_EXT_LINEAR_ADDR: - if(flipper_i32hex_file_check_data(data, ret.data_size)) { - data[0] = data[4]; - data[1] = data[5]; - data[3] = 0; - data[4] = 0; - ret.status = FlipperI32HexFileStatusUdateAddr; - ret.data_size = 4; - } else { - ret.status = FlipperI32HexFileStatusErrorCrc; - ret.data_size = 0; - } - break; - case I32HEX_TYPE_START_LINEAR_ADDR: - ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; - ret.data_size = 0; - break; - default: - ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; - ret.data_size = 0; - break; - } - } else { - ret.status = FlipperI32HexFileStatusErrorData; - ret.data_size = 0; - } - return ret; -} - -bool flipper_i32hex_file_check(FlipperI32HexFile* instance) { - furi_assert(instance); - - uint32_t data_size = 280; - uint8_t data[280] = {0}; - bool ret = true; - - if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { - FURI_LOG_E(TAG, "File is not open"); - ret = false; - } else { - stream_rewind(instance->stream); - - while(stream_read_line(instance->stream, instance->str_data)) { - FlipperI32HexFileRet parse_ret = flipper_i32hex_file_parse( - instance, furi_string_get_cstr(instance->str_data), data, data_size); - - if(parse_ret.status < 0) { - ret = false; - } - } - stream_rewind(instance->stream); - } - return ret; -} - -FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( - FlipperI32HexFile* instance, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - furi_assert(data); - - FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; - if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { - ret.status = FlipperI32HexFileStatusErrorFileRead; - } else { - stream_read_line(instance->stream, instance->str_data); - ret = flipper_i32hex_file_parse( - instance, furi_string_get_cstr(instance->str_data), data, data_size); - } - - return ret; -} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h deleted file mode 100644 index 765b94beb..000000000 --- a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include - -typedef struct FlipperI32HexFile FlipperI32HexFile; - -typedef enum { - FlipperI32HexFileStatusOK = 0, - FlipperI32HexFileStatusData = 2, - FlipperI32HexFileStatusUdateAddr = 3, - FlipperI32HexFileStatusEofFile = 4, - FlipperI32HexFileStatusOpenFileWrite = 5, - FlipperI32HexFileStatusOpenFileRead = 6, - - // Errors - FlipperI32HexFileStatusErrorCrc = (-1), - FlipperI32HexFileStatusErrorOverflow = (-2), - FlipperI32HexFileStatusErrorData = (-3), - FlipperI32HexFileStatusErrorUnsupportedCommand = (-4), - FlipperI32HexFileStatusErrorNoOpenFile = (-5), - FlipperI32HexFileStatusErrorFileWrite = (-6), - FlipperI32HexFileStatusErrorFileRead = (-7), - - FlipperI32HexFileStatusReserved = - 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization. -} FlipperI32HexFileStatus; - -typedef struct { - FlipperI32HexFileStatus status; - uint32_t data_size; -} FlipperI32HexFileRet; - -FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr); - -FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name); - -void flipper_i32hex_file_close(FlipperI32HexFile* instance); - -FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( - FlipperI32HexFile* instance, - uint8_t* data, - uint32_t data_size); - -FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance); - -const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance); - -void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr); - -bool flipper_i32hex_file_check(FlipperI32HexFile* instance); - -FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( - FlipperI32HexFile* instance, - uint8_t* data, - uint32_t data_size); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png b/applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png deleted file mode 100644 index 533787fe3569f70196d3f71e8373102dfd3967a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3614 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 Vpqb^9w@m;7?5&;gRaoD1{{|C}E&c!i diff --git a/applications/external/avr_isp_programmer/images/avr_wiring.png b/applications/external/avr_isp_programmer/images/avr_wiring.png deleted file mode 100644 index 957012405127ce4504ef8cd4e805b1fc31963026..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4513 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 diff --git a/applications/external/avr_isp_programmer/images/chif_not_found_83x37.png b/applications/external/avr_isp_programmer/images/chif_not_found_83x37.png deleted file mode 100644 index b03bf3567ade9b2e4ce2d29328032ebd40c89401..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3742 zcmaJ@c{r4N`+r3CEm@K{W62gXW^B!vvCLx07Dh%=4Kv21F=I@Pr9`q-hor0#mFy}? z31!Qg5mMR9k`UfwiIbS$IPdAazdzpI=X##!`~BY6{rTLVdwH(wNjU6eBO$t16aWAT zJ6o(PZ*}86`-S;=ZxMz43jiRBqhc_J?JyV+gGu+Jo+bl8$Y8b`1@AT^k6IgDLEFbi z-ms^;$_ay9(N`j6lQnf!MWheKtL6>Jxisv;;RKZ0a^v|E6Cs7X2VJsd^_d z`fmK?j*U;@cLUzlu6^#>dh*_Ux^y|avRkNLSUlC%(8V}Xya=tb>tl3lbIYemuw|5} z1_O{5t|X}jZ>sYF>k&xg0kwLe7XV*KpO`RE@0e9@urH1)HH*$T#us^sub!2B&|WxF z7O)IUMBfK2t@$Fe(>2|ITmj%@r?1Zha9AHWsdeFV9}tHyOD43{~3Y&v9|j0#kfWk%sa|PVEtp`>lKImecjhZF8K_9PO|y&RE+yWxlgUx&ZnB7 zD?8yL6O@R}yt)j_S4%)&*Lk(SmrEKS)7#)TA2S9Xo-*ePPu4H=_T~R(uO&@j)sL?M zz)}sp;jOkXf24o(r*1ZP(PGmkcRvv6XLmga0FGld!1#_zi&kL(z~)BjKD1I=Y1pGz zFSxH^=Wv7AkCP^s&>GE+Xlb-4DRLk4q)zEYw03OQLuK8Qkhhk~M)fZKu_+8maHIP( zNfblsJ5e~NLAy3eM8K*|csEgXFrLrnGC@62SRo^3UA4hhK<0`Ds6AfRMa@3h*cR$~ z84q%|RbE0dcfjM0SwBxUYXe{xf5g_>KyO4f8N@Eg%zxs~0g5V531q6)RhU1HtKoZ6Ro%hS9D;5mOQVOD>ICYAJ>Gk2Rm~`m=eD z4-6Vdu+>w4CzG@rA{`!&X*Si6Nx;Cgs;}*^dvp)qE7NP;8|bP&qgRw=WV=^ArG1bT zP$2}rp$9t97BiVW*)(Z5sWhp&!< zVIF>$anezASzeXv1DCkM-9~3J;a$=4cJ}#YcW(CW^;hs;qdxe;dcJGqrixSA8;{=3 z8JjO@U-(zp;u5iP(XH_mZN;oTLVGBR>^%?C9qudkT~Tbs8<;}p(x)?|GU)CE-74L4 za>*T{HxJ#^ys4xM!507wsOc3;Ja%ghK+;ho&bYh~m1tjLHSQ(;dnU@bS@TiXz`3)! zHR+qmHCIr@MR{u@!m8&Q&0t%tOZY1vScI6Jea-3Hu73PcO!9Z`tY za&U1#zEWNdmi;oYU?Dx{#qr1-2YSJ1Xx;Spedi&Y_)XgPf>j%Ff?%b%hTxDmXAkm~ zaS$D;3~3$u!v*8rWQoZq-Xx}dx|CeqgS^{s{kyf)Rcgzz35^L_3$5j@rl6*(roH2= z<3gsZWA%NV`(_Si4y|3UyY6(o%P`JDLEposv!=7&XN^5Qc{JpxUR7b$GqPR9?){sN^vU5c}Hn__(xTHRnb$$hf^N}hsvvH zRp*Hm9|g+OSLIC$DRn95pP&DI6D1@OHy~M}d{j9i_%Tx!aRf1%$+@*)asJgx>I{TJ z=$7vOU^r2=yHlr`n(da=XG2k-R0l^d$6raXzt{;*GY4lWwT!gYO&(&c26=x9>s`&x zs?2JfFC2QXV6s46h#S8B+UT}Uj;CSpo2E9*N0+G{3$fcb4FbkWBb+hLQIsds>JVQ@ zvPaqbhfnj_#cRYx1@mv_%-a*@6G+oh*r?};*QWJP+n#nhH_>xW#EfAssB=l&Fm4Y} z5V@a^!k-Xj73H;KV?FGg>dQn6#1Q#g#lXDP)!b?;Ijf|LWf!L!%2fT^zFsR+U7Jql zBy*^eF^40*yn7=={7k&k6d|q^6BpwVYmvx^C+zKkrWvz)hB3io*zed>>}VDR>I{FN zf5=$Zycm26IcWOa=($A;*w6EIKOvi7ciMg*9IRVz5_tN>*pK<;xbf_9v59bnbV!>w zBQ%fGxDrz!Uj&xXL!??d#5*0l@h>ZB-9q`R`)f#t~CzTcx9NcH&uN}tLR#-gM`CK79vMJ^DKx4Lm}#*(bto&1)+;o9aE|( zvy{(%XFE&DF%?^{0~fVZ zt>3w1-XpC%qE0i+F(B%AL&wF2Cwu{OV(y|-G3V!o-_LtH6Cj>rPl(@Rvz5%{5-yj^ z4k@I`UHG6q95SU8NAGxj)wiP8Tw7?mJ!l3^w2WCojN#ku`h+P)O|JkX7>3A z@SnpchwfB`Py2GlPD#-hpG&ho_2Rf!rp;>2ILDTrv6d=^rgnQg^T>RFI6<3b%_6r_ z`kY&9Zq;O#S04+gUI?pu67IJ)qm*OH8Cj_d{X?Gnu0IEk8mU_jqp!VMTOE@hiC}7N zayn}U*jfu^wa&FCRxIbO1~4OW{T5zZ!yguhFPy4p=PvgQ+pG!3M0al`uO>-hb|z&c zb;e4>&gC35hr`D$n42>{3NYQIZp|Eptvg$td03hFTh1R9>`)7($P)9NCy}U=OpE7w?WqIZvJgUC`$G|M_Uu?M=Z(iegF%SAai# z`NyL1jf=ehN<|iqz;dJevDic=8L%SJeaIj?8j(VFB@;=ZLG5HD0Pt&5@dOsZ(E;I0 zr-6yvKHv}(0fqcjmY9LB&vF4>3h)P1Kc^EqyI5IF~f2wU5lk67e zg!c^#@P(7qEX+a35Co5aMrIK~A+*zh!H5u)+F!f~-hSH*Q3L(u!U{mC{aX~l@h}KO zXOcmtV5q*Yfq?enh%g^RKccT52xb6-LZH0cR3B=JfEgm7aM0hE8ZRJ|4z6!T3-H8RAL~rk`Q@@_Of|z8#8zz%a=~7M+Qw(@*~_Mf`iUj|2aEkBc6%Ub3|?d`nMplMCRsD-G|*pJBdEXD zV)aYDzpjS`{dS`D2EscJb2lO0{+PQx=-(}3(8oy0uhgTX+fL`k zyt(6*cX@@0I3*>}r~j|T>)y@tdH&;DIV$ov7@BF+XQ7hWqLV(GY3!CV9|KV_4;@Qw zHgg+M-MBTofZWY;GdHGi+ddaS#dNqK9JDfZcS%67m6T`CT6nRQe wIH}{3#|#He;{>wqaxL5a-a2W^9f$+?fvxTxD_~Z-3{Ny*hjYS~qfcJ^KPy3ZW&i*H diff --git a/applications/external/avr_isp_programmer/images/chip_error_70x22.png b/applications/external/avr_isp_programmer/images/chip_error_70x22.png deleted file mode 100644 index 16f81178c0ec857591db3fe40830a5955db54e73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3688 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 diff --git a/applications/external/avr_isp_programmer/images/chip_long_70x22.png b/applications/external/avr_isp_programmer/images/chip_long_70x22.png deleted file mode 100644 index 3edfff82de952d6f49a012154a23104e3fd935fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3656 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 diff --git a/applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png b/applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QjS=r55&&8K)tZJ>! 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 B&F}yK diff --git a/applications/external/avr_isp_programmer/images/link_waiting_77x56.png b/applications/external/avr_isp_programmer/images/link_waiting_77x56.png deleted file mode 100644 index d7d32aed59a19e24a7e1f2216c806872610a0af5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3883 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 - -//https://github.com/avrdudes/avrdude/blob/master/src/avrintel.c - -const AvrIspChipArr avr_isp_chip_arr[] = { // Value of -1 typically means unknown - //{mcu_name, mcuid, family, {sig, na, ture}, flstart, flsize, pgsiz, nb, bootsz, eestart, eesize, ep, rambeg, ramsiz, nf, nl, ni}, // Source - {"ATtiny4", 0, F_AVR8L, {0x1E, 0x8F, 0x0A}, 0, 0x00200, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny5", 1, F_AVR8L, {0x1E, 0x8F, 0x09}, 0, 0x00200, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny9", 2, F_AVR8L, {0x1E, 0x90, 0x08}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny10", 3, F_AVR8L, {0x1E, 0x90, 0x03}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny20", 4, F_AVR8L, {0x1E, 0x91, 0x0F}, 0, 0x00800, 0x020, 0, 0, 0, 0, 0, 0x0040, 0x0080, 1, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny40", 5, F_AVR8L, {0x1E, 0x92, 0x0E}, 0, 0x01000, 0x040, 0, 0, 0, 0, 0, 0x0040, 0x0100, 1, 1, 18}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny102", 6, F_AVR8L, {0x1E, 0x90, 0x0C}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 16}, // atdf, avrdude, boot size (manual) - {"ATtiny104", 7, F_AVR8L, {0x1E, 0x90, 0x0B}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 16}, // atdf, avrdude, boot size (manual) - - {"ATtiny11", 8, F_AVR8, {0x1E, 0x90, 0x04}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 1, 0x0060, 0x0020, 1, 1, 5}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny12", 9, F_AVR8, {0x1E, 0x90, 0x05}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 2, 0x0060, 0x0020, 1, 1, 6}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny13", 10, F_AVR8, {0x1E, 0x90, 0x07}, 0, 0x00400, 0x020, 0, 0, 0, 0x0040, 4, 0x0060, 0x0040, 2, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny13A", 11, F_AVR8, {0x1E, 0x90, 0x07}, 0, 0x00400, 0x020, 0, 0, 0, 0x0040, 4, 0x0060, 0x0040, 2, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny15", 12, F_AVR8, {0x1E, 0x90, 0x06}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 2, 0x0060, 0x0020, 1, 1, 9}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny22", 13, F_AVR8, {0x1E, 0x91, 0x06}, 0, 0x00800, -1, 0, 0, -1, -1, -1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, boot size (manual) - {"ATtiny24", 14, F_AVR8, {0x1E, 0x91, 0x0B}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny24A", 15, F_AVR8, {0x1E, 0x91, 0x0B}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny25", 16, F_AVR8, {0x1E, 0x91, 0x08}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny26", 17, F_AVR8, {0x1E, 0x91, 0x09}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 2, 1, 12}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny28", 18, F_AVR8, {0x1E, 0x91, 0x07}, 0, 0x00800, 0x002, 0, 0, 0, 0, 0, 0x0060, 0x0020, 1, 1, 6}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny43U", 19, F_AVR8, {0x1E, 0x92, 0x0C}, 0, 0x01000, 0x040, 0, 0, 0, 0x0040, 4, 0x0060, 0x0100, 3, 1, 16}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny44", 20, F_AVR8, {0x1E, 0x92, 0x07}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny44A", 21, F_AVR8, {0x1E, 0x92, 0x07}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny45", 22, F_AVR8, {0x1E, 0x92, 0x06}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny48", 23, F_AVR8, {0x1E, 0x92, 0x09}, 0, 0x01000, 0x040, 0, 0, 0, 0x0040, 4, 0x0100, 0x0100, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny84", 24, F_AVR8, {0x1E, 0x93, 0x0C}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny84A", 25, F_AVR8, {0x1E, 0x93, 0x0C}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny85", 26, F_AVR8, {0x1E, 0x93, 0x0B}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny87", 27, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny88", 28, F_AVR8, {0x1E, 0x93, 0x11}, 0, 0x02000, 0x040, 0, 0, 0, 0x0040, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny167", 29, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny261", 30, F_AVR8, {0x1E, 0x91, 0x0C}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny261A", 31, F_AVR8, {0x1E, 0x91, 0x0C}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny441", 32, F_AVR8, {0x1E, 0x92, 0x15}, 0, 0x01000, 0x010, 0, 0, 0, 0x0100, 4, 0x0100, 0x0100, 3, 1, 30}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny461", 33, F_AVR8, {0x1E, 0x92, 0x08}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny461A", 34, F_AVR8, {0x1E, 0x92, 0x08}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny828", 35, F_AVR8, {0x1E, 0x93, 0x14}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny828R", 36, F_AVR8, {0x1E, 0x93, 0x14}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // avrdude, from ATtiny828 - {"ATtiny841", 37, F_AVR8, {0x1E, 0x93, 0x15}, 0, 0x02000, 0x010, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 30}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny861", 38, F_AVR8, {0x1E, 0x93, 0x0D}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny861A", 39, F_AVR8, {0x1E, 0x93, 0x0D}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1634", 40, F_AVR8, {0x1E, 0x94, 0x12}, 0, 0x04000, 0x020, 0, 0, 0, 0x0100, 4, 0x0100, 0x0400, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1634R", 41, F_AVR8, {0x1E, 0x94, 0x12}, 0, 0x04000, 0x020, 0, 0, 0, 0x0100, 4, 0x0100, 0x0400, 3, 1, 28}, // avrdude, from ATtiny1634 - {"ATtiny2313", 42, F_AVR8, {0x1E, 0x91, 0x0A}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny2313A", 43, F_AVR8, {0x1E, 0x91, 0x0A}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny4313", 44, F_AVR8, {0x1E, 0x92, 0x0D}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8", 45, F_AVR8, {0x1E, 0x93, 0x07}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8A", 46, F_AVR8, {0x1E, 0x93, 0x07}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8HVA", 47, F_AVR8, {0x1E, 0x93, 0x10}, 0, 0x02000, 0x080, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 1, 1, 21}, // atdf, avr-gcc 12.2.0 - {"ATmega8U2", 48, F_AVR8, {0x1E, 0x93, 0x89}, 0, 0x02000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega16", 49, F_AVR8, {0x1E, 0x94, 0x03}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega16A", 50, F_AVR8, {0x1E, 0x94, 0x03}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega16HVA", 51, F_AVR8, {0x1E, 0x94, 0x0C}, 0, 0x04000, 0x080, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 1, 1, 21}, // atdf, avr-gcc 12.2.0 - {"ATmega16HVB", 52, F_AVR8, {0x1E, 0x94, 0x0D}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 2, 1, 29}, // atdf, avr-gcc 12.2.0 - {"ATmega16HVBrevB", 53, F_AVR8, {0x1E, 0x94, 0x0D}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 2, 1, 29}, // atdf, avr-gcc 12.2.0 - {"ATmega16M1", 54, F_AVR8, {0x1E, 0x94, 0x84}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0 - {"ATmega16HVA2", 55, F_AVR8, {0x1E, 0x94, 0x0E}, 0, 0x04000, 0x080, -1, -1, -1, -1, -1, 0x0100, 0x0400, 2, 1, 22}, // avr-gcc 12.2.0 - {"ATmega16U2", 56, F_AVR8, {0x1E, 0x94, 0x89}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega16U4", 57, F_AVR8, {0x1E, 0x94, 0x88}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0500, 3, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32", 58, F_AVR8, {0x1E, 0x95, 0x02}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0060, 0x0800, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32A", 59, F_AVR8, {0x1E, 0x95, 0x02}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0060, 0x0800, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32HVB", 60, F_AVR8, {0x1E, 0x95, 0x10}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 2, 1, 29}, // atdf, avr-gcc 12.2.0 - {"ATmega32HVBrevB", 61, F_AVR8, {0x1E, 0x95, 0x10}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 2, 1, 29}, // atdf, avr-gcc 12.2.0 - {"ATmega32C1", 62, F_AVR8, {0x1E, 0x95, 0x86}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0 - {"ATmega32M1", 63, F_AVR8, {0x1E, 0x95, 0x84}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32U2", 64, F_AVR8, {0x1E, 0x95, 0x8A}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0400, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32U4", 65, F_AVR8, {0x1E, 0x95, 0x87}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0a00, 3, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32U6", 66, F_AVR8, {0x1E, 0x95, 0x88}, 0, 0x08000, 0x080, 4, 0x0200, -1, -1, -1, 0x0100, 0x0a00, 3, 1, 38}, // avr-gcc 12.2.0, boot size (manual) - {"ATmega48", 67, F_AVR8, {0x1E, 0x92, 0x05}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega48A", 68, F_AVR8, {0x1E, 0x92, 0x05}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega48P", 69, F_AVR8, {0x1E, 0x92, 0x0A}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega48PA", 70, F_AVR8, {0x1E, 0x92, 0x0A}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega48PB", 71, F_AVR8, {0x1E, 0x92, 0x10}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 27}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega64", 72, F_AVR8, {0x1E, 0x96, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega64A", 73, F_AVR8, {0x1E, 0x96, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega64HVE", 74, F_AVR8, {0x1E, 0x96, 0x10}, 0, 0x10000, 0x080, 4, 0x0400, -1, -1, -1, 0x0100, 0x1000, 2, 1, 25}, // avr-gcc 12.2.0, boot size (manual) - {"ATmega64C1", 75, F_AVR8, {0x1E, 0x96, 0x86}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0 - {"ATmega64M1", 76, F_AVR8, {0x1E, 0x96, 0x84}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega64HVE2", 77, F_AVR8, {0x1E, 0x96, 0x10}, 0, 0x10000, 0x080, 4, 0x0400, 0, 0x0400, 4, 0x0100, 0x1000, 2, 1, 25}, // atdf, avr-gcc 12.2.0 - {"ATmega64RFR2", 78, F_AVR8, {0x1E, 0xA6, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0200, 0x2000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88", 79, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88A", 80, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88P", 81, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88PA", 82, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88PB", 83, F_AVR8, {0x1E, 0x93, 0x16}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 27}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega103", 84, F_AVR8, {0x1E, 0x97, 0x01}, 0, 0x20000, 0x100, 0, 0, 0, 0x1000, 1, 0x0060, 0x0fa0, 1, 1, 24}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega128", 85, F_AVR8, {0x1E, 0x97, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega128A", 86, F_AVR8, {0x1E, 0x97, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega128RFA1", 87, F_AVR8, {0x1E, 0xA7, 0x01}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 72}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega128RFR2", 88, F_AVR8, {0x1E, 0xA7, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega161", 89, F_AVR8, {0x1E, 0x94, 0x01}, 0, 0x04000, 0x080, 1, 0x0400, 0, 0x0200, 1, 0x0060, 0x0400, 1, 1, 21}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega162", 90, F_AVR8, {0x1E, 0x94, 0x04}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega163", 91, F_AVR8, {0x1E, 0x94, 0x02}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 1, 0x0060, 0x0400, 2, 1, 18}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega164A", 92, F_AVR8, {0x1E, 0x94, 0x0F}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega164P", 93, F_AVR8, {0x1E, 0x94, 0x0A}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega164PA", 94, F_AVR8, {0x1E, 0x94, 0x0A}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega165", 95, F_AVR8, {0x1E, 0x94, 0x10}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega165A", 96, F_AVR8, {0x1E, 0x94, 0x10}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega165P", 97, F_AVR8, {0x1E, 0x94, 0x07}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega165PA", 98, F_AVR8, {0x1E, 0x94, 0x07}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168", 99, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168A", 100, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168P", 101, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168PA", 102, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168PB", 103, F_AVR8, {0x1E, 0x94, 0x15}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 27}, // atdf, avr-gcc 7.3.0, avrdude - {"ATmega169", 104, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega169A", 105, F_AVR8, {0x1E, 0x94, 0x11}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega169P", 106, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega169PA", 107, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega256RFR2", 108, F_AVR8, {0x1E, 0xA8, 0x02}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x2000, 8, 0x0200, 0x8000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega323", 109, F_AVR8, {0x1E, 0x95, 0x01}, 0, 0x08000, 0x080, 4, 0x0200, -1, -1, -1, 0x0060, 0x0800, 2, 1, 21}, // avr-gcc 12.2.0, boot size (manual) - {"ATmega324A", 110, F_AVR8, {0x1E, 0x95, 0x15}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega324P", 111, F_AVR8, {0x1E, 0x95, 0x08}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega324PA", 112, F_AVR8, {0x1E, 0x95, 0x11}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega324PB", 113, F_AVR8, {0x1E, 0x95, 0x17}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 51}, // atdf, avrdude - {"ATmega325", 114, F_AVR8, {0x1E, 0x95, 0x05}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega325A", 115, F_AVR8, {0x1E, 0x95, 0x05}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega325P", 116, F_AVR8, {0x1E, 0x95, 0x0D}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega325PA", 117, F_AVR8, {0x1E, 0x95, 0x0D}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega328", 118, F_AVR8, {0x1E, 0x95, 0x14}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega328P", 119, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega328PB", 120, F_AVR8, {0x1E, 0x95, 0x16}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 45}, // atdf, avr-gcc 7.3.0, avrdude - {"ATmega329", 121, F_AVR8, {0x1E, 0x95, 0x03}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega329A", 122, F_AVR8, {0x1E, 0x95, 0x03}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega329P", 123, F_AVR8, {0x1E, 0x95, 0x0B}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega329PA", 124, F_AVR8, {0x1E, 0x95, 0x0B}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega406", 125, F_AVR8, {0x1E, 0x95, 0x07}, 0, 0x0a000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0800, 2, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega640", 126, F_AVR8, {0x1E, 0x96, 0x08}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644", 127, F_AVR8, {0x1E, 0x96, 0x09}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644A", 128, F_AVR8, {0x1E, 0x96, 0x09}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644P", 129, F_AVR8, {0x1E, 0x96, 0x0A}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644PA", 130, F_AVR8, {0x1E, 0x96, 0x0A}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644RFR2", 131, F_AVR8, {0x1E, 0xA6, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0200, 0x2000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega645", 132, F_AVR8, {0x1E, 0x96, 0x05}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega645A", 133, F_AVR8, {0x1E, 0x96, 0x05}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega645P", 134, F_AVR8, {0x1E, 0x96, 0x0D}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega649", 135, F_AVR8, {0x1E, 0x96, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega649A", 136, F_AVR8, {0x1E, 0x96, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega649P", 137, F_AVR8, {0x1E, 0x96, 0x0B}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1280", 138, F_AVR8, {0x1E, 0x97, 0x03}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1281", 139, F_AVR8, {0x1E, 0x97, 0x04}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1284", 140, F_AVR8, {0x1E, 0x97, 0x06}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x4000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1284P", 141, F_AVR8, {0x1E, 0x97, 0x05}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x4000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1284RFR2", 142, F_AVR8, {0x1E, 0xA7, 0x03}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega2560", 143, F_AVR8, {0x1E, 0x98, 0x01}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega2561", 144, F_AVR8, {0x1E, 0x98, 0x02}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega2564RFR2", 145, F_AVR8, {0x1E, 0xA8, 0x03}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x2000, 8, 0x0200, 0x8000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3250", 146, F_AVR8, {0x1E, 0x95, 0x06}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3250A", 147, F_AVR8, {0x1E, 0x95, 0x06}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3250P", 148, F_AVR8, {0x1E, 0x95, 0x0E}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3250PA", 149, F_AVR8, {0x1E, 0x95, 0x0E}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3290", 150, F_AVR8, {0x1E, 0x95, 0x04}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3290A", 151, F_AVR8, {0x1E, 0x95, 0x04}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3290P", 152, F_AVR8, {0x1E, 0x95, 0x0C}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3290PA", 153, F_AVR8, {0x1E, 0x95, 0x0C}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6450", 154, F_AVR8, {0x1E, 0x96, 0x06}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6450A", 155, F_AVR8, {0x1E, 0x96, 0x06}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6450P", 156, F_AVR8, {0x1E, 0x96, 0x0E}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6490", 157, F_AVR8, {0x1E, 0x96, 0x04}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6490A", 158, F_AVR8, {0x1E, 0x96, 0x04}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6490P", 159, F_AVR8, {0x1E, 0x96, 0x0C}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8515", 160, F_AVR8, {0x1E, 0x93, 0x06}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0200, 2, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8535", 161, F_AVR8, {0x1E, 0x93, 0x08}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0200, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"AT43USB320", 162, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0200, -1, -1, 0}, // avr-gcc 12.2.0 - {"AT43USB355", 163, F_AVR8, {0xff, -1, -1}, 0, 0x06000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0400, -1, -1, 0}, // avr-gcc 12.2.0 - {"AT76C711", 164, F_AVR8, {0xff, -1, -1}, 0, 0x04000, -1, -1, -1, -1, -1, -1, 0x0060, 0x07a0, -1, -1, 0}, // avr-gcc 12.2.0 - {"AT86RF401", 165, F_AVR8, {0x1E, 0x91, 0x81}, 0, 0x00800, -1, -1, -1, -1, -1, -1, 0x0060, 0x0080, 0, 1, 3}, // avr-gcc 12.2.0 - {"AT90PWM1", 166, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0 - {"AT90PWM2", 167, F_AVR8, {0x1E, 0x93, 0x81}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90PWM2B", 168, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM3", 169, F_AVR8, {0x1E, 0x93, 0x81}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM3B", 170, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90CAN32", 171, F_AVR8, {0x1E, 0x95, 0x81}, 0, 0x08000, 0x100, 4, 0x0400, 0, 0x0400, 8, 0x0100, 0x0800, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90CAN64", 172, F_AVR8, {0x1E, 0x96, 0x81}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM81", 173, F_AVR8, {0x1E, 0x93, 0x88}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0100, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"AT90USB82", 174, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90SCR100", 175, F_AVR8, {0x1E, 0x96, 0xC1}, 0, 0x10000, 0x100, 4, 0x0200, -1, -1, -1, 0x0100, 0x1000, 3, 1, 38}, // avr-gcc 12.2.0, boot size (manual) - {"AT90CAN128", 176, F_AVR8, {0x1E, 0x97, 0x81}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM161", 177, F_AVR8, {0x1E, 0x94, 0x8B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"AT90USB162", 178, F_AVR8, {0x1E, 0x94, 0x82}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM216", 179, F_AVR8, {0x1E, 0x94, 0x83}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM316", 180, F_AVR8, {0x1E, 0x94, 0x83}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90USB646", 181, F_AVR8, {0x1E, 0x96, 0x82}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90USB647", 182, F_AVR8, {0x1E, 0x96, 0x82}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90S1200", 183, F_AVR8, {0x1E, 0x90, 0x01}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 1, 0x0060, 0x0020, 1, 1, 4}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90USB1286", 184, F_AVR8, {0x1E, 0x97, 0x82}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x2000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90USB1287", 185, F_AVR8, {0x1E, 0x97, 0x82}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x2000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90S2313", 186, F_AVR8, {0x1E, 0x91, 0x01}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, 1, 1, 11}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S2323", 187, F_AVR8, {0x1E, 0x91, 0x02}, 0, 0x00800, -1, 0, 0, -1, -1, -1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, boot size (manual) - {"AT90S2333", 188, F_AVR8, {0x1E, 0x91, 0x05}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, -1, -1, 14}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S2343", 189, F_AVR8, {0x1E, 0x91, 0x03}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S4414", 190, F_AVR8, {0x1E, 0x92, 0x01}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0100, 1, 1, 13}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S4433", 191, F_AVR8, {0x1E, 0x92, 0x03}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0080, 1, 1, 14}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S4434", 192, F_AVR8, {0x1E, 0x92, 0x02}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0100, 1, 1, 17}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S8515", 193, F_AVR8, {0x1E, 0x93, 0x01}, 0, 0x02000, 0x001, 0, 0, 0, 0x0200, 1, 0x0060, 0x0200, 1, 1, 13}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90C8534", 194, F_AVR8, {0xff, -1, -1}, 0, 0x02000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0100, -1, -1, 0}, // avr-gcc 12.2.0 - {"AT90S8535", 195, F_AVR8, {0x1E, 0x93, 0x03}, 0, 0x02000, 0x001, 0, 0, 0, 0x0200, 1, 0x0060, 0x0200, 1, 1, 17}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT94K", 196, F_AVR8, {0xff, -1, -1}, 0, 0x08000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0fa0, -1, -1, 0}, // avr-gcc 12.2.0 - {"ATA5272", 197, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 37}, // atdf, avr-gcc 12.2.0 - {"ATA5505", 198, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"ATA5700M322", 199, F_AVR8, {0x1E, 0x95, 0x67}, 0x08000, 0x08000, 0x040, 0, 0, 0, 0x0880, 16, 0x0200, 0x0400, 1, 1, 51}, // atdf - {"ATA5702M322", 200, F_AVR8, {0x1E, 0x95, 0x69}, 0x08000, 0x08000, 0x040, 0, 0, 0, 0x0880, 16, 0x0200, 0x0400, 1, 1, 51}, // atdf, avr-gcc 12.2.0 - {"ATA5781", 201, F_AVR8, {0x1E, 0x95, 0x64}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA5782", 202, F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 12.2.0 - {"ATA5783", 203, F_AVR8, {0x1E, 0x95, 0x66}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA5787", 204, F_AVR8, {0x1E, 0x94, 0x6C}, 0x08000, 0x05200, 0x040, 0, 0, 0, 0x0400, 16, 0x0200, 0x0800, 1, 1, 44}, // atdf - {"ATA5790", 205, F_AVR8, {0x1E, 0x94, 0x61}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 30}, // atdf, avr-gcc 12.2.0 - {"ATA5790N", 206, F_AVR8, {0x1E, 0x94, 0x62}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 31}, // atdf, avr-gcc 12.2.0 - {"ATA5791", 207, F_AVR8, {0x1E, 0x94, 0x62}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 31}, // atdf, avr-gcc 7.3.0 - {"ATA5795", 208, F_AVR8, {0x1E, 0x93, 0x61}, 0, 0x02000, 0x040, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 23}, // atdf, avr-gcc 12.2.0 - {"ATA5831", 209, F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 12.2.0 - {"ATA5832", 210, F_AVR8, {0x1E, 0x95, 0x62}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA5833", 211, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA5835", 212, F_AVR8, {0x1E, 0x94, 0x6B}, 0x08000, 0x05200, 0x040, 0, 0, 0, 0x0400, 16, 0x0200, 0x0800, 1, 1, 44}, // atdf - {"ATA6285", 213, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0140, 4, 0x0100, 0x0200, 2, 1, 27}, // atdf, avr-gcc 12.2.0 - {"ATA6286", 214, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0140, 4, 0x0100, 0x0200, 2, 1, 27}, // atdf, avr-gcc 12.2.0 - {"ATA6289", 215, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, -1, -1, -1, 0x0100, 0x0200, 2, 1, 27}, // avr-gcc 12.2.0, boot size (manual) - {"ATA6612C", 216, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0 - {"ATA6613C", 217, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0 - {"ATA6614Q", 218, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0 - {"ATA6616C", 219, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"ATA6617C", 220, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"ATA8210", 221, F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 7.3.0 - {"ATA8215", 222, F_AVR8, {0x1E, 0x95, 0x64}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA8510", 223, F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 7.3.0 - {"ATA8515", 224, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA664251", 225, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"M3000", 226, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x1000, 0x1000, -1, -1, 0}, // avr-gcc 12.2.0 - {"LGT8F88P", 227, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // avrdude, from ATmega88 - {"LGT8F168P", 228, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // avrdude, from ATmega168P - {"LGT8F328P", 229, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // avrdude, from ATmega328P - - {"ATxmega8E5", 230, F_XMEGA, {0x1E, 0x93, 0x41}, 0, 0x02800, 0x080, 1, 0x0800, 0, 0x0200, 32, 0x2000, 0x0400, 7, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16A4", 231, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 94}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16A4U", 232, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16C4", 233, F_XMEGA, {0x1E, 0x94, 0x43}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16D4", 234, F_XMEGA, {0x1E, 0x94, 0x42}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16E5", 235, F_XMEGA, {0x1E, 0x94, 0x45}, 0, 0x05000, 0x080, 1, 0x1000, 0, 0x0200, 32, 0x2000, 0x0800, 7, 1, 43}, // atdf, avr-gcc 7.3.0, avrdude - {"ATxmega32C3", 236, F_XMEGA, {0x1E, 0x95, 0x49}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0 - {"ATxmega32D3", 237, F_XMEGA, {0x1E, 0x95, 0x4A}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 114}, // atdf, avr-gcc 12.2.0 - {"ATxmega32A4", 238, F_XMEGA, {0x1E, 0x95, 0x41}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 94}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega32A4U", 239, F_XMEGA, {0x1E, 0x95, 0x41}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega32C4", 240, F_XMEGA, {0x1E, 0x95, 0x44}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega32D4", 241, F_XMEGA, {0x1E, 0x95, 0x42}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega32E5", 242, F_XMEGA, {0x1E, 0x95, 0x4C}, 0, 0x09000, 0x080, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 7, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A1", 243, F_XMEGA, {0x1E, 0x96, 0x4E}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 125}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A1U", 244, F_XMEGA, {0x1E, 0x96, 0x4E}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64B1", 245, F_XMEGA, {0x1E, 0x96, 0x52}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 81}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A3", 246, F_XMEGA, {0x1E, 0x96, 0x42}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A3U", 247, F_XMEGA, {0x1E, 0x96, 0x42}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64B3", 248, F_XMEGA, {0x1E, 0x96, 0x51}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 54}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64C3", 249, F_XMEGA, {0x1E, 0x96, 0x49}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64D3", 250, F_XMEGA, {0x1E, 0x96, 0x4A}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A4", 251, F_XMEGA, {0x1E, 0x96, 0x46}, 0, 0x11000, 0x100, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega64A4U", 252, F_XMEGA, {0x1E, 0x96, 0x46}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64D4", 253, F_XMEGA, {0x1E, 0x96, 0x47}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A1", 254, F_XMEGA, {0x1E, 0x97, 0x4C}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 125}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A1revD", 255, F_XMEGA, {0x1E, 0x97, 0x41}, 0, 0x22000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega128A1U", 256, F_XMEGA, {0x1E, 0x97, 0x4C}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128B1", 257, F_XMEGA, {0x1E, 0x97, 0x4D}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 81}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A3", 258, F_XMEGA, {0x1E, 0x97, 0x42}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A3U", 259, F_XMEGA, {0x1E, 0x97, 0x42}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128B3", 260, F_XMEGA, {0x1E, 0x97, 0x4B}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 54}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128C3", 261, F_XMEGA, {0x1E, 0x97, 0x52}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128D3", 262, F_XMEGA, {0x1E, 0x97, 0x48}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A4", 263, F_XMEGA, {0x1E, 0x97, 0x46}, 0, 0x22000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega128A4U", 264, F_XMEGA, {0x1E, 0x97, 0x46}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128D4", 265, F_XMEGA, {0x1E, 0x97, 0x47}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega192A1", 266, F_XMEGA, {0x1E, 0x97, 0x4E}, 0, 0x32000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega192A3", 267, F_XMEGA, {0x1E, 0x97, 0x44}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega192A3U", 268, F_XMEGA, {0x1E, 0x97, 0x44}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega192C3", 269, F_XMEGA, {0x1E, 0x97, 0x51}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega192D3", 270, F_XMEGA, {0x1E, 0x97, 0x49}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256A1", 271, F_XMEGA, {0x1E, 0x98, 0x46}, 0, 0x42000, 0x200, -1, -1, 0, 0x1000, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega256A3", 272, F_XMEGA, {0x1E, 0x98, 0x42}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256A3B", 273, F_XMEGA, {0x1E, 0x98, 0x43}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256A3BU", 274, F_XMEGA, {0x1E, 0x98, 0x43}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256A3U", 275, F_XMEGA, {0x1E, 0x98, 0x42}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256C3", 276, F_XMEGA, {0x1E, 0x98, 0x46}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256D3", 277, F_XMEGA, {0x1E, 0x98, 0x44}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega384C3", 278, F_XMEGA, {0x1E, 0x98, 0x45}, 0, 0x62000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x8000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega384D3", 279, F_XMEGA, {0x1E, 0x98, 0x47}, 0, 0x62000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x8000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - - {"ATtiny202", 280, F_AVR8X, {0x1E, 0x91, 0x23}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny204", 281, F_AVR8X, {0x1E, 0x91, 0x22}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny212", 282, F_AVR8X, {0x1E, 0x91, 0x21}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny214", 283, F_AVR8X, {0x1E, 0x91, 0x20}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny402", 284, F_AVR8X, {0x1E, 0x92, 0x27}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny404", 285, F_AVR8X, {0x1E, 0x92, 0x26}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny406", 286, F_AVR8X, {0x1E, 0x92, 0x25}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny412", 287, F_AVR8X, {0x1E, 0x92, 0x23}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny414", 288, F_AVR8X, {0x1E, 0x92, 0x22}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny416", 289, F_AVR8X, {0x1E, 0x92, 0x21}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny416auto", 290, F_AVR8X, {0x1E, 0x92, 0x28}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf - {"ATtiny417", 291, F_AVR8X, {0x1E, 0x92, 0x20}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny424", 292, F_AVR8X, {0x1E, 0x92, 0x2C}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude - {"ATtiny426", 293, F_AVR8X, {0x1E, 0x92, 0x2B}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude - {"ATtiny427", 294, F_AVR8X, {0x1E, 0x92, 0x2A}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude - {"ATtiny804", 295, F_AVR8X, {0x1E, 0x93, 0x25}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny806", 296, F_AVR8X, {0x1E, 0x93, 0x24}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny807", 297, F_AVR8X, {0x1E, 0x93, 0x23}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny814", 298, F_AVR8X, {0x1E, 0x93, 0x22}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny816", 299, F_AVR8X, {0x1E, 0x93, 0x21}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny817", 300, F_AVR8X, {0x1E, 0x93, 0x20}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny824", 301, F_AVR8X, {0x1E, 0x93, 0x29}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude - {"ATtiny826", 302, F_AVR8X, {0x1E, 0x93, 0x28}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude - {"ATtiny827", 303, F_AVR8X, {0x1E, 0x93, 0x27}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude - {"ATtiny1604", 304, F_AVR8X, {0x1E, 0x94, 0x25}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1606", 305, F_AVR8X, {0x1E, 0x94, 0x24}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1607", 306, F_AVR8X, {0x1E, 0x94, 0x23}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1614", 307, F_AVR8X, {0x1E, 0x94, 0x22}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1616", 308, F_AVR8X, {0x1E, 0x94, 0x21}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1617", 309, F_AVR8X, {0x1E, 0x94, 0x20}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1624", 310, F_AVR8X, {0x1E, 0x94, 0x2A}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude - {"ATtiny1626", 311, F_AVR8X, {0x1E, 0x94, 0x29}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude - {"ATtiny1627", 312, F_AVR8X, {0x1E, 0x94, 0x28}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude - {"ATtiny3214", 313, F_AVR8X, {0x1E, 0x95, 0x20}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // avr-gcc 12.2.0 - {"ATtiny3216", 314, F_AVR8X, {0x1E, 0x95, 0x21}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny3217", 315, F_AVR8X, {0x1E, 0x95, 0x22}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny3224", 316, F_AVR8X, {0x1E, 0x95, 0x28}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude - {"ATtiny3226", 317, F_AVR8X, {0x1E, 0x95, 0x27}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude - {"ATtiny3227", 318, F_AVR8X, {0x1E, 0x95, 0x26}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude - {"ATmega808", 319, F_AVR8X, {0x1E, 0x93, 0x26}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega809", 320, F_AVR8X, {0x1E, 0x93, 0x2A}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1608", 321, F_AVR8X, {0x1E, 0x94, 0x27}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1609", 322, F_AVR8X, {0x1E, 0x94, 0x26}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3208", 323, F_AVR8X, {0x1E, 0x95, 0x30}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3209", 324, F_AVR8X, {0x1E, 0x95, 0x31}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega4808", 325, F_AVR8X, {0x1E, 0x96, 0x50}, 0, 0x0c000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega4809", 326, F_AVR8X, {0x1E, 0x96, 0x51}, 0, 0x0c000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude - {"AVR8EA28", 327, F_AVR8X, {0x1E, 0x93, 0x2C}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR8EA32", 328, F_AVR8X, {0x1E, 0x93, 0x2B}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR16DD14", 329, F_AVR8X, {0x1E, 0x94, 0x34}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude - {"AVR16DD20", 330, F_AVR8X, {0x1E, 0x94, 0x33}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude - {"AVR16DD28", 331, F_AVR8X, {0x1E, 0x94, 0x32}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude - {"AVR16EA28", 332, F_AVR8X, {0x1E, 0x94, 0x37}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR16DD32", 333, F_AVR8X, {0x1E, 0x94, 0x31}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude - {"AVR16EA32", 334, F_AVR8X, {0x1E, 0x94, 0x36}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR16EA48", 335, F_AVR8X, {0x1E, 0x94, 0x35}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR32DD14", 336, F_AVR8X, {0x1E, 0x95, 0x3B}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude - {"AVR32DD20", 337, F_AVR8X, {0x1E, 0x95, 0x3A}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude - {"AVR32DA28", 338, F_AVR8X, {0x1E, 0x95, 0x34}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 41}, // atdf, avrdude - {"AVR32DB28", 339, F_AVR8X, {0x1E, 0x95, 0x37}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 42}, // atdf, avrdude - {"AVR32DD28", 340, F_AVR8X, {0x1E, 0x95, 0x39}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude - {"AVR32EA28", 341, F_AVR8X, {0x1E, 0x95, 0x3E}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR32DA32", 342, F_AVR8X, {0x1E, 0x95, 0x33}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 44}, // atdf, avrdude - {"AVR32DB32", 343, F_AVR8X, {0x1E, 0x95, 0x36}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 44}, // atdf, avrdude - {"AVR32DD32", 344, F_AVR8X, {0x1E, 0x95, 0x38}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude - {"AVR32EA32", 345, F_AVR8X, {0x1E, 0x95, 0x3D}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR32DA48", 346, F_AVR8X, {0x1E, 0x95, 0x32}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 58}, // atdf, avrdude - {"AVR32DB48", 347, F_AVR8X, {0x1E, 0x95, 0x35}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 61}, // atdf, avrdude - {"AVR32EA48", 348, F_AVR8X, {0x1E, 0x95, 0x3C}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR64DD14", 349, F_AVR8X, {0x1E, 0x96, 0x1D}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude - {"AVR64DD20", 350, F_AVR8X, {0x1E, 0x96, 0x1C}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude - {"AVR64DA28", 351, F_AVR8X, {0x1E, 0x96, 0x15}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 41}, // atdf, avrdude - {"AVR64DB28", 352, F_AVR8X, {0x1E, 0x96, 0x19}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 42}, // atdf, avrdude - {"AVR64DD28", 353, F_AVR8X, {0x1E, 0x96, 0x1B}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude - {"AVR64EA28", 354, F_AVR8X, {0x1E, 0x96, 0x20}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 37}, // atdf, avrdude - {"AVR64DA32", 355, F_AVR8X, {0x1E, 0x96, 0x14}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 44}, // atdf, avrdude - {"AVR64DB32", 356, F_AVR8X, {0x1E, 0x96, 0x18}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 44}, // atdf, avrdude - {"AVR64DD32", 357, F_AVR8X, {0x1E, 0x96, 0x1A}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude - {"AVR64EA32", 358, F_AVR8X, {0x1E, 0x96, 0x1F}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 37}, // atdf, avrdude - {"AVR64DA48", 359, F_AVR8X, {0x1E, 0x96, 0x13}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 58}, // atdf, avrdude - {"AVR64DB48", 360, F_AVR8X, {0x1E, 0x96, 0x17}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 61}, // atdf, avrdude - {"AVR64EA48", 361, F_AVR8X, {0x1E, 0x96, 0x1E}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 45}, // atdf, avrdude - {"AVR64DA64", 362, F_AVR8X, {0x1E, 0x96, 0x12}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 64}, // atdf, avrdude - {"AVR64DB64", 363, F_AVR8X, {0x1E, 0x96, 0x16}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 65}, // atdf, avrdude - {"AVR128DA28", 364, F_AVR8X, {0x1E, 0x97, 0x0A}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 41}, // atdf, avrdude - {"AVR128DB28", 365, F_AVR8X, {0x1E, 0x97, 0x0E}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 42}, // atdf, avrdude - {"AVR128DA32", 366, F_AVR8X, {0x1E, 0x97, 0x09}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 44}, // atdf, avrdude - {"AVR128DB32", 367, F_AVR8X, {0x1E, 0x97, 0x0D}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 44}, // atdf, avrdude - {"AVR128DA48", 368, F_AVR8X, {0x1E, 0x97, 0x08}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 58}, // atdf, avrdude - {"AVR128DB48", 369, F_AVR8X, {0x1E, 0x97, 0x0C}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 61}, // atdf, avrdude - {"AVR128DA64", 370, F_AVR8X, {0x1E, 0x97, 0x07}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 64}, // atdf, avrdude - {"AVR128DB64", 371, F_AVR8X, {0x1E, 0x97, 0x0B}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 65}, // atdf, avrdude -}; - -const size_t avr_isp_chip_arr_size = COUNT_OF(avr_isp_chip_arr); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h deleted file mode 100644 index 66f16a7b9..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -#define F_AVR8L 1 // TPI programming, ATtiny(4|5|9|10|20|40|102|104) -#define F_AVR8 2 // ISP programming with SPI, "classic" AVRs -#define F_XMEGA 4 // PDI programming, ATxmega family -#define F_AVR8X 8 // UPDI programming, newer 8-bit MCUs - -struct AvrIspChipArr { // Value of -1 typically means unknown - const char* name; // Name of part - uint16_t mcuid; // ID of MCU in 0..2039 - uint8_t avrarch; // F_AVR8L, F_AVR8, F_XMEGA or F_AVR8X - uint8_t sigs[3]; // Signature bytes - int32_t flashoffset; // Flash offset - int32_t flashsize; // Flash size - int16_t pagesize; // Flash page size - int8_t nboots; // Number of supported boot sectors - int16_t bootsize; // Size of (smallest) boot sector - int32_t eepromoffset; // EEPROM offset - int32_t eepromsize; // EEPROM size - int32_t eeprompagesize; // EEPROM page size - int32_t sramstart; // SRAM offset - int32_t sramsize; // SRAM size - int8_t nfuses; // Number of fuse bytes - int8_t nlocks; // Number of lock bytes - uint8_t ninterrupts; // Number of vectors in interrupt vector table -}; - -typedef struct AvrIspChipArr AvrIspChipArr; - -extern const AvrIspChipArr avr_isp_chip_arr[]; -extern const size_t avr_isp_chip_arr_size; \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c deleted file mode 100644 index bbb6d4739..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ /dev/null @@ -1,639 +0,0 @@ -#include "avr_isp_prog.h" -#include "avr_isp_prog_cmd.h" - -#include - -#define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 -#define TAG "AvrIspProg" - -struct AvrIspProgSignature { - uint8_t vendor; - uint8_t part_family; - uint8_t part_number; -}; - -typedef struct AvrIspProgSignature AvrIspProgSignature; - -struct AvrIspProgCfgDevice { - uint8_t devicecode; - uint8_t revision; - uint8_t progtype; - uint8_t parmode; - uint8_t polling; - uint8_t selftimed; - uint8_t lockbytes; - uint8_t fusebytes; - uint8_t flashpoll; - uint16_t eeprompoll; - uint16_t pagesize; - uint16_t eepromsize; - uint32_t flashsize; -}; - -typedef struct AvrIspProgCfgDevice AvrIspProgCfgDevice; - -struct AvrIspProg { - AvrIspSpiSw* spi; - AvrIspProgCfgDevice* cfg; - FuriStreamBuffer* stream_rx; - FuriStreamBuffer* stream_tx; - - uint16_t error; - uint16_t addr; - bool pmode; - bool exit; - bool rst_active_high; - uint8_t buff[AVR_ISP_PROG_TX_RX_BUF_SIZE]; - - AvrIspProgCallback callback; - void* context; -}; - -static void avr_isp_prog_end_pmode(AvrIspProg* instance); - -AvrIspProg* avr_isp_prog_init(void) { - AvrIspProg* instance = malloc(sizeof(AvrIspProg)); - instance->cfg = malloc(sizeof(AvrIspProgCfgDevice)); - instance->stream_rx = - furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); - instance->stream_tx = - furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); - instance->rst_active_high = false; - instance->exit = false; - return instance; -} - -void avr_isp_prog_free(AvrIspProg* instance) { - furi_assert(instance); - if(instance->spi) avr_isp_prog_end_pmode(instance); - furi_stream_buffer_free(instance->stream_tx); - furi_stream_buffer_free(instance->stream_rx); - free(instance->cfg); - free(instance); -} - -size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) { - return furi_stream_buffer_spaces_available(instance->stream_rx); -} - -bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len) { - furi_assert(instance); - furi_assert(data); - furi_assert(len != 0); - size_t ret = furi_stream_buffer_send(instance->stream_rx, data, sizeof(uint8_t) * len, 0); - return ret == sizeof(uint8_t) * len; -} - -size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len) { - furi_assert(instance); - return furi_stream_buffer_receive(instance->stream_tx, data, sizeof(int8_t) * max_len, 0); -} - -void avr_isp_prog_exit(AvrIspProg* instance) { - furi_assert(instance); - instance->exit = true; -} - -void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context) { - furi_assert(instance); - furi_assert(context); - instance->callback = callback; - instance->context = context; -} - -static void avr_isp_prog_tx_ch(AvrIspProg* instance, uint8_t data) { - furi_assert(instance); - furi_stream_buffer_send(instance->stream_tx, &data, sizeof(uint8_t), FuriWaitForever); -} - -static uint8_t avr_isp_prog_getch(AvrIspProg* instance) { - furi_assert(instance); - uint8_t data[1] = {0}; - while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) { - if(instance->exit) break; - }; - return data[0]; -} - -static void avr_isp_prog_fill(AvrIspProg* instance, size_t len) { - furi_assert(instance); - for(size_t x = 0; x < len; x++) { - instance->buff[x] = avr_isp_prog_getch(instance); - } -} - -static void avr_isp_prog_reset_target(AvrIspProg* instance, bool reset) { - furi_assert(instance); - avr_isp_spi_sw_res_set(instance->spi, (reset == instance->rst_active_high) ? true : false); -} - -static uint8_t avr_isp_prog_spi_transaction( - AvrIspProg* instance, - uint8_t cmd, - uint8_t addr_hi, - uint8_t addr_lo, - uint8_t data) { - furi_assert(instance); - - avr_isp_spi_sw_txrx(instance->spi, cmd); - avr_isp_spi_sw_txrx(instance->spi, addr_hi); - avr_isp_spi_sw_txrx(instance->spi, addr_lo); - return avr_isp_spi_sw_txrx(instance->spi, data); -} - -static void avr_isp_prog_empty_reply(AvrIspProg* instance) { - furi_assert(instance); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - avr_isp_prog_tx_ch(instance, STK_OK); - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } -} - -static void avr_isp_prog_breply(AvrIspProg* instance, uint8_t data) { - furi_assert(instance); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - avr_isp_prog_tx_ch(instance, data); - avr_isp_prog_tx_ch(instance, STK_OK); - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } -} - -static void avr_isp_prog_get_version(AvrIspProg* instance, uint8_t data) { - furi_assert(instance); - switch(data) { - case STK_HW_VER: - avr_isp_prog_breply(instance, AVR_ISP_HWVER); - break; - case STK_SW_MAJOR: - avr_isp_prog_breply(instance, AVR_ISP_SWMAJ); - break; - case STK_SW_MINOR: - avr_isp_prog_breply(instance, AVR_ISP_SWMIN); - break; - case AVP_ISP_CONNECT_TYPE: - avr_isp_prog_breply(instance, AVP_ISP_SERIAL_CONNECT_TYPE); - break; - default: - avr_isp_prog_breply(instance, AVR_ISP_RESP_0); - } -} - -static void avr_isp_prog_set_cfg(AvrIspProg* instance) { - furi_assert(instance); - // call this after reading cfg packet into buff[] - instance->cfg->devicecode = instance->buff[0]; - instance->cfg->revision = instance->buff[1]; - instance->cfg->progtype = instance->buff[2]; - instance->cfg->parmode = instance->buff[3]; - instance->cfg->polling = instance->buff[4]; - instance->cfg->selftimed = instance->buff[5]; - instance->cfg->lockbytes = instance->buff[6]; - instance->cfg->fusebytes = instance->buff[7]; - instance->cfg->flashpoll = instance->buff[8]; - // ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as flashpoll - instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11]; - instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13]; - instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15]; - instance->cfg->flashsize = instance->buff[16] << 24 | instance->buff[17] << 16 | - instance->buff[18] << 8 | instance->buff[19]; - - // avr devices have active low reset, at89sx are active high - instance->rst_active_high = (instance->cfg->devicecode >= 0xe0); -} -static bool - avr_isp_prog_set_pmode(AvrIspProg* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { - furi_assert(instance); - uint8_t res = 0; - avr_isp_spi_sw_txrx(instance->spi, a); - avr_isp_spi_sw_txrx(instance->spi, b); - res = avr_isp_spi_sw_txrx(instance->spi, c); - avr_isp_spi_sw_txrx(instance->spi, d); - return res == 0x53; -} - -static void avr_isp_prog_end_pmode(AvrIspProg* instance) { - furi_assert(instance); - if(instance->pmode) { - avr_isp_prog_reset_target(instance, false); - // We're about to take the target out of reset - // so configure SPI pins as input - - if(instance->spi) avr_isp_spi_sw_free(instance->spi); - instance->spi = NULL; - } - - instance->pmode = false; -} - -static bool avr_isp_prog_start_pmode(AvrIspProg* instance, AvrIspSpiSwSpeed spi_speed) { - furi_assert(instance); - // Reset target before driving PIN_SCK or PIN_MOSI - - // SPI.begin() will configure SS as output, - // so SPI master mode is selected. - // We have defined RESET as pin 10, - // which for many arduino's is not the SS pin. - // So we have to configure RESET as output here, - // (reset_target() first sets the correct level) - if(instance->spi) avr_isp_spi_sw_free(instance->spi); - instance->spi = avr_isp_spi_sw_init(spi_speed); - - avr_isp_prog_reset_target(instance, true); - // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm": - - // Pulse RESET after PIN_SCK is low: - avr_isp_spi_sw_sck_set(instance->spi, false); - - // discharge PIN_SCK, value arbitrally chosen - furi_delay_ms(20); - avr_isp_prog_reset_target(instance, false); - - // Pulse must be minimum 2 target CPU speed cycles - // so 100 usec is ok for CPU speeds above 20KHz - furi_delay_ms(1); - - avr_isp_prog_reset_target(instance, true); - - // Send the enable programming command: - // datasheet: must be > 20 msec - furi_delay_ms(50); - if(avr_isp_prog_set_pmode(instance, AVR_ISP_SET_PMODE)) { - instance->pmode = true; - return true; - } - return false; -} - -static AvrIspProgSignature avr_isp_prog_check_signature(AvrIspProg* instance) { - furi_assert(instance); - AvrIspProgSignature signature; - signature.vendor = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR); - signature.part_family = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); - signature.part_number = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); - return signature; -} - -static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { - AvrIspSpiSwSpeed spi_speed[] = { - AvrIspSpiSwSpeed1Mhz, - AvrIspSpiSwSpeed400Khz, - AvrIspSpiSwSpeed250Khz, - AvrIspSpiSwSpeed125Khz, - AvrIspSpiSwSpeed60Khz, - AvrIspSpiSwSpeed40Khz, - AvrIspSpiSwSpeed20Khz, - AvrIspSpiSwSpeed10Khz, - AvrIspSpiSwSpeed5Khz, - AvrIspSpiSwSpeed1Khz, - }; - for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { - if(avr_isp_prog_start_pmode(instance, spi_speed[i])) { - AvrIspProgSignature sig = avr_isp_prog_check_signature(instance); - AvrIspProgSignature sig_examination = avr_isp_prog_check_signature(instance); //-V656 - uint8_t y = 0; - while(y < 8) { - if(memcmp( - (uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspProgSignature)) != - 0) - break; - sig_examination = avr_isp_prog_check_signature(instance); - y++; - } - if(y == 8) { - if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { - if(i < (COUNT_OF(spi_speed) - 1)) { - avr_isp_prog_end_pmode(instance); - i++; - return avr_isp_prog_start_pmode(instance, spi_speed[i]); - } - } - return true; - } - } - } - - if(instance->spi) { - avr_isp_spi_sw_free(instance->spi); - instance->spi = NULL; - } - - return false; -} - -static void avr_isp_prog_universal(AvrIspProg* instance) { - furi_assert(instance); - uint8_t data; - - avr_isp_prog_fill(instance, 4); - data = avr_isp_prog_spi_transaction( - instance, instance->buff[0], instance->buff[1], instance->buff[2], instance->buff[3]); - avr_isp_prog_breply(instance, data); -} - -static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t data) { - furi_assert(instance); - avr_isp_prog_spi_transaction(instance, AVR_ISP_COMMIT(addr)); - /* polling flash */ - if(data == 0xFF) { - furi_delay_ms(5); - } else { - /* polling flash */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { - break; - }; - } - } -} - -static uint16_t avr_isp_prog_current_page(AvrIspProg* instance) { - furi_assert(instance); - uint16_t page = 0; - switch(instance->cfg->pagesize) { - case 32: - page = instance->addr & 0xFFFFFFF0; - break; - case 64: - page = instance->addr & 0xFFFFFFE0; - break; - case 128: - page = instance->addr & 0xFFFFFFC0; - break; - case 256: - page = instance->addr & 0xFFFFFF80; - break; - - default: - page = instance->addr; - break; - } - - return page; -} - -static uint8_t avr_isp_prog_write_flash_pages(AvrIspProg* instance, size_t length) { - furi_assert(instance); - size_t x = 0; - uint16_t page = avr_isp_prog_current_page(instance); - while(x < length) { - if(page != avr_isp_prog_current_page(instance)) { - --x; - avr_isp_prog_commit(instance, page, instance->buff[x++]); - page = avr_isp_prog_current_page(instance); - } - avr_isp_prog_spi_transaction( - instance, AVR_ISP_WRITE_FLASH_LO(instance->addr, instance->buff[x++])); - - avr_isp_prog_spi_transaction( - instance, AVR_ISP_WRITE_FLASH_HI(instance->addr, instance->buff[x++])); - instance->addr++; - } - - avr_isp_prog_commit(instance, page, instance->buff[--x]); - return STK_OK; -} - -static void avr_isp_prog_write_flash(AvrIspProg* instance, size_t length) { - furi_assert(instance); - avr_isp_prog_fill(instance, length); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - avr_isp_prog_tx_ch(instance, avr_isp_prog_write_flash_pages(instance, length)); - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } -} - -// write (length) bytes, (start) is a byte address -static uint8_t - avr_isp_prog_write_eeprom_chunk(AvrIspProg* instance, uint16_t start, uint16_t length) { - furi_assert(instance); - // this writes byte-by-byte, - // page writing may be faster (4 bytes at a time) - avr_isp_prog_fill(instance, length); - for(uint16_t x = 0; x < length; x++) { - uint16_t addr = start + x; - avr_isp_prog_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, instance->buff[x])); - furi_delay_ms(10); - } - return STK_OK; -} - -static uint8_t avr_isp_prog_write_eeprom(AvrIspProg* instance, size_t length) { - furi_assert(instance); - // here is a word address, get the byte address - uint16_t start = instance->addr * 2; - uint16_t remaining = length; - if(length > instance->cfg->eepromsize) { - instance->error++; - return STK_FAILED; - } - while(remaining > AVR_ISP_EECHUNK) { - avr_isp_prog_write_eeprom_chunk(instance, start, AVR_ISP_EECHUNK); - start += AVR_ISP_EECHUNK; - remaining -= AVR_ISP_EECHUNK; - } - avr_isp_prog_write_eeprom_chunk(instance, start, remaining); - return STK_OK; -} - -static void avr_isp_prog_program_page(AvrIspProg* instance) { - furi_assert(instance); - uint8_t result = STK_FAILED; - uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); - uint8_t memtype = avr_isp_prog_getch(instance); - // flash memory @addr, (length) bytes - if(memtype == STK_SET_FLASH_TYPE) { - avr_isp_prog_write_flash(instance, length); - return; - } - if(memtype == STK_SET_EEPROM_TYPE) { - result = avr_isp_prog_write_eeprom(instance, length); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - avr_isp_prog_tx_ch(instance, result); - - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } - return; - } - avr_isp_prog_tx_ch(instance, STK_FAILED); - return; -} - -static uint8_t avr_isp_prog_flash_read_page(AvrIspProg* instance, uint16_t length) { - furi_assert(instance); - for(uint16_t x = 0; x < length; x += 2) { - avr_isp_prog_tx_ch( - instance, - avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(instance->addr))); - avr_isp_prog_tx_ch( - instance, - avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(instance->addr))); - instance->addr++; - } - return STK_OK; -} - -static uint8_t avr_isp_prog_eeprom_read_page(AvrIspProg* instance, uint16_t length) { - furi_assert(instance); - // here again we have a word address - uint16_t start = instance->addr * 2; - for(uint16_t x = 0; x < length; x++) { - uint16_t addr = start + x; - avr_isp_prog_tx_ch( - instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr))); - } - return STK_OK; -} - -static void avr_isp_prog_read_page(AvrIspProg* instance) { - furi_assert(instance); - uint8_t result = STK_FAILED; - uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); - uint8_t memtype = avr_isp_prog_getch(instance); - if(avr_isp_prog_getch(instance) != CRC_EOP) { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - return; - } - avr_isp_prog_tx_ch(instance, STK_INSYNC); - if(memtype == STK_SET_FLASH_TYPE) result = avr_isp_prog_flash_read_page(instance, length); - if(memtype == STK_SET_EEPROM_TYPE) result = avr_isp_prog_eeprom_read_page(instance, length); - avr_isp_prog_tx_ch(instance, result); -} - -static void avr_isp_prog_read_signature(AvrIspProg* instance) { - furi_assert(instance); - if(avr_isp_prog_getch(instance) != CRC_EOP) { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - return; - } - avr_isp_prog_tx_ch(instance, STK_INSYNC); - - avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR)); - avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY)); - avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER)); - - avr_isp_prog_tx_ch(instance, STK_OK); -} - -void avr_isp_prog_avrisp(AvrIspProg* instance) { - furi_assert(instance); - uint8_t ch = avr_isp_prog_getch(instance); - - switch(ch) { - case STK_GET_SYNC: - FURI_LOG_D(TAG, "cmd STK_GET_SYNC"); - instance->error = 0; - avr_isp_prog_empty_reply(instance); - break; - case STK_GET_SIGN_ON: - FURI_LOG_D(TAG, "cmd STK_GET_SIGN_ON"); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - - avr_isp_prog_tx_ch(instance, 'A'); - avr_isp_prog_tx_ch(instance, 'V'); - avr_isp_prog_tx_ch(instance, 'R'); - avr_isp_prog_tx_ch(instance, ' '); - avr_isp_prog_tx_ch(instance, 'I'); - avr_isp_prog_tx_ch(instance, 'S'); - avr_isp_prog_tx_ch(instance, 'P'); - - avr_isp_prog_tx_ch(instance, STK_OK); - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } - break; - case STK_GET_PARAMETER: - FURI_LOG_D(TAG, "cmd STK_GET_PARAMETER"); - avr_isp_prog_get_version(instance, avr_isp_prog_getch(instance)); - break; - case STK_SET_DEVICE: - FURI_LOG_D(TAG, "cmd STK_SET_DEVICE"); - avr_isp_prog_fill(instance, 20); - avr_isp_prog_set_cfg(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_SET_DEVICE_EXT: // ignore for now - FURI_LOG_D(TAG, "cmd STK_SET_DEVICE_EXT"); - avr_isp_prog_fill(instance, 5); - avr_isp_prog_empty_reply(instance); - break; - case STK_ENTER_PROGMODE: - FURI_LOG_D(TAG, "cmd STK_ENTER_PROGMODE"); - if(!instance->pmode) avr_isp_prog_auto_set_spi_speed_start_pmode(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_LOAD_ADDRESS: - FURI_LOG_D(TAG, "cmd STK_LOAD_ADDRESS"); - instance->addr = avr_isp_prog_getch(instance) | avr_isp_prog_getch(instance) << 8; - avr_isp_prog_empty_reply(instance); - break; - case STK_PROG_FLASH: // ignore for now - FURI_LOG_D(TAG, "cmd STK_PROG_FLASH"); - avr_isp_prog_getch(instance); - avr_isp_prog_getch(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_PROG_DATA: // ignore for now - FURI_LOG_D(TAG, "cmd STK_PROG_DATA"); - avr_isp_prog_getch(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_PROG_PAGE: - FURI_LOG_D(TAG, "cmd STK_PROG_PAGE"); - avr_isp_prog_program_page(instance); - break; - case STK_READ_PAGE: - FURI_LOG_D(TAG, "cmd STK_READ_PAGE"); - avr_isp_prog_read_page(instance); - break; - case STK_UNIVERSAL: - FURI_LOG_D(TAG, "cmd STK_UNIVERSAL"); - avr_isp_prog_universal(instance); - break; - case STK_LEAVE_PROGMODE: - FURI_LOG_D(TAG, "cmd STK_LEAVE_PROGMODE"); - instance->error = 0; - if(instance->pmode) avr_isp_prog_end_pmode(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_READ_SIGN: - FURI_LOG_D(TAG, "cmd STK_READ_SIGN"); - avr_isp_prog_read_signature(instance); - break; - // expecting a command, not CRC_EOP - // this is how we can get back in sync - case CRC_EOP: - FURI_LOG_D(TAG, "cmd CRC_EOP"); - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - break; - // anything else we will return STK_UNKNOWN - default: - FURI_LOG_D(TAG, "cmd STK_ERROR_CMD"); - instance->error++; - if(avr_isp_prog_getch(instance) == CRC_EOP) - avr_isp_prog_tx_ch(instance, STK_UNKNOWN); - else - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } - - if(instance->callback) { - instance->callback(instance->context); - } -} diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h deleted file mode 100644 index 2c15ab066..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "avr_isp_spi_sw.h" -#include - -typedef struct AvrIspProg AvrIspProg; -typedef void (*AvrIspProgCallback)(void* context); - -AvrIspProg* avr_isp_prog_init(void); -void avr_isp_prog_free(AvrIspProg* instance); -size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) ; -bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len); -size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len); -void avr_isp_prog_avrisp(AvrIspProg* instance); -void avr_isp_prog_exit(AvrIspProg* instance); -void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context); diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h deleted file mode 100644 index f8b07203e..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once - -// http://ww1.microchip.com/downloads/en/appnotes/atmel-0943-in-system-programming_applicationnote_avr910.pdf -// AVR ISP Definitions -#define AVR_ISP_HWVER 0X02 -#define AVR_ISP_SWMAJ 0X01 -#define AVR_ISP_SWMIN 0X12 -#define AVP_ISP_SERIAL_CONNECT_TYPE 0X53 -#define AVP_ISP_CONNECT_TYPE 0x93 -#define AVR_ISP_RESP_0 0X00 - -#define AVR_ISP_SET_PMODE 0xAC, 0x53, 0x00, 0x00 -#define AVR_ISP_READ_VENDOR 0x30, 0x00, 0x00, 0x00 -#define AVR_ISP_READ_PART_FAMILY 0x30, 0x00, 0x01, 0x00 -#define AVR_ISP_READ_PART_NUMBER 0x30, 0x00, 0x02, 0x00 -#define AVR_ISP_ERASE_CHIP \ - 0xAC, 0x80, 0x00, 0x00 //Erase Chip, Wait N ms, Release RESET to end the erase. -//The only way to end a Chip Erase cycle is by temporarily releasing the Reset line - -#define AVR_ISP_EXTENDED_ADDR(data) 0x4D, 0x00, data, 0x00 -#define AVR_ISP_WRITE_FLASH_LO(add, data) 0x40, (add >> 8) & 0xFF, add & 0xFF, data -#define AVR_ISP_WRITE_FLASH_HI(add, data) 0x48, (add >> 8) & 0xFF, add & 0xFF, data -#define AVR_ISP_READ_FLASH_LO(add) 0x20, (add >> 8) & 0xFF, add & 0xFF, 0x00 -#define AVR_ISP_READ_FLASH_HI(add) 0x28, (add >> 8) & 0xFF, add & 0xFF, 0x00 - -#define AVR_ISP_WRITE_EEPROM(add, data) \ - 0xC0, (add >> 8) & 0xFF, add & 0xFF, data //Send cmd, Wait N ms -#define AVR_ISP_READ_EEPROM(add) 0xA0, (add >> 8) & 0xFF, add & 0xFF, 0xFF - -#define AVR_ISP_COMMIT(add) \ - 0x4C, (add >> 8) & 0xFF, add & 0xFF, 0x00 //Send cmd, polling read last addr page - -#define AVR_ISP_OSCCAL(add) 0x38, 0x00, add, 0x00 - -#define AVR_ISP_WRITE_LOCK_BYTE(data) 0xAC, 0xE0, 0x00, data //Send cmd, Wait N ms -#define AVR_ISP_READ_LOCK_BYTE 0x58, 0x00, 0x00, 0x00 -#define AVR_ISP_WRITE_FUSE_LOW(data) 0xAC, 0xA0, 0x00, data //Send cmd, Wait N ms -#define AVR_ISP_READ_FUSE_LOW 0x50, 0x00, 0x00, 0x00 -#define AVR_ISP_WRITE_FUSE_HIGH(data) 0xAC, 0xA8, 0x00, data //Send cmd, Wait N ms -#define AVR_ISP_READ_FUSE_HIGH 0x58, 0x08, 0x00, 0x00 -#define AVR_ISP_WRITE_FUSE_EXTENDED(data) 0xAC, 0xA4, 0x00, data //Send cmd, Wait N ms (~write) -#define AVR_ISP_READ_FUSE_EXTENDED 0x50, 0x08, 0x00, 0x00 - -#define AVR_ISP_EECHUNK 0x20 - -// https://www.microchip.com/content/dam/mchp/documents/OTH/ApplicationNotes/ApplicationNotes/doc2525.pdf -// STK Definitions -#define STK_OK 0x10 -#define STK_FAILED 0x11 -#define STK_UNKNOWN 0x12 -#define STK_INSYNC 0x14 -#define STK_NOSYNC 0x15 -#define CRC_EOP 0x20 - -#define STK_GET_SYNC 0x30 -#define STK_GET_SIGN_ON 0x31 -#define STK_SET_PARAMETER 0x40 -#define STK_GET_PARAMETER 0x41 -#define STK_SET_DEVICE 0x42 -#define STK_SET_DEVICE_EXT 0x45 -#define STK_ENTER_PROGMODE 0x50 -#define STK_LEAVE_PROGMODE 0x51 -#define STK_CHIP_ERASE 0x52 -#define STK_CHECK_AUTOINC 0x53 -#define STK_LOAD_ADDRESS 0x55 -#define STK_UNIVERSAL 0x56 -#define STK_UNIVERSAL_MULTI 0x57 -#define STK_PROG_FLASH 0x60 -#define STK_PROG_DATA 0x61 -#define STK_PROG_FUSE 0x62 -#define STK_PROG_FUSE_EXT 0x65 -#define STK_PROG_LOCK 0x63 -#define STK_PROG_PAGE 0x64 -#define STK_READ_FLASH 0x70 -#define STK_READ_DATA 0x71 -#define STK_READ_FUSE 0x72 -#define STK_READ_LOCK 0x73 -#define STK_READ_PAGE 0x74 -#define STK_READ_SIGN 0x75 -#define STK_READ_OSCCAL 0x76 -#define STK_READ_FUSE_EXT 0x77 -#define STK_READ_OSCCAL_EXT 0x78 -#define STK_HW_VER 0x80 -#define STK_SW_MAJOR 0x81 -#define STK_SW_MINOR 0x82 -#define STK_LEDS 0x83 -#define STK_VTARGET 0x84 -#define STK_VADJUST 0x85 -#define STK_OSC_PSCALE 0x86 -#define STK_OSC_CMATCH 0x87 -#define STK_SCK_DURATION 0x89 -#define STK_BUFSIZEL 0x90 -#define STK_BUFSIZEH 0x91 -#define STK_STK500_TOPCARD_DETECT 0x98 - -#define STK_SET_EEPROM_TYPE 0X45 -#define STK_SET_FLASH_TYPE 0X46 diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c deleted file mode 100644 index c6d9d54c8..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "avr_isp_spi_sw.h" - -#include - -#define AVR_ISP_SPI_SW_MISO &gpio_ext_pa6 -#define AVR_ISP_SPI_SW_MOSI &gpio_ext_pa7 -#define AVR_ISP_SPI_SW_SCK &gpio_ext_pb3 -#define AVR_ISP_RESET &gpio_ext_pb2 - -struct AvrIspSpiSw { - AvrIspSpiSwSpeed speed_wait_time; - const GpioPin* miso; - const GpioPin* mosi; - const GpioPin* sck; - const GpioPin* res; -}; - -AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed) { - AvrIspSpiSw* instance = malloc(sizeof(AvrIspSpiSw)); - instance->speed_wait_time = speed; - instance->miso = AVR_ISP_SPI_SW_MISO; - instance->mosi = AVR_ISP_SPI_SW_MOSI; - instance->sck = AVR_ISP_SPI_SW_SCK; - instance->res = AVR_ISP_RESET; - - furi_hal_gpio_init(instance->miso, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(instance->mosi, false); - furi_hal_gpio_init(instance->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(instance->sck, false); - furi_hal_gpio_init(instance->sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(instance->res, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - - return instance; -} - -void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { - furi_assert(instance); - furi_hal_gpio_init(instance->res, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(instance->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(instance->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(instance->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - free(instance); -} - -uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data) { - furi_assert(instance); - for(uint8_t i = 0; i < 8; ++i) { - furi_hal_gpio_write(instance->mosi, (data & 0x80) ? true : false); - - furi_hal_gpio_write(instance->sck, true); - if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) - furi_delay_us(instance->speed_wait_time - 1); - - data = (data << 1) | furi_hal_gpio_read(instance->miso); //-V792 - - furi_hal_gpio_write(instance->sck, false); - if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) - furi_delay_us(instance->speed_wait_time - 1); - } - return data; -} - -void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state) { - furi_assert(instance); - furi_hal_gpio_write(instance->res, state); -} - -void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state) { - furi_assert(instance); - furi_hal_gpio_write(instance->sck, state); -} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h deleted file mode 100644 index 44de5ff79..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -typedef enum { - AvrIspSpiSwSpeed1Mhz = 0, - AvrIspSpiSwSpeed400Khz = 1, - AvrIspSpiSwSpeed250Khz = 2, - AvrIspSpiSwSpeed125Khz = 4, - AvrIspSpiSwSpeed60Khz = 8, - AvrIspSpiSwSpeed40Khz = 12, - AvrIspSpiSwSpeed20Khz = 24, - AvrIspSpiSwSpeed10Khz = 48, - AvrIspSpiSwSpeed5Khz = 96, - AvrIspSpiSwSpeed1Khz = 480, -} AvrIspSpiSwSpeed; - -typedef struct AvrIspSpiSw AvrIspSpiSw; - -AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed); -void avr_isp_spi_sw_free(AvrIspSpiSw* instance); -uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data); -void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state); -void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/clock.png b/applications/external/avr_isp_programmer/lib/driver/clock.png deleted file mode 100644 index 93a59fe681d26795d0db342a0c55ba42fc2c7529..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3649 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 LTALIbyM_M?u*zuP diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c deleted file mode 100644 index 4af125aee..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "../avr_isp_app_i.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const avr_isp_scene_on_enter_handlers[])(void*) = { -#include "avr_isp_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const avr_isp_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "avr_isp_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const avr_isp_scene_on_exit_handlers[])(void* context) = { -#include "avr_isp_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers avr_isp_scene_handlers = { - .on_enter_handlers = avr_isp_scene_on_enter_handlers, - .on_event_handlers = avr_isp_scene_on_event_handlers, - .on_exit_handlers = avr_isp_scene_on_exit_handlers, - .scene_num = AvrIspSceneNum, -}; diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h deleted file mode 100644 index 658ee7432..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) AvrIspScene##id, -typedef enum { -#include "avr_isp_scene_config.h" - AvrIspSceneNum, -} AvrIspScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers avr_isp_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "avr_isp_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "avr_isp_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "avr_isp_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c deleted file mode 100644 index e5f530fec..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c +++ /dev/null @@ -1,99 +0,0 @@ -#include "../avr_isp_app_i.h" -#include "../helpers/avr_isp_types.h" - -void avr_isp_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -void avr_isp_scene_about_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - FuriString* temp_str = furi_string_alloc(); - furi_string_printf(temp_str, "\e#%s\n", "Information"); - - furi_string_cat_printf(temp_str, "Version: %s\n", AVR_ISP_VERSION_APP); - furi_string_cat_printf(temp_str, "Developed by: %s\n", AVR_ISP_DEVELOPED); - furi_string_cat_printf(temp_str, "Github: %s\n\n", AVR_ISP_GITHUB); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); - furi_string_cat_printf( - temp_str, - "This application is an AVR in-system programmer based on stk500mk1. It is compatible with AVR-based" - " microcontrollers including Arduino. You can also use it to repair the chip if you accidentally" - " corrupt the bootloader.\n\n"); - - furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); - furi_string_cat_printf(temp_str, "- Create a dump of your chip on an SD card\n"); - furi_string_cat_printf(temp_str, "- Flash your chip firmware from the SD card\n"); - furi_string_cat_printf(temp_str, "- Act as a wired USB ISP using avrdude software\n\n"); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Supported chip series:"); - furi_string_cat_printf( - temp_str, - "Example command for avrdude flashing: avrdude.exe -p m328p -c stk500v1 -P COMxx -U flash:r:" - "X:\\sketch_sample.hex" - ":i\n"); - furi_string_cat_printf( - temp_str, - "Where: " - "-p m328p" - " brand of your chip, " - "-P COMxx" - " com port number in the system when " - "ISP Programmer" - " is enabled\n\n"); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Info"); - furi_string_cat_printf( - temp_str, - "ATtinyXXXX\nATmegaXXXX\nAT43Uxxx\nAT76C711\nAT86RF401\nAT90xxxxx\nAT94K\n" - "ATAxxxxx\nATA664251\nM3000\nLGT8F88P\nLGT8F168P\nLGT8F328P\n"); - - furi_string_cat_printf( - temp_str, "For a more detailed list of supported chips, see AVRDude help\n"); - - widget_add_text_box_element( - app->widget, - 0, - 0, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! \e!\n", - false); - widget_add_text_box_element( - app->widget, - 0, - 2, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! ISP Programmer \e!\n", - false); - widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); - furi_string_free(temp_str); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); -} - -bool avr_isp_scene_about_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void avr_isp_scene_about_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - // Clear views - widget_reset(app->widget); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c deleted file mode 100644 index 79c239390..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_chip_detect_callback(AvrIspCustomEvent event, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void avr_isp_scene_chip_detect_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - switch(app->error) { - case AvrIspErrorReading: - case AvrIspErrorWriting: - case AvrIspErrorWritingFuse: - avr_isp_chip_detect_set_state( - app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorOccured); - break; - case AvrIspErrorVerification: - avr_isp_chip_detect_set_state( - app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorVerification); - break; - - default: - avr_isp_chip_detect_set_state( - app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateNoDetect); - break; - } - app->error = AvrIspErrorNoError; - avr_isp_chip_detect_view_set_callback( - app->avr_isp_chip_detect_view, avr_isp_scene_chip_detect_callback, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewChipDetect); -} - -bool avr_isp_scene_chip_detect_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case AvrIspCustomEventSceneChipDetectOk: - - if(scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == - AvrIspViewProgrammer) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneProgrammer); - } else if( - scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == - AvrIspViewReader) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneInputName); - } else if( - scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == - AvrIspViewWriter) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneLoad); - } - - consumed = true; - break; - default: - break; - } - } else if(event.type == SceneManagerEventTypeTick) { - } - return consumed; -} - -void avr_isp_scene_chip_detect_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h deleted file mode 100644 index 6f22511db..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h +++ /dev/null @@ -1,10 +0,0 @@ -ADD_SCENE(avr_isp, start, Start) -ADD_SCENE(avr_isp, about, About) -ADD_SCENE(avr_isp, programmer, Programmer) -ADD_SCENE(avr_isp, reader, Reader) -ADD_SCENE(avr_isp, input_name, InputName) -ADD_SCENE(avr_isp, load, Load) -ADD_SCENE(avr_isp, writer, Writer) -ADD_SCENE(avr_isp, wiring, Wiring) -ADD_SCENE(avr_isp, chip_detect, ChipDetect) -ADD_SCENE(avr_isp, success, Success) diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c deleted file mode 100644 index 3394f4362..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c +++ /dev/null @@ -1,89 +0,0 @@ -#include "../avr_isp_app_i.h" -#include - -#define MAX_TEXT_INPUT_LEN 22 - -void avr_isp_scene_input_name_text_callback(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneInputName); -} - -void avr_isp_scene_input_name_get_timefilename(FuriString* name) { - FuriHalRtcDateTime datetime = {0}; - furi_hal_rtc_get_datetime(&datetime); - furi_string_printf( - name, - "AVR_dump-%.4d%.2d%.2d-%.2d%.2d%.2d", - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second); -} - -void avr_isp_scene_input_name_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - // Setup view - TextInput* text_input = app->text_input; - bool dev_name_empty = false; - - FuriString* file_name = furi_string_alloc(); - - avr_isp_scene_input_name_get_timefilename(file_name); - furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); - //highlighting the entire filename by default - dev_name_empty = true; - - strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME); - text_input_set_header_text(text_input, "Name dump"); - text_input_set_result_callback( - text_input, - avr_isp_scene_input_name_text_callback, - app, - app->file_name_tmp, - MAX_TEXT_INPUT_LEN, // buffer size - dev_name_empty); - - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(STORAGE_APP_DATA_PATH_PREFIX, AVR_ISP_APP_EXTENSION, ""); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - furi_string_free(file_name); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewTextInput); -} - -bool avr_isp_scene_input_name_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - if(event.type == SceneManagerEventTypeBack) { - scene_manager_previous_scene(app->scene_manager); - return true; - } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == AvrIspCustomEventSceneInputName) { - if(strcmp(app->file_name_tmp, "") != 0) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneReader); - } else { - } - } - } - return false; -} - -void avr_isp_scene_input_name_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - // Clear validator - void* validator_context = text_input_get_validator_callback_context(app->text_input); - text_input_set_validator(app->text_input, NULL, NULL); - validator_is_file_free(validator_context); - // Clear view - text_input_reset(app->text_input); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c deleted file mode 100644 index e8890e373..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_load_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - if(avr_isp_load_from_file(app)) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneWriter); - } else { - scene_manager_previous_scene(app->scene_manager); - } -} - -bool avr_isp_scene_load_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void avr_isp_scene_load_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c deleted file mode 100644 index 0915e1e8a..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_programmer_callback(AvrIspCustomEvent event, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void avr_isp_scene_programmer_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - avr_isp_programmer_view_set_callback( - app->avr_isp_programmer_view, avr_isp_scene_programmer_callback, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewProgrammer); -} - -bool avr_isp_scene_programmer_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void avr_isp_scene_programmer_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c deleted file mode 100644 index 8dcb47597..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_reader_callback(AvrIspCustomEvent event, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void avr_isp_scene_reader_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - avr_isp_reader_set_file_path( - app->avr_isp_reader_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); - avr_isp_reader_view_set_callback(app->avr_isp_reader_view, avr_isp_scene_reader_callback, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewReader); -} - -bool avr_isp_scene_reader_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - UNUSED(app); - bool consumed = false; - if(event.type == SceneManagerEventTypeBack) { - //do not handle exit on "Back" - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case AvrIspCustomEventSceneReadingOk: - scene_manager_next_scene(app->scene_manager, AvrIspSceneSuccess); - consumed = true; - break; - case AvrIspCustomEventSceneExit: - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorVerification: - app->error = AvrIspErrorVerification; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorReading: - app->error = AvrIspErrorReading; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - default: - break; - } - } else if(event.type == SceneManagerEventTypeTick) { - avr_isp_reader_update_progress(app->avr_isp_reader_view); - } - return consumed; -} - -void avr_isp_scene_reader_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c deleted file mode 100644 index b00bfefce..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_start_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - AvrIspApp* app = context; - - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void avr_isp_scene_start_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - Submenu* submenu = app->submenu; - submenu_add_item( - submenu, "Dump AVR", SubmenuIndexAvrIspReader, avr_isp_scene_start_submenu_callback, app); - submenu_add_item( - submenu, "Flash AVR", SubmenuIndexAvrIspWriter, avr_isp_scene_start_submenu_callback, app); - submenu_add_item( - submenu, - "ISP Programmer", - SubmenuIndexAvrIspProgrammer, - avr_isp_scene_start_submenu_callback, - app); - submenu_add_item( - submenu, "Wiring", SubmenuIndexAvrIsWiring, avr_isp_scene_start_submenu_callback, app); - submenu_add_item( - submenu, "About", SubmenuIndexAvrIspAbout, avr_isp_scene_start_submenu_callback, app); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, AvrIspSceneStart)); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewSubmenu); -} - -bool avr_isp_scene_start_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexAvrIspAbout) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneAbout); - consumed = true; - } else if(event.event == SubmenuIndexAvrIspProgrammer) { - scene_manager_set_scene_state( - app->scene_manager, AvrIspSceneChipDetect, AvrIspViewProgrammer); - scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - } else if(event.event == SubmenuIndexAvrIspReader) { - scene_manager_set_scene_state( - app->scene_manager, AvrIspSceneChipDetect, AvrIspViewReader); - scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - } else if(event.event == SubmenuIndexAvrIspWriter) { - scene_manager_set_scene_state( - app->scene_manager, AvrIspSceneChipDetect, AvrIspViewWriter); - scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - } else if(event.event == SubmenuIndexAvrIsWiring) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneWiring); - consumed = true; - } - scene_manager_set_scene_state(app->scene_manager, AvrIspSceneStart, event.event); - } - - return consumed; -} - -void avr_isp_scene_start_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c deleted file mode 100644 index a88ed28aa..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_success_popup_callback(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneSuccess); -} - -void avr_isp_scene_success_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - Popup* popup = app->popup; - popup_set_icon(popup, 32, 5, &I_dolphin_nice_96x59); - popup_set_header(popup, "Success!", 8, 22, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, app); - popup_set_callback(popup, avr_isp_scene_success_popup_callback); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewPopup); -} - -bool avr_isp_scene_success_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == AvrIspCustomEventSceneSuccess) { - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneStart); - return true; - } - } - return false; -} - -void avr_isp_scene_success_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - Popup* popup = app->popup; - popup_reset(popup); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c deleted file mode 100644 index 787ed5673..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_wiring_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - widget_add_icon_element(app->widget, 0, 0, &I_avr_wiring); - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); -} - -bool avr_isp_scene_wiring_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} -void avr_isp_scene_wiring_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c deleted file mode 100644 index 39c944fd5..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_writer_callback(AvrIspCustomEvent event, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void avr_isp_scene_writer_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - avr_isp_writer_set_file_path( - app->avr_isp_writer_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); - avr_isp_writer_view_set_callback(app->avr_isp_writer_view, avr_isp_scene_writer_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWriter); -} - -bool avr_isp_scene_writer_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeBack) { - //do not handle exit on "Back" - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case AvrIspCustomEventSceneExitStartMenu: - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneStart); - consumed = true; - break; - case AvrIspCustomEventSceneExit: - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorVerification: - app->error = AvrIspErrorVerification; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorWriting: - app->error = AvrIspErrorWriting; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorWritingFuse: - app->error = AvrIspErrorWritingFuse; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - default: - break; - } - } else if(event.type == SceneManagerEventTypeTick) { - avr_isp_writer_update_progress(app->avr_isp_writer_view); - } - return consumed; -} - -void avr_isp_scene_writer_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c deleted file mode 100644 index fdcf71c36..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c +++ /dev/null @@ -1,213 +0,0 @@ -#include "avr_isp_view_chip_detect.h" -#include -#include - -#include "../helpers/avr_isp_worker_rw.h" - -struct AvrIspChipDetectView { - View* view; - AvrIspWorkerRW* avr_isp_worker_rw; - AvrIspChipDetectViewCallback callback; - void* context; -}; - -typedef struct { - uint16_t idx; - const char* name_chip; - uint32_t flash_size; - AvrIspChipDetectViewState state; -} AvrIspChipDetectViewModel; - -void avr_isp_chip_detect_view_set_callback( - AvrIspChipDetectView* instance, - AvrIspChipDetectViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state) { - furi_assert(instance); - - with_view_model( - instance->view, AvrIspChipDetectViewModel * model, { model->state = state; }, true); -} - -void avr_isp_chip_detect_view_draw(Canvas* canvas, AvrIspChipDetectViewModel* model) { - canvas_clear(canvas); - - char str_buf[64] = {0}; - canvas_set_font(canvas, FontPrimary); - - switch(model->state) { - case AvrIspChipDetectViewStateDetected: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip detected!"); - canvas_draw_icon(canvas, 29, 14, &I_chip_long_70x22); - canvas_set_font(canvas, FontSecondary); - snprintf(str_buf, sizeof(str_buf), "%ld Kb", model->flash_size / 1024); - canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignCenter, str_buf); - canvas_draw_str_aligned(canvas, 64, 45, AlignCenter, AlignCenter, model->name_chip); - elements_button_right(canvas, "Next"); - break; - case AvrIspChipDetectViewStateErrorOccured: - canvas_draw_str_aligned( - canvas, 64, 5, AlignCenter, AlignCenter, "Error occured, try again!"); - canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignCenter, "Check the wiring and retry"); - break; - case AvrIspChipDetectViewStateErrorVerification: - canvas_draw_str_aligned( - canvas, 64, 5, AlignCenter, AlignCenter, "Data verification failed"); - canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignCenter, "Try to restart the process"); - break; - - default: - //AvrIspChipDetectViewStateNoDetect - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip not found!"); - canvas_draw_icon(canvas, 29, 12, &I_chif_not_found_83x37); - - break; - } - canvas_set_font(canvas, FontSecondary); - elements_button_left(canvas, "Retry"); -} - -bool avr_isp_chip_detect_view_input(InputEvent* event, void* context) { - furi_assert(context); - - AvrIspChipDetectView* instance = context; - - if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - return false; - } else if(event->key == InputKeyRight) { - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { - if(model->state == AvrIspChipDetectViewStateDetected) { - if(instance->callback) - instance->callback( - AvrIspCustomEventSceneChipDetectOk, instance->context); - } - }, - false); - - } else if(event->key == InputKeyLeft) { - bool detect_chip = false; - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { - if(model->state != AvrIspChipDetectViewStateDetecting) { - model->state = AvrIspChipDetectViewStateDetecting; - detect_chip = true; - } - }, - false); - if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); - } - } else { - return false; - } - - return true; -} - -static void avr_isp_chip_detect_detect_chip_callback( - void* context, - const char* name, - bool detect_chip, - uint32_t flash_size) { - furi_assert(context); - - AvrIspChipDetectView* instance = context; - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { - model->name_chip = name; - model->flash_size = flash_size; - if(detect_chip) { - model->state = AvrIspChipDetectViewStateDetected; - } else { - model->state = AvrIspChipDetectViewStateNoDetect; - } - }, - true); -} -void avr_isp_chip_detect_view_enter(void* context) { - furi_assert(context); - - AvrIspChipDetectView* instance = context; - bool detect_chip = false; - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { - if(model->state == AvrIspChipDetectViewStateNoDetect || - model->state == AvrIspChipDetectViewStateDetected) { - detect_chip = true; - } - }, - false); - - //Start avr_isp_worker_rw - instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); - - avr_isp_worker_rw_set_callback( - instance->avr_isp_worker_rw, avr_isp_chip_detect_detect_chip_callback, instance); - - if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); -} - -void avr_isp_chip_detect_view_exit(void* context) { - furi_assert(context); - - AvrIspChipDetectView* instance = context; - - avr_isp_worker_rw_set_callback(instance->avr_isp_worker_rw, NULL, NULL); - avr_isp_worker_rw_free(instance->avr_isp_worker_rw); -} - -AvrIspChipDetectView* avr_isp_chip_detect_view_alloc() { - AvrIspChipDetectView* instance = malloc(sizeof(AvrIspChipDetectView)); - - // View allocation and configuration - instance->view = view_alloc(); - - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspChipDetectViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_chip_detect_view_draw); - view_set_input_callback(instance->view, avr_isp_chip_detect_view_input); - view_set_enter_callback(instance->view, avr_isp_chip_detect_view_enter); - view_set_exit_callback(instance->view, avr_isp_chip_detect_view_exit); - - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { model->state = AvrIspChipDetectViewStateNoDetect; }, - false); - return instance; -} - -void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h deleted file mode 100644 index 37f2ae233..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include "../helpers/avr_isp_types.h" -#include "../helpers/avr_isp_event.h" - -typedef struct AvrIspChipDetectView AvrIspChipDetectView; - -typedef void (*AvrIspChipDetectViewCallback)(AvrIspCustomEvent event, void* context); - -typedef enum { - AvrIspChipDetectViewStateNoDetect, - AvrIspChipDetectViewStateDetecting, - AvrIspChipDetectViewStateDetected, - AvrIspChipDetectViewStateErrorOccured, - AvrIspChipDetectViewStateErrorVerification, -} AvrIspChipDetectViewState; - -void avr_isp_chip_detect_view_set_callback( - AvrIspChipDetectView* instance, - AvrIspChipDetectViewCallback callback, - void* context); - -void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state); - -AvrIspChipDetectView* avr_isp_chip_detect_view_alloc(); - -void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance); - -View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance); - -void avr_isp_chip_detect_view_exit(void* context); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c deleted file mode 100644 index 34e18770b..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c +++ /dev/null @@ -1,134 +0,0 @@ -#include "avr_isp_view_programmer.h" -#include - -#include "../helpers/avr_isp_worker.h" -#include - -struct AvrIspProgrammerView { - View* view; - AvrIspWorker* worker; - AvrIspProgrammerViewCallback callback; - void* context; -}; - -typedef struct { - AvrIspProgrammerViewStatus status; -} AvrIspProgrammerViewModel; - -void avr_isp_programmer_view_set_callback( - AvrIspProgrammerView* instance, - AvrIspProgrammerViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_programmer_view_draw(Canvas* canvas, AvrIspProgrammerViewModel* model) { - canvas_clear(canvas); - - if(model->status == AvrIspProgrammerViewStatusUSBConnect) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_icon(canvas, 0, 0, &I_isp_active_128x53); - elements_multiline_text(canvas, 45, 10, "ISP mode active"); - } else { - canvas_set_font(canvas, FontSecondary); - canvas_draw_icon(canvas, 51, 6, &I_link_waiting_77x56); - elements_multiline_text(canvas, 0, 25, "Waiting for\nsoftware\nconnection"); - } -} - -bool avr_isp_programmer_view_input(InputEvent* event, void* context) { - furi_assert(context); - UNUSED(context); - - if(event->key == InputKeyBack || event->type != InputTypeShort) { - return false; - } - - return true; -} - -static void avr_isp_programmer_usb_connect_callback(void* context, bool status_connect) { - furi_assert(context); - AvrIspProgrammerView* instance = context; - - with_view_model( - instance->view, - AvrIspProgrammerViewModel * model, - { - if(status_connect) { - model->status = AvrIspProgrammerViewStatusUSBConnect; - } else { - model->status = AvrIspProgrammerViewStatusNoUSBConnect; - } - }, - true); -} - -void avr_isp_programmer_view_enter(void* context) { - furi_assert(context); - - AvrIspProgrammerView* instance = context; - with_view_model( - instance->view, - AvrIspProgrammerViewModel * model, - { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, - true); - - //Start worker - instance->worker = avr_isp_worker_alloc(instance->context); - - avr_isp_worker_set_callback( - instance->worker, avr_isp_programmer_usb_connect_callback, instance); - - avr_isp_worker_start(instance->worker); -} - -void avr_isp_programmer_view_exit(void* context) { - furi_assert(context); - - AvrIspProgrammerView* instance = context; - //Stop worker - if(avr_isp_worker_is_running(instance->worker)) { - avr_isp_worker_stop(instance->worker); - } - avr_isp_worker_set_callback(instance->worker, NULL, NULL); - avr_isp_worker_free(instance->worker); -} - -AvrIspProgrammerView* avr_isp_programmer_view_alloc() { - AvrIspProgrammerView* instance = malloc(sizeof(AvrIspProgrammerView)); - - // View allocation and configuration - instance->view = view_alloc(); - - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspProgrammerViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_programmer_view_draw); - view_set_input_callback(instance->view, avr_isp_programmer_view_input); - view_set_enter_callback(instance->view, avr_isp_programmer_view_enter); - view_set_exit_callback(instance->view, avr_isp_programmer_view_exit); - - with_view_model( - instance->view, - AvrIspProgrammerViewModel * model, - { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, - false); - return instance; -} - -void avr_isp_programmer_view_free(AvrIspProgrammerView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h deleted file mode 100644 index 9f005b026..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include "../helpers/avr_isp_types.h" -#include "../helpers/avr_isp_event.h" - -typedef struct AvrIspProgrammerView AvrIspProgrammerView; - -typedef void (*AvrIspProgrammerViewCallback)(AvrIspCustomEvent event, void* context); - -typedef enum { - AvrIspProgrammerViewStatusNoUSBConnect, - AvrIspProgrammerViewStatusUSBConnect, -} AvrIspProgrammerViewStatus; - -void avr_isp_programmer_view_set_callback( - AvrIspProgrammerView* instance, - AvrIspProgrammerViewCallback callback, - void* context); - -AvrIspProgrammerView* avr_isp_programmer_view_alloc(); - -void avr_isp_programmer_view_free(AvrIspProgrammerView* instance); - -View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance); - -void avr_isp_programmer_view_exit(void* context); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c deleted file mode 100644 index 92d15bd7f..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c +++ /dev/null @@ -1,215 +0,0 @@ -#include "avr_isp_view_reader.h" -#include - -#include "../helpers/avr_isp_worker_rw.h" - -struct AvrIspReaderView { - View* view; - AvrIspWorkerRW* avr_isp_worker_rw; - const char* file_path; - const char* file_name; - AvrIspReaderViewCallback callback; - void* context; -}; - -typedef struct { - AvrIspReaderViewStatus status; - float progress_flash; - float progress_eeprom; -} AvrIspReaderViewModel; - -void avr_isp_reader_update_progress(AvrIspReaderView* instance) { - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - model->progress_flash = - avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); - model->progress_eeprom = - avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); - }, - true); -} - -void avr_isp_reader_view_set_callback( - AvrIspReaderView* instance, - AvrIspReaderViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_reader_set_file_path( - AvrIspReaderView* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; -} - -void avr_isp_reader_view_draw(Canvas* canvas, AvrIspReaderViewModel* model) { - canvas_clear(canvas); - char str_buf[64] = {0}; - - canvas_set_font(canvas, FontPrimary); - switch(model->status) { - case AvrIspReaderViewStatusIDLE: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to dump"); - canvas_set_font(canvas, FontSecondary); - elements_button_center(canvas, "Start"); - break; - case AvrIspReaderViewStatusReading: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Reading dump"); - break; - case AvrIspReaderViewStatusVerification: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifyng dump"); - break; - - default: - break; - } - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 0, 27, "Flash"); - snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100)); - elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_buf); - canvas_draw_str(canvas, 0, 43, "EEPROM"); - snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_buf); -} - -bool avr_isp_reader_view_input(InputEvent* event, void* context) { - furi_assert(context); - AvrIspReaderView* instance = context; - - bool ret = true; - if(event->key == InputKeyBack && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - if(model->status == AvrIspReaderViewStatusIDLE) { - if(instance->callback) - instance->callback(AvrIspCustomEventSceneExit, instance->context); - ret = false; - } - }, - false); - } else if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - if(model->status == AvrIspReaderViewStatusIDLE) { - model->status = AvrIspReaderViewStatusReading; - avr_isp_worker_rw_read_dump_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - } - }, - false); - } - return ret; -} - -static void avr_isp_reader_callback_status(void* context, AvrIspWorkerRWStatus status) { - furi_assert(context); - AvrIspReaderView* instance = context; - - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - switch(status) { - case AvrIspWorkerRWStatusEndReading: - model->status = AvrIspReaderViewStatusVerification; - avr_isp_worker_rw_verification_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - model->status = AvrIspReaderViewStatusVerification; - break; - case AvrIspWorkerRWStatusEndVerification: - if(instance->callback) - instance->callback(AvrIspCustomEventSceneReadingOk, instance->context); - break; - case AvrIspWorkerRWStatusErrorVerification: - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); - break; - - default: - //AvrIspWorkerRWStatusErrorReading; - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorReading, instance->context); - break; - } - }, - true); -} - -void avr_isp_reader_view_enter(void* context) { - furi_assert(context); - AvrIspReaderView* instance = context; - - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - model->status = AvrIspReaderViewStatusIDLE; - model->progress_flash = 0.0f; - model->progress_eeprom = 0.0f; - }, - true); - - //Start avr_isp_worker_rw - instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); - - avr_isp_worker_rw_set_callback_status( - instance->avr_isp_worker_rw, avr_isp_reader_callback_status, instance); - - avr_isp_worker_rw_start(instance->avr_isp_worker_rw); -} - -void avr_isp_reader_view_exit(void* context) { - furi_assert(context); - - AvrIspReaderView* instance = context; - //Stop avr_isp_worker_rw - if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { - avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); - } - - avr_isp_worker_rw_free(instance->avr_isp_worker_rw); -} - -AvrIspReaderView* avr_isp_reader_view_alloc() { - AvrIspReaderView* instance = malloc(sizeof(AvrIspReaderView)); - - // View allocation and configuration - instance->view = view_alloc(); - - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspReaderViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_reader_view_draw); - view_set_input_callback(instance->view, avr_isp_reader_view_input); - view_set_enter_callback(instance->view, avr_isp_reader_view_enter); - view_set_exit_callback(instance->view, avr_isp_reader_view_exit); - - return instance; -} - -void avr_isp_reader_view_free(AvrIspReaderView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* avr_isp_reader_view_get_view(AvrIspReaderView* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h deleted file mode 100644 index 44a439948..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include "../helpers/avr_isp_types.h" -#include "../helpers/avr_isp_event.h" - -typedef struct AvrIspReaderView AvrIspReaderView; - -typedef void (*AvrIspReaderViewCallback)(AvrIspCustomEvent event, void* context); - -typedef enum { - AvrIspReaderViewStatusIDLE, - AvrIspReaderViewStatusReading, - AvrIspReaderViewStatusVerification, -} AvrIspReaderViewStatus; - -void avr_isp_reader_update_progress(AvrIspReaderView* instance); - -void avr_isp_reader_set_file_path( - AvrIspReaderView* instance, - const char* file_path, - const char* file_name); - -void avr_isp_reader_view_set_callback( - AvrIspReaderView* instance, - AvrIspReaderViewCallback callback, - void* context); - -AvrIspReaderView* avr_isp_reader_view_alloc(); - -void avr_isp_reader_view_free(AvrIspReaderView* instance); - -View* avr_isp_reader_view_get_view(AvrIspReaderView* instance); - -void avr_isp_reader_view_exit(void* context); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c deleted file mode 100644 index a06b78535..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c +++ /dev/null @@ -1,268 +0,0 @@ -#include "avr_isp_view_writer.h" -#include - -#include "../helpers/avr_isp_worker_rw.h" -#include - -struct AvrIspWriterView { - View* view; - AvrIspWorkerRW* avr_isp_worker_rw; - const char* file_path; - const char* file_name; - AvrIspWriterViewCallback callback; - void* context; -}; - -typedef struct { - AvrIspWriterViewStatus status; - float progress_flash; - float progress_eeprom; -} AvrIspWriterViewModel; - -void avr_isp_writer_update_progress(AvrIspWriterView* instance) { - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - model->progress_flash = - avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); - model->progress_eeprom = - avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); - }, - true); -} - -void avr_isp_writer_view_set_callback( - AvrIspWriterView* instance, - AvrIspWriterViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_writer_set_file_path( - AvrIspWriterView* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; -} - -void avr_isp_writer_view_draw(Canvas* canvas, AvrIspWriterViewModel* model) { - canvas_clear(canvas); - char str_flash[32] = {0}; - char str_eeprom[32] = {0}; - - canvas_set_font(canvas, FontPrimary); - - switch(model->status) { - case AvrIspWriterViewStatusIDLE: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to write"); - canvas_set_font(canvas, FontSecondary); - elements_button_center(canvas, "Start"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - break; - case AvrIspWriterViewStatusWriting: - if(float_is_equal(model->progress_flash, 0.f)) { - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying firmware"); - snprintf(str_flash, sizeof(str_flash), "***"); - snprintf(str_eeprom, sizeof(str_eeprom), "***"); - } else { - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing dump"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf( - str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - } - break; - case AvrIspWriterViewStatusVerification: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying dump"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - break; - case AvrIspWriterViewStatusWritingFuse: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing fuse"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - break; - case AvrIspWriterViewStatusWritingFuseOk: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Done!"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - canvas_set_font(canvas, FontSecondary); - elements_button_center(canvas, "Reflash"); - elements_button_right(canvas, "Exit"); - break; - - default: - break; - } - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 0, 27, "Flash"); - // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100)); - elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_flash); - canvas_draw_str(canvas, 0, 43, "EEPROM"); - // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_eeprom); -} - -bool avr_isp_writer_view_input(InputEvent* event, void* context) { - furi_assert(context); - AvrIspWriterView* instance = context; - - bool ret = true; - if(event->key == InputKeyBack && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - if((model->status == AvrIspWriterViewStatusIDLE) || - (model->status == AvrIspWriterViewStatusWritingFuseOk)) { - if(instance->callback) - instance->callback(AvrIspCustomEventSceneExit, instance->context); - ret = false; - } - }, - false); - } else if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - if((model->status == AvrIspWriterViewStatusIDLE) || - (model->status == AvrIspWriterViewStatusWritingFuseOk)) { - model->status = AvrIspWriterViewStatusWriting; - - avr_isp_worker_rw_write_dump_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - } - }, - false); - } else if(event->key == InputKeyRight && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - if((model->status == AvrIspWriterViewStatusIDLE) || - (model->status == AvrIspWriterViewStatusWritingFuseOk)) { - if(instance->callback) - instance->callback(AvrIspCustomEventSceneExitStartMenu, instance->context); - ret = false; - } - }, - false); - } - return ret; -} - -static void avr_isp_writer_callback_status(void* context, AvrIspWorkerRWStatus status) { - furi_assert(context); - - AvrIspWriterView* instance = context; - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - switch(status) { - case AvrIspWorkerRWStatusEndWriting: - model->status = AvrIspWriterViewStatusVerification; - avr_isp_worker_rw_verification_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - model->status = AvrIspWriterViewStatusVerification; - break; - case AvrIspWorkerRWStatusErrorVerification: - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); - break; - case AvrIspWorkerRWStatusEndVerification: - avr_isp_worker_rw_write_fuse_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - model->status = AvrIspWriterViewStatusWritingFuse; - break; - case AvrIspWorkerRWStatusErrorWritingFuse: - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorWritingFuse, instance->context); - break; - case AvrIspWorkerRWStatusEndWritingFuse: - model->status = AvrIspWriterViewStatusWritingFuseOk; - break; - - default: - //AvrIspWorkerRWStatusErrorWriting; - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorWriting, instance->context); - break; - } - }, - true); -} - -void avr_isp_writer_view_enter(void* context) { - furi_assert(context); - - AvrIspWriterView* instance = context; - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - model->status = AvrIspWriterViewStatusIDLE; - model->progress_flash = 0.0f; - model->progress_eeprom = 0.0f; - }, - true); - - //Start avr_isp_worker_rw - instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); - - avr_isp_worker_rw_set_callback_status( - instance->avr_isp_worker_rw, avr_isp_writer_callback_status, instance); - - avr_isp_worker_rw_start(instance->avr_isp_worker_rw); -} - -void avr_isp_writer_view_exit(void* context) { - furi_assert(context); - AvrIspWriterView* instance = context; - - //Stop avr_isp_worker_rw - if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { - avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); - } - - avr_isp_worker_rw_free(instance->avr_isp_worker_rw); -} - -AvrIspWriterView* avr_isp_writer_view_alloc() { - AvrIspWriterView* instance = malloc(sizeof(AvrIspWriterView)); - - // View allocation and configuration - instance->view = view_alloc(); - - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspWriterViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_writer_view_draw); - view_set_input_callback(instance->view, avr_isp_writer_view_input); - view_set_enter_callback(instance->view, avr_isp_writer_view_enter); - view_set_exit_callback(instance->view, avr_isp_writer_view_exit); - - return instance; -} - -void avr_isp_writer_view_free(AvrIspWriterView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* avr_isp_writer_view_get_view(AvrIspWriterView* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h deleted file mode 100644 index 1ff728387..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include "../helpers/avr_isp_types.h" -#include "../helpers/avr_isp_event.h" - -typedef struct AvrIspWriterView AvrIspWriterView; - -typedef void (*AvrIspWriterViewCallback)(AvrIspCustomEvent event, void* context); - -typedef enum { - AvrIspWriterViewStatusIDLE, - AvrIspWriterViewStatusWriting, - AvrIspWriterViewStatusVerification, - AvrIspWriterViewStatusWritingFuse, - AvrIspWriterViewStatusWritingFuseOk, -} AvrIspWriterViewStatus; - -void avr_isp_writer_update_progress(AvrIspWriterView* instance); - -void avr_isp_writer_set_file_path( - AvrIspWriterView* instance, - const char* file_path, - const char* file_name); - -void avr_isp_writer_view_set_callback( - AvrIspWriterView* instance, - AvrIspWriterViewCallback callback, - void* context); - -AvrIspWriterView* avr_isp_writer_view_alloc(); - -void avr_isp_writer_view_free(AvrIspWriterView* instance); - -View* avr_isp_writer_view_get_view(AvrIspWriterView* instance); - -void avr_isp_writer_view_exit(void* context); diff --git a/applications/external/dap_link/README.md b/applications/external/dap_link/README.md deleted file mode 100644 index aead0a60a..000000000 --- a/applications/external/dap_link/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# Flipper Zero as CMSIS DAP/DAP Link -Flipper Zero as a [Free-DAP](https://github.com/ataradov/free-dap) based SWD\JTAG debugger. Free-DAP is a free and open source firmware implementation of the [CMSIS-DAP](https://www.keil.com/pack/doc/CMSIS_Dev/DAP/html/index.html) debugger. - -## Protocols -SWD, JTAG , CMSIS-DAP v1 (18 KiB/s), CMSIS-DAP v2 (46 KiB/s), VCP (USB-UART). - -WinUSB for driverless installation for Windows 8 and above. - -## Usage - -### VSCode + Cortex-Debug - Set `"device": "cmsis-dap"` - -
- BluePill configuration example - - ```json -{ - "name": "Attach (DAP)", - "cwd": "${workspaceFolder}", - "executable": "./build/firmware.elf", - "request": "attach", - "type": "cortex-debug", - "servertype": "openocd", - "device": "cmsis-dap", - "configFiles": [ - "interface/cmsis-dap.cfg", - "target/stm32f1x.cfg", - ], -}, - ``` -
- -
- Flipper Zero configuration example - - ```json -{ - "name": "Attach (DAP)", - "cwd": "${workspaceFolder}", - "executable": "./build/latest/firmware.elf", - "request": "attach", - "type": "cortex-debug", - "servertype": "openocd", - "device": "cmsis-dap", - "svdFile": "./debug/STM32WB55_CM4.svd", - "rtos": "FreeRTOS", - "configFiles": [ - "interface/cmsis-dap.cfg", - "./debug/stm32wbx.cfg", - ], - "postAttachCommands": [ - "source debug/flipperapps.py", - ], -}, - ``` -
- -### OpenOCD -Use `interface/cmsis-dap.cfg`. You will need OpenOCD v0.11.0. - -Additional commands: -* `cmsis_dap_backend hid` for CMSIS-DAP v1 protocol. -* `cmsis_dap_backend usb_bulk` for CMSIS-DAP v2 protocol. -* `cmsis_dap_serial DAP_Oyevoxo` use DAP-Link running on Flipper named `Oyevoxo`. -* `cmsis-dap cmd 81` - reboot connected DAP-Link. - -
- Flash BluePill - - ``` -openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c init -c "program build/firmware.bin reset exit 0x8000000" - ``` -
- -
- Flash Flipper Zero using DAP v2 protocol - - ``` -openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_backend usb_bulk" -f debug/stm32wbx.cfg -c init -c "program build/latest/firmware.bin reset exit 0x8000000" - ``` -
- -
- Reboot connected DAP-Link on Flipper named Oyevoxo - - ``` -openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_serial DAP_Oyevoxo" -c "transport select swd" -c "adapter speed 4000000" -c init -c "cmsis-dap cmd 81" -c "exit" - ``` -
- -### PlatformIO -Use `debug_tool = cmsis-dap` and `upload_protocol = cmsis-dap`. [Documentation](https://docs.platformio.org/en/latest/plus/debug-tools/cmsis-dap.html#debugging-tool-cmsis-dap). Remember that Windows 8 and above do not require drivers. - -
- BluePill platformio.ini example - - ``` -[env:bluepill_f103c8] -platform = ststm32 -board = bluepill_f103c8 -debug_tool = cmsis-dap -upload_protocol = cmsis-dap - ``` -
diff --git a/applications/external/dap_link/application.fam b/applications/external/dap_link/application.fam deleted file mode 100644 index 017143803..000000000 --- a/applications/external/dap_link/application.fam +++ /dev/null @@ -1,24 +0,0 @@ -App( - appid="dap_link", - name="DAP Link", - apptype=FlipperAppType.EXTERNAL, - entry_point="dap_link_app", - requires=[ - "gui", - "dialogs", - ], - stack_size=4 * 1024, - order=20, - fap_icon="dap_link.png", - fap_category="GPIO", - fap_private_libs=[ - Lib( - name="free-dap", - cincludes=["."], - sources=[ - "dap.c", - ], - ), - ], - fap_icon_assets="icons", -) diff --git a/applications/external/dap_link/dap_config.h b/applications/external/dap_link/dap_config.h deleted file mode 100644 index 88b90bd34..000000000 --- a/applications/external/dap_link/dap_config.h +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2022, Alex Taradov . All rights reserved. - -#ifndef _DAP_CONFIG_H_ -#define _DAP_CONFIG_H_ - -/*- Includes ----------------------------------------------------------------*/ -#include - -/*- Definitions -------------------------------------------------------------*/ -#define DAP_CONFIG_ENABLE_JTAG - -#define DAP_CONFIG_DEFAULT_PORT DAP_PORT_SWD -#define DAP_CONFIG_DEFAULT_CLOCK 4200000 // Hz - -#define DAP_CONFIG_PACKET_SIZE 64 -#define DAP_CONFIG_PACKET_COUNT 1 - -#define DAP_CONFIG_JTAG_DEV_COUNT 8 - -// DAP_CONFIG_PRODUCT_STR must contain "CMSIS-DAP" to be compatible with the standard -#define DAP_CONFIG_VENDOR_STR "Flipper Zero" -#define DAP_CONFIG_PRODUCT_STR "Generic CMSIS-DAP Adapter" -#define DAP_CONFIG_SER_NUM_STR usb_serial_number -#define DAP_CONFIG_CMSIS_DAP_VER_STR "2.0.0" - -#define DAP_CONFIG_RESET_TARGET_FN dap_app_target_reset -#define DAP_CONFIG_VENDOR_FN dap_app_vendor_cmd - -// Attribute to use for performance-critical functions -#define DAP_CONFIG_PERFORMANCE_ATTR - -// A value at which dap_clock_test() produces 1 kHz output on the SWCLK pin -// #define DAP_CONFIG_DELAY_CONSTANT 19000 -#define DAP_CONFIG_DELAY_CONSTANT 6290 - -// A threshold for switching to fast clock (no added delays) -// This is the frequency produced by dap_clock_test(1) on the SWCLK pin -#define DAP_CONFIG_FAST_CLOCK 2400000 // Hz - -/*- Prototypes --------------------------------------------------------------*/ -extern char usb_serial_number[16]; - -/*- Implementations ---------------------------------------------------------*/ -extern GpioPin flipper_dap_swclk_pin; -extern GpioPin flipper_dap_swdio_pin; -extern GpioPin flipper_dap_reset_pin; -extern GpioPin flipper_dap_tdo_pin; -extern GpioPin flipper_dap_tdi_pin; - -extern void dap_app_vendor_cmd(uint8_t cmd); -extern void dap_app_target_reset(); -extern void dap_app_disconnect(); -extern void dap_app_connect_swd(); -extern void dap_app_connect_jtag(); - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWCLK_TCK_write(int value) { - furi_hal_gpio_write(&flipper_dap_swclk_pin, value); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWDIO_TMS_write(int value) { - furi_hal_gpio_write(&flipper_dap_swdio_pin, value); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_TDI_write(int value) { -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_write(&flipper_dap_tdi_pin, value); -#else - (void)value; -#endif -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_TDO_write(int value) { -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_write(&flipper_dap_tdo_pin, value); -#else - (void)value; -#endif -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_nTRST_write(int value) { - (void)value; -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_nRESET_write(int value) { - furi_hal_gpio_write(&flipper_dap_reset_pin, value); -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_SWCLK_TCK_read(void) { - return furi_hal_gpio_read(&flipper_dap_swclk_pin); -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_SWDIO_TMS_read(void) { - return furi_hal_gpio_read(&flipper_dap_swdio_pin); -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_TDO_read(void) { -#ifdef DAP_CONFIG_ENABLE_JTAG - return furi_hal_gpio_read(&flipper_dap_tdo_pin); -#else - return 0; -#endif -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_TDI_read(void) { -#ifdef DAP_CONFIG_ENABLE_JTAG - return furi_hal_gpio_read(&flipper_dap_tdi_pin); -#else - return 0; -#endif -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_nTRST_read(void) { - return 0; -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_nRESET_read(void) { - return furi_hal_gpio_read(&flipper_dap_reset_pin); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWCLK_TCK_set(void) { - LL_GPIO_SetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWCLK_TCK_clr(void) { - LL_GPIO_ResetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWDIO_TMS_in(void) { - LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_INPUT); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWDIO_TMS_out(void) { - LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_OUTPUT); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SETUP(void) { - furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#endif -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_DISCONNECT(void) { - furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#endif - dap_app_disconnect(); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_CONNECT_SWD(void) { - furi_hal_gpio_init( - &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_swdio_pin, true); - - furi_hal_gpio_init( - &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_swclk_pin, true); - - furi_hal_gpio_init( - &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_reset_pin, true); - -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#endif - dap_app_connect_swd(); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_CONNECT_JTAG(void) { - furi_hal_gpio_init( - &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_swdio_pin, true); - - furi_hal_gpio_init( - &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_swclk_pin, true); - - furi_hal_gpio_init( - &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_reset_pin, true); - -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - - furi_hal_gpio_init( - &flipper_dap_tdi_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_tdi_pin, true); -#endif - dap_app_connect_jtag(); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_LED(int index, int state) { - (void)index; - (void)state; -} - -//----------------------------------------------------------------------------- -__attribute__((always_inline)) static inline void DAP_CONFIG_DELAY(uint32_t cycles) { - asm volatile("1: subs %[cycles], %[cycles], #1 \n" - " bne 1b \n" - : [cycles] "+l"(cycles)); -} - -#endif // _DAP_CONFIG_H_ diff --git a/applications/external/dap_link/dap_link.c b/applications/external/dap_link/dap_link.c deleted file mode 100644 index eafb435e7..000000000 --- a/applications/external/dap_link/dap_link.c +++ /dev/null @@ -1,527 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dap_link.h" -#include "dap_config.h" -#include "gui/dap_gui.h" -#include "usb/dap_v2_usb.h" -#include -#include "dap_link_icons.h" - -/***************************************************************************/ -/****************************** DAP COMMON *********************************/ -/***************************************************************************/ - -struct DapApp { - FuriThread* dap_thread; - FuriThread* cdc_thread; - FuriThread* gui_thread; - - DapState state; - DapConfig config; -}; - -void dap_app_get_state(DapApp* app, DapState* state) { - *state = app->state; -} - -#define DAP_PROCESS_THREAD_TICK 500 - -typedef enum { - DapThreadEventStop = (1 << 0), -} DapThreadEvent; - -void dap_thread_send_stop(FuriThread* thread) { - furi_thread_flags_set(furi_thread_get_id(thread), DapThreadEventStop); -} - -GpioPin flipper_dap_swclk_pin; -GpioPin flipper_dap_swdio_pin; -GpioPin flipper_dap_reset_pin; -GpioPin flipper_dap_tdo_pin; -GpioPin flipper_dap_tdi_pin; - -/***************************************************************************/ -/****************************** DAP PROCESS ********************************/ -/***************************************************************************/ - -typedef struct { - uint8_t data[DAP_CONFIG_PACKET_SIZE]; - uint8_t size; -} DapPacket; - -typedef enum { - DAPThreadEventStop = DapThreadEventStop, - DAPThreadEventRxV1 = (1 << 1), - DAPThreadEventRxV2 = (1 << 2), - DAPThreadEventUSBConnect = (1 << 3), - DAPThreadEventUSBDisconnect = (1 << 4), - DAPThreadEventApplyConfig = (1 << 5), - DAPThreadEventAll = DAPThreadEventStop | DAPThreadEventRxV1 | DAPThreadEventRxV2 | - DAPThreadEventUSBConnect | DAPThreadEventUSBDisconnect | - DAPThreadEventApplyConfig, -} DAPThreadEvent; - -#define USB_SERIAL_NUMBER_LEN 16 -char usb_serial_number[USB_SERIAL_NUMBER_LEN] = {0}; - -const char* dap_app_get_serial(DapApp* app) { - UNUSED(app); - return usb_serial_number; -} - -static void dap_app_rx1_callback(void* context) { - furi_assert(context); - FuriThreadId thread_id = (FuriThreadId)context; - furi_thread_flags_set(thread_id, DAPThreadEventRxV1); -} - -static void dap_app_rx2_callback(void* context) { - furi_assert(context); - FuriThreadId thread_id = (FuriThreadId)context; - furi_thread_flags_set(thread_id, DAPThreadEventRxV2); -} - -static void dap_app_usb_state_callback(bool state, void* context) { - furi_assert(context); - FuriThreadId thread_id = (FuriThreadId)context; - if(state) { - furi_thread_flags_set(thread_id, DAPThreadEventUSBConnect); - } else { - furi_thread_flags_set(thread_id, DAPThreadEventUSBDisconnect); - } -} - -static void dap_app_process_v1() { - DapPacket tx_packet; - DapPacket rx_packet; - memset(&tx_packet, 0, sizeof(DapPacket)); - rx_packet.size = dap_v1_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); - dap_process_request(rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); - dap_v1_usb_tx(tx_packet.data, DAP_CONFIG_PACKET_SIZE); -} - -static void dap_app_process_v2() { - DapPacket tx_packet; - DapPacket rx_packet; - memset(&tx_packet, 0, sizeof(DapPacket)); - rx_packet.size = dap_v2_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); - size_t len = dap_process_request( - rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); - dap_v2_usb_tx(tx_packet.data, len); -} - -void dap_app_vendor_cmd(uint8_t cmd) { - // openocd -c "cmsis-dap cmd 81" - if(cmd == 0x01) { - furi_hal_power_reset(); - } -} - -void dap_app_target_reset() { - FURI_LOG_I("DAP", "Target reset"); -} - -static void dap_init_gpio(DapSwdPins swd_pins) { - switch(swd_pins) { - case DapSwdPinsPA7PA6: - flipper_dap_swclk_pin = gpio_ext_pa7; - flipper_dap_swdio_pin = gpio_ext_pa6; - break; - case DapSwdPinsPA14PA13: - flipper_dap_swclk_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_14}; - flipper_dap_swdio_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_13}; - break; - } - - flipper_dap_reset_pin = gpio_ext_pa4; - flipper_dap_tdo_pin = gpio_ext_pb3; - flipper_dap_tdi_pin = gpio_ext_pb2; -} - -static void dap_deinit_gpio(DapSwdPins swd_pins) { - // setup gpio pins to default state - furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - if(DapSwdPinsPA14PA13 == swd_pins) { - // PA14 and PA13 are used by SWD - furi_hal_gpio_init_ex( - &flipper_dap_swclk_pin, - GpioModeAltFunctionPushPull, - GpioPullDown, - GpioSpeedLow, - GpioAltFn0JTCK_SWCLK); - furi_hal_gpio_init_ex( - &flipper_dap_swdio_pin, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn0JTMS_SWDIO); - } else { - furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - } -} - -static int32_t dap_process(void* p) { - DapApp* app = p; - DapState* dap_state = &(app->state); - - // allocate resources - FuriHalUsbInterface* usb_config_prev; - app->config.swd_pins = DapSwdPinsPA7PA6; - DapSwdPins swd_pins_prev = app->config.swd_pins; - - // init pins - dap_init_gpio(swd_pins_prev); - - // init dap - dap_init(); - - // get name - const char* name = furi_hal_version_get_name_ptr(); - if(!name) { - name = "Flipper"; - } - snprintf(usb_serial_number, USB_SERIAL_NUMBER_LEN, "DAP_%s", name); - - // init usb - usb_config_prev = furi_hal_usb_get_config(); - dap_common_usb_alloc_name(usb_serial_number); - dap_common_usb_set_context(furi_thread_get_id(furi_thread_get_current())); - dap_v1_usb_set_rx_callback(dap_app_rx1_callback); - dap_v2_usb_set_rx_callback(dap_app_rx2_callback); - dap_common_usb_set_state_callback(dap_app_usb_state_callback); - furi_hal_usb_set_config(&dap_v2_usb_hid, NULL); - - // work - uint32_t events; - while(1) { - events = furi_thread_flags_wait(DAPThreadEventAll, FuriFlagWaitAny, FuriWaitForever); - - if(!(events & FuriFlagError)) { - if(events & DAPThreadEventRxV1) { - dap_app_process_v1(); - dap_state->dap_counter++; - dap_state->dap_version = DapVersionV1; - } - - if(events & DAPThreadEventRxV2) { - dap_app_process_v2(); - dap_state->dap_counter++; - dap_state->dap_version = DapVersionV2; - } - - if(events & DAPThreadEventUSBConnect) { - dap_state->usb_connected = true; - } - - if(events & DAPThreadEventUSBDisconnect) { - dap_state->usb_connected = false; - dap_state->dap_version = DapVersionUnknown; - } - - if(events & DAPThreadEventApplyConfig) { - if(swd_pins_prev != app->config.swd_pins) { - dap_deinit_gpio(swd_pins_prev); - swd_pins_prev = app->config.swd_pins; - dap_init_gpio(swd_pins_prev); - } - } - - if(events & DAPThreadEventStop) { - break; - } - } - } - - // deinit usb - furi_hal_usb_set_config(usb_config_prev, NULL); - dap_common_usb_free_name(); - dap_deinit_gpio(swd_pins_prev); - return 0; -} - -/***************************************************************************/ -/****************************** CDC PROCESS ********************************/ -/***************************************************************************/ - -typedef enum { - CDCThreadEventStop = DapThreadEventStop, - CDCThreadEventUARTRx = (1 << 1), - CDCThreadEventCDCRx = (1 << 2), - CDCThreadEventCDCConfig = (1 << 3), - CDCThreadEventApplyConfig = (1 << 4), - CDCThreadEventAll = CDCThreadEventStop | CDCThreadEventUARTRx | CDCThreadEventCDCRx | - CDCThreadEventCDCConfig | CDCThreadEventApplyConfig, -} CDCThreadEvent; - -typedef struct { - FuriStreamBuffer* rx_stream; - FuriThreadId thread_id; - FuriHalUartId uart_id; - struct usb_cdc_line_coding line_coding; -} CDCProcess; - -static void cdc_uart_irq_cb(UartIrqEvent ev, uint8_t data, void* ctx) { - CDCProcess* app = ctx; - - if(ev == UartIrqEventRXNE) { - furi_stream_buffer_send(app->rx_stream, &data, 1, 0); - furi_thread_flags_set(app->thread_id, CDCThreadEventUARTRx); - } -} - -static void cdc_usb_rx_callback(void* context) { - CDCProcess* app = context; - furi_thread_flags_set(app->thread_id, CDCThreadEventCDCRx); -} - -static void cdc_usb_control_line_callback(uint8_t state, void* context) { - UNUSED(context); - UNUSED(state); -} - -static void cdc_usb_config_callback(struct usb_cdc_line_coding* config, void* context) { - CDCProcess* app = context; - app->line_coding = *config; - furi_thread_flags_set(app->thread_id, CDCThreadEventCDCConfig); -} - -static FuriHalUartId cdc_init_uart( - DapUartType type, - DapUartTXRX swap, - uint32_t baudrate, - void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), - void* ctx) { - FuriHalUartId uart_id = FuriHalUartIdUSART1; - if(baudrate == 0) baudrate = 115200; - - switch(type) { - case DapUartTypeUSART1: - uart_id = FuriHalUartIdUSART1; - furi_hal_console_disable(); - furi_hal_uart_deinit(uart_id); - if(swap == DapUartTXRXSwap) { - LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_SWAPPED); - } else { - LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); - } - furi_hal_uart_init(uart_id, baudrate); - furi_hal_uart_set_irq_cb(uart_id, cb, ctx); - break; - case DapUartTypeLPUART1: - uart_id = FuriHalUartIdLPUART1; - furi_hal_uart_deinit(uart_id); - if(swap == DapUartTXRXSwap) { - LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_SWAPPED); - } else { - LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); - } - furi_hal_uart_init(uart_id, baudrate); - furi_hal_uart_set_irq_cb(uart_id, cb, ctx); - break; - } - - return uart_id; -} - -static void cdc_deinit_uart(DapUartType type) { - switch(type) { - case DapUartTypeUSART1: - furi_hal_uart_deinit(FuriHalUartIdUSART1); - LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); - furi_hal_console_init(); - break; - case DapUartTypeLPUART1: - furi_hal_uart_deinit(FuriHalUartIdLPUART1); - LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); - break; - } -} - -static int32_t cdc_process(void* p) { - DapApp* dap_app = p; - DapState* dap_state = &(dap_app->state); - - dap_app->config.uart_pins = DapUartTypeLPUART1; - dap_app->config.uart_swap = DapUartTXRXNormal; - - DapUartType uart_pins_prev = dap_app->config.uart_pins; - DapUartTXRX uart_swap_prev = dap_app->config.uart_swap; - - CDCProcess* app = malloc(sizeof(CDCProcess)); - app->thread_id = furi_thread_get_id(furi_thread_get_current()); - app->rx_stream = furi_stream_buffer_alloc(512, 1); - - const uint8_t rx_buffer_size = 64; - uint8_t* rx_buffer = malloc(rx_buffer_size); - - app->uart_id = cdc_init_uart( - uart_pins_prev, uart_swap_prev, dap_state->cdc_baudrate, cdc_uart_irq_cb, app); - - dap_cdc_usb_set_context(app); - dap_cdc_usb_set_rx_callback(cdc_usb_rx_callback); - dap_cdc_usb_set_control_line_callback(cdc_usb_control_line_callback); - dap_cdc_usb_set_config_callback(cdc_usb_config_callback); - - uint32_t events; - while(1) { - events = furi_thread_flags_wait(CDCThreadEventAll, FuriFlagWaitAny, FuriWaitForever); - - if(!(events & FuriFlagError)) { - if(events & CDCThreadEventCDCConfig) { - if(dap_state->cdc_baudrate != app->line_coding.dwDTERate) { - dap_state->cdc_baudrate = app->line_coding.dwDTERate; - if(dap_state->cdc_baudrate > 0) { - furi_hal_uart_set_br(app->uart_id, dap_state->cdc_baudrate); - } - } - } - - if(events & CDCThreadEventUARTRx) { - size_t len = - furi_stream_buffer_receive(app->rx_stream, rx_buffer, rx_buffer_size, 0); - - if(len > 0) { - dap_cdc_usb_tx(rx_buffer, len); - } - dap_state->cdc_rx_counter += len; - } - - if(events & CDCThreadEventCDCRx) { - size_t len = dap_cdc_usb_rx(rx_buffer, rx_buffer_size); - if(len > 0) { - furi_hal_uart_tx(app->uart_id, rx_buffer, len); - } - dap_state->cdc_tx_counter += len; - } - - if(events & CDCThreadEventApplyConfig) { - if(uart_pins_prev != dap_app->config.uart_pins || - uart_swap_prev != dap_app->config.uart_swap) { - cdc_deinit_uart(uart_pins_prev); - uart_pins_prev = dap_app->config.uart_pins; - uart_swap_prev = dap_app->config.uart_swap; - app->uart_id = cdc_init_uart( - uart_pins_prev, - uart_swap_prev, - dap_state->cdc_baudrate, - cdc_uart_irq_cb, - app); - } - } - - if(events & CDCThreadEventStop) { - break; - } - } - } - - cdc_deinit_uart(uart_pins_prev); - free(rx_buffer); - furi_stream_buffer_free(app->rx_stream); - free(app); - - return 0; -} - -/***************************************************************************/ -/******************************* MAIN APP **********************************/ -/***************************************************************************/ - -static DapApp* dap_app_alloc() { - DapApp* dap_app = malloc(sizeof(DapApp)); - dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app); - dap_app->cdc_thread = furi_thread_alloc_ex("DAP CDC", 1024, cdc_process, dap_app); - dap_app->gui_thread = furi_thread_alloc_ex("DAP GUI", 1024, dap_gui_thread, dap_app); - return dap_app; -} - -static void dap_app_free(DapApp* dap_app) { - furi_assert(dap_app); - furi_thread_free(dap_app->dap_thread); - furi_thread_free(dap_app->cdc_thread); - furi_thread_free(dap_app->gui_thread); - free(dap_app); -} - -static DapApp* app_handle = NULL; - -void dap_app_disconnect() { - app_handle->state.dap_mode = DapModeDisconnected; -} - -void dap_app_connect_swd() { - app_handle->state.dap_mode = DapModeSWD; -} - -void dap_app_connect_jtag() { - app_handle->state.dap_mode = DapModeJTAG; -} - -void dap_app_set_config(DapApp* app, DapConfig* config) { - app->config = *config; - furi_thread_flags_set(furi_thread_get_id(app->dap_thread), DAPThreadEventApplyConfig); - furi_thread_flags_set(furi_thread_get_id(app->cdc_thread), CDCThreadEventApplyConfig); -} - -DapConfig* dap_app_get_config(DapApp* app) { - return &app->config; -} - -int32_t dap_link_app(void* p) { - UNUSED(p); - - if(furi_hal_usb_is_locked()) { - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); - dialog_message_set_text( - message, - "Disconnect from\nPC or phone to\nuse this function.", - 3, - 30, - AlignLeft, - AlignTop); - dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0); - dialog_message_show(dialogs, message); - dialog_message_free(message); - furi_record_close(RECORD_DIALOGS); - return -1; - } - - // alloc app - DapApp* app = dap_app_alloc(); - app_handle = app; - - furi_thread_start(app->dap_thread); - furi_thread_start(app->cdc_thread); - furi_thread_start(app->gui_thread); - - // wait until gui thread is finished - furi_thread_join(app->gui_thread); - - // send stop event to threads - dap_thread_send_stop(app->dap_thread); - dap_thread_send_stop(app->cdc_thread); - - // wait for threads to stop - furi_thread_join(app->dap_thread); - furi_thread_join(app->cdc_thread); - - // free app - dap_app_free(app); - - return 0; -} \ No newline at end of file diff --git a/applications/external/dap_link/dap_link.h b/applications/external/dap_link/dap_link.h deleted file mode 100644 index d51726c45..000000000 --- a/applications/external/dap_link/dap_link.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include - -typedef enum { - DapModeDisconnected, - DapModeSWD, - DapModeJTAG, -} DapMode; - -typedef enum { - DapVersionUnknown, - DapVersionV1, - DapVersionV2, -} DapVersion; - -typedef struct { - bool usb_connected; - DapMode dap_mode; - DapVersion dap_version; - uint32_t dap_counter; - uint32_t cdc_baudrate; - uint32_t cdc_tx_counter; - uint32_t cdc_rx_counter; -} DapState; - -typedef enum { - DapSwdPinsPA7PA6, // Pins 2, 3 - DapSwdPinsPA14PA13, // Pins 10, 12 -} DapSwdPins; - -typedef enum { - DapUartTypeUSART1, // Pins 13, 14 - DapUartTypeLPUART1, // Pins 15, 16 -} DapUartType; - -typedef enum { - DapUartTXRXNormal, - DapUartTXRXSwap, -} DapUartTXRX; - -typedef struct { - DapSwdPins swd_pins; - DapUartType uart_pins; - DapUartTXRX uart_swap; -} DapConfig; - -typedef struct DapApp DapApp; - -void dap_app_get_state(DapApp* app, DapState* state); - -const char* dap_app_get_serial(DapApp* app); - -void dap_app_set_config(DapApp* app, DapConfig* config); - -DapConfig* dap_app_get_config(DapApp* app); \ No newline at end of file diff --git a/applications/external/dap_link/dap_link.png b/applications/external/dap_link/dap_link.png deleted file mode 100644 index 2278ce2b61cf97e51720bafb2d07dae48d986560..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>@$__Y43U`H zI>D2R!GMF={pJ7tHnyBWw`O@WO=4vXnQOryxnPsm9sxmyCsV>A?tXRt_DY<&Wq)IO q>DB4a%~XFc{Gk)L^WR4G>Gvdb>|Y;BDpm&?%HZkh=d#Wzp$P!!VJ~6; diff --git a/applications/external/dap_link/gui/dap_gui.c b/applications/external/dap_link/gui/dap_gui.c deleted file mode 100644 index 4dd986153..000000000 --- a/applications/external/dap_link/gui/dap_gui.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "dap_gui.h" -#include "dap_gui_i.h" - -#define DAP_GUI_TICK 250 - -static bool dap_gui_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - DapGuiApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool dap_gui_back_event_callback(void* context) { - furi_assert(context); - DapGuiApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void dap_gui_tick_event_callback(void* context) { - furi_assert(context); - DapGuiApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -DapGuiApp* dap_gui_alloc() { - DapGuiApp* app = malloc(sizeof(DapGuiApp)); - app->gui = furi_record_open(RECORD_GUI); - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&dap_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - - view_dispatcher_set_custom_event_callback(app->view_dispatcher, dap_gui_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, dap_gui_back_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, dap_gui_tick_event_callback, DAP_GUI_TICK); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - app->var_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - DapGuiAppViewVarItemList, - variable_item_list_get_view(app->var_item_list)); - - app->main_view = dap_main_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, DapGuiAppViewMainView, dap_main_view_get_view(app->main_view)); - - app->widget = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, DapGuiAppViewWidget, widget_get_view(app->widget)); - - scene_manager_next_scene(app->scene_manager, DapSceneMain); - - return app; -} - -void dap_gui_free(DapGuiApp* app) { - view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewVarItemList); - variable_item_list_free(app->var_item_list); - - view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewMainView); - dap_main_view_free(app->main_view); - - view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewWidget); - widget_free(app->widget); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Close records - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - - free(app); -} - -int32_t dap_gui_thread(void* arg) { - DapGuiApp* app = dap_gui_alloc(); - app->dap_app = arg; - - notification_message_block(app->notifications, &sequence_display_backlight_enforce_on); - view_dispatcher_run(app->view_dispatcher); - notification_message_block(app->notifications, &sequence_display_backlight_enforce_auto); - - dap_gui_free(app); - return 0; -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/dap_gui.h b/applications/external/dap_link/gui/dap_gui.h deleted file mode 100644 index 3d8e6bdf9..000000000 --- a/applications/external/dap_link/gui/dap_gui.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -int32_t dap_gui_thread(void* arg); \ No newline at end of file diff --git a/applications/external/dap_link/gui/dap_gui_custom_event.h b/applications/external/dap_link/gui/dap_gui_custom_event.h deleted file mode 100644 index 8b127c9d4..000000000 --- a/applications/external/dap_link/gui/dap_gui_custom_event.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -typedef enum { - DapAppCustomEventConfig, - DapAppCustomEventHelp, - DapAppCustomEventAbout, -} DapAppCustomEvent; diff --git a/applications/external/dap_link/gui/dap_gui_i.h b/applications/external/dap_link/gui/dap_gui_i.h deleted file mode 100644 index 59411e78c..000000000 --- a/applications/external/dap_link/gui/dap_gui_i.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "dap_gui.h" -#include "../dap_link.h" -#include "scenes/config/dap_scene.h" -#include "dap_gui_custom_event.h" -#include "views/dap_main_view.h" - -typedef struct { - DapApp* dap_app; - - Gui* gui; - NotificationApp* notifications; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - - VariableItemList* var_item_list; - DapMainView* main_view; - Widget* widget; -} DapGuiApp; - -typedef enum { - DapGuiAppViewVarItemList, - DapGuiAppViewMainView, - DapGuiAppViewWidget, -} DapGuiAppView; diff --git a/applications/external/dap_link/gui/scenes/config/dap_scene.c b/applications/external/dap_link/gui/scenes/config/dap_scene.c deleted file mode 100644 index 37e235540..000000000 --- a/applications/external/dap_link/gui/scenes/config/dap_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "dap_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const dap_scene_on_enter_handlers[])(void*) = { -#include "dap_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const dap_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "dap_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const dap_scene_on_exit_handlers[])(void* context) = { -#include "dap_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers dap_scene_handlers = { - .on_enter_handlers = dap_scene_on_enter_handlers, - .on_event_handlers = dap_scene_on_event_handlers, - .on_exit_handlers = dap_scene_on_exit_handlers, - .scene_num = DapSceneNum, -}; diff --git a/applications/external/dap_link/gui/scenes/config/dap_scene.h b/applications/external/dap_link/gui/scenes/config/dap_scene.h deleted file mode 100644 index 6fb38da4a..000000000 --- a/applications/external/dap_link/gui/scenes/config/dap_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) DapScene##id, -typedef enum { -#include "dap_scene_config.h" - DapSceneNum, -} DapScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers dap_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "dap_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "dap_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "dap_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/dap_link/gui/scenes/config/dap_scene_config.h b/applications/external/dap_link/gui/scenes/config/dap_scene_config.h deleted file mode 100644 index 8957aca06..000000000 --- a/applications/external/dap_link/gui/scenes/config/dap_scene_config.h +++ /dev/null @@ -1,4 +0,0 @@ -ADD_SCENE(dap, main, Main) -ADD_SCENE(dap, config, Config) -ADD_SCENE(dap, help, Help) -ADD_SCENE(dap, about, About) \ No newline at end of file diff --git a/applications/external/dap_link/gui/scenes/dap_scene_about.c b/applications/external/dap_link/gui/scenes/dap_scene_about.c deleted file mode 100644 index 0974e60a7..000000000 --- a/applications/external/dap_link/gui/scenes/dap_scene_about.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "../dap_gui_i.h" - -#define DAP_VERSION_APP "0.1.0" -#define DAP_DEVELOPED "Dr_Zlo" -#define DAP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" - -void dap_scene_about_on_enter(void* context) { - DapGuiApp* app = context; - - FuriString* temp_str; - temp_str = furi_string_alloc(); - furi_string_printf(temp_str, "\e#%s\n", "Information"); - - furi_string_cat_printf(temp_str, "Version: %s\n", DAP_VERSION_APP); - furi_string_cat_printf(temp_str, "Developed by: %s\n", DAP_DEVELOPED); - furi_string_cat_printf(temp_str, "Github: %s\n\n", DAP_GITHUB); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); - furi_string_cat_printf( - temp_str, "CMSIS-DAP debugger\nbased on Free-DAP\nThanks to Alex Taradov\n\n"); - - furi_string_cat_printf( - temp_str, - "Supported protocols:\n" - "SWD, JTAG, UART\n" - "DAP v1 (cmsis_backend hid), DAP v2 (cmsis_backend usb_bulk), VCP\n"); - - widget_add_text_box_element( - app->widget, - 0, - 0, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! \e!\n", - false); - widget_add_text_box_element( - app->widget, - 0, - 2, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! DAP Link \e!\n", - false); - widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); - furi_string_free(temp_str); - - view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); -} - -bool dap_scene_about_on_event(void* context, SceneManagerEvent event) { - DapGuiApp* app = context; - bool consumed = false; - UNUSED(app); - UNUSED(event); - - return consumed; -} - -void dap_scene_about_on_exit(void* context) { - DapGuiApp* app = context; - - // Clear views - widget_reset(app->widget); -} diff --git a/applications/external/dap_link/gui/scenes/dap_scene_config.c b/applications/external/dap_link/gui/scenes/dap_scene_config.c deleted file mode 100644 index 48d5fedcd..000000000 --- a/applications/external/dap_link/gui/scenes/dap_scene_config.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "../dap_gui_i.h" - -static const char* swd_pins[] = {[DapSwdPinsPA7PA6] = "2,3", [DapSwdPinsPA14PA13] = "10,12"}; -static const char* uart_pins[] = {[DapUartTypeUSART1] = "13,14", [DapUartTypeLPUART1] = "15,16"}; -static const char* uart_swap[] = {[DapUartTXRXNormal] = "No", [DapUartTXRXSwap] = "Yes"}; - -static void swd_pins_cb(VariableItem* item) { - DapGuiApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, swd_pins[index]); - - DapConfig* config = dap_app_get_config(app->dap_app); - config->swd_pins = index; - dap_app_set_config(app->dap_app, config); -} - -static void uart_pins_cb(VariableItem* item) { - DapGuiApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, uart_pins[index]); - - DapConfig* config = dap_app_get_config(app->dap_app); - config->uart_pins = index; - dap_app_set_config(app->dap_app, config); -} - -static void uart_swap_cb(VariableItem* item) { - DapGuiApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, uart_swap[index]); - - DapConfig* config = dap_app_get_config(app->dap_app); - config->uart_swap = index; - dap_app_set_config(app->dap_app, config); -} - -static void ok_cb(void* context, uint32_t index) { - DapGuiApp* app = context; - switch(index) { - case 3: - view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventHelp); - break; - case 4: - view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventAbout); - break; - default: - break; - } -} - -void dap_scene_config_on_enter(void* context) { - DapGuiApp* app = context; - VariableItemList* var_item_list = app->var_item_list; - VariableItem* item; - DapConfig* config = dap_app_get_config(app->dap_app); - - item = variable_item_list_add( - var_item_list, "SWC SWD Pins", COUNT_OF(swd_pins), swd_pins_cb, app); - variable_item_set_current_value_index(item, config->swd_pins); - variable_item_set_current_value_text(item, swd_pins[config->swd_pins]); - - item = - variable_item_list_add(var_item_list, "UART Pins", COUNT_OF(uart_pins), uart_pins_cb, app); - variable_item_set_current_value_index(item, config->uart_pins); - variable_item_set_current_value_text(item, uart_pins[config->uart_pins]); - - item = variable_item_list_add( - var_item_list, "Swap TX RX", COUNT_OF(uart_swap), uart_swap_cb, app); - variable_item_set_current_value_index(item, config->uart_swap); - variable_item_set_current_value_text(item, uart_swap[config->uart_swap]); - - variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL); - variable_item_list_add(var_item_list, "About", 0, NULL, NULL); - - variable_item_list_set_selected_item( - var_item_list, scene_manager_get_scene_state(app->scene_manager, DapSceneConfig)); - - variable_item_list_set_enter_callback(var_item_list, ok_cb, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewVarItemList); -} - -bool dap_scene_config_on_event(void* context, SceneManagerEvent event) { - DapGuiApp* app = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DapAppCustomEventHelp) { - scene_manager_next_scene(app->scene_manager, DapSceneHelp); - return true; - } else if(event.event == DapAppCustomEventAbout) { - scene_manager_next_scene(app->scene_manager, DapSceneAbout); - return true; - } - } - return false; -} - -void dap_scene_config_on_exit(void* context) { - DapGuiApp* app = context; - scene_manager_set_scene_state( - app->scene_manager, - DapSceneConfig, - variable_item_list_get_selected_item_index(app->var_item_list)); - variable_item_list_reset(app->var_item_list); -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/scenes/dap_scene_help.c b/applications/external/dap_link/gui/scenes/dap_scene_help.c deleted file mode 100644 index d8d70e7ff..000000000 --- a/applications/external/dap_link/gui/scenes/dap_scene_help.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "../dap_gui_i.h" - -void dap_scene_help_on_enter(void* context) { - DapGuiApp* app = context; - DapConfig* config = dap_app_get_config(app->dap_app); - FuriString* string = furi_string_alloc(); - - furi_string_cat(string, "CMSIS DAP/DAP Link v2\r\n"); - furi_string_cat_printf(string, "Serial: %s\r\n", dap_app_get_serial(app->dap_app)); - furi_string_cat( - string, - "Pinout:\r\n" - "\e#SWD:\r\n"); - - switch(config->swd_pins) { - case DapSwdPinsPA7PA6: - furi_string_cat( - string, - " SWC: 2 [A7]\r\n" - " SWD: 3 [A6]\r\n"); - break; - case DapSwdPinsPA14PA13: - furi_string_cat( - string, - " SWC: 10 [SWC]\r\n" - " SWD: 12 [SIO]\r\n"); - break; - default: - break; - } - - furi_string_cat(string, "\e#JTAG:\r\n"); - switch(config->swd_pins) { - case DapSwdPinsPA7PA6: - furi_string_cat( - string, - " TCK: 2 [A7]\r\n" - " TMS: 3 [A6]\r\n" - " RST: 4 [A4]\r\n" - " TDO: 5 [B3]\r\n" - " TDI: 6 [B2]\r\n"); - break; - case DapSwdPinsPA14PA13: - furi_string_cat( - string, - " RST: 4 [A4]\r\n" - " TDO: 5 [B3]\r\n" - " TDI: 6 [B2]\r\n" - " TCK: 10 [SWC]\r\n" - " TMS: 12 [SIO]\r\n"); - break; - default: - break; - } - - furi_string_cat(string, "\e#UART:\r\n"); - switch(config->uart_pins) { - case DapUartTypeUSART1: - if(config->uart_swap == DapUartTXRXNormal) { - furi_string_cat( - string, - " TX: 13 [TX]\r\n" - " RX: 14 [RX]\r\n"); - } else { - furi_string_cat( - string, - " RX: 13 [TX]\r\n" - " TX: 14 [RX]\r\n"); - } - break; - case DapUartTypeLPUART1: - if(config->uart_swap == DapUartTXRXNormal) { - furi_string_cat( - string, - " TX: 15 [C1]\r\n" - " RX: 16 [C0]\r\n"); - } else { - furi_string_cat( - string, - " RX: 15 [C1]\r\n" - " TX: 16 [C0]\r\n"); - } - break; - default: - break; - } - - widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(string)); - furi_string_free(string); - view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); -} - -bool dap_scene_help_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void dap_scene_help_on_exit(void* context) { - DapGuiApp* app = context; - widget_reset(app->widget); -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/scenes/dap_scene_main.c b/applications/external/dap_link/gui/scenes/dap_scene_main.c deleted file mode 100644 index 8c19bd6a5..000000000 --- a/applications/external/dap_link/gui/scenes/dap_scene_main.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "../dap_gui_i.h" -#include "../../dap_link.h" - -typedef struct { - DapState dap_state; - bool dap_active; - bool tx_active; - bool rx_active; -} DapSceneMainState; - -static bool process_dap_state(DapGuiApp* app) { - DapSceneMainState* state = - (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); - if(state == NULL) return true; - - DapState* prev_state = &state->dap_state; - DapState next_state; - dap_app_get_state(app->dap_app, &next_state); - bool need_to_update = false; - - if(prev_state->dap_mode != next_state.dap_mode) { - switch(next_state.dap_mode) { - case DapModeDisconnected: - dap_main_view_set_mode(app->main_view, DapMainViewModeDisconnected); - notification_message(app->notifications, &sequence_blink_stop); - break; - case DapModeSWD: - dap_main_view_set_mode(app->main_view, DapMainViewModeSWD); - notification_message(app->notifications, &sequence_blink_start_blue); - break; - case DapModeJTAG: - dap_main_view_set_mode(app->main_view, DapMainViewModeJTAG); - notification_message(app->notifications, &sequence_blink_start_magenta); - break; - } - need_to_update = true; - } - - if(prev_state->dap_version != next_state.dap_version) { - switch(next_state.dap_version) { - case DapVersionUnknown: - dap_main_view_set_version(app->main_view, DapMainViewVersionUnknown); - break; - case DapVersionV1: - dap_main_view_set_version(app->main_view, DapMainViewVersionV1); - break; - case DapVersionV2: - dap_main_view_set_version(app->main_view, DapMainViewVersionV2); - break; - } - need_to_update = true; - } - - if(prev_state->usb_connected != next_state.usb_connected) { - dap_main_view_set_usb_connected(app->main_view, next_state.usb_connected); - need_to_update = true; - } - - if(prev_state->dap_counter != next_state.dap_counter) { - if(!state->dap_active) { - state->dap_active = true; - dap_main_view_set_dap(app->main_view, state->dap_active); - need_to_update = true; - } - } else { - if(state->dap_active) { - state->dap_active = false; - dap_main_view_set_dap(app->main_view, state->dap_active); - need_to_update = true; - } - } - - if(prev_state->cdc_baudrate != next_state.cdc_baudrate) { - dap_main_view_set_baudrate(app->main_view, next_state.cdc_baudrate); - need_to_update = true; - } - - if(prev_state->cdc_tx_counter != next_state.cdc_tx_counter) { - if(!state->tx_active) { - state->tx_active = true; - dap_main_view_set_tx(app->main_view, state->tx_active); - need_to_update = true; - notification_message(app->notifications, &sequence_blink_start_red); - } - } else { - if(state->tx_active) { - state->tx_active = false; - dap_main_view_set_tx(app->main_view, state->tx_active); - need_to_update = true; - notification_message(app->notifications, &sequence_blink_stop); - } - } - - if(prev_state->cdc_rx_counter != next_state.cdc_rx_counter) { - if(!state->rx_active) { - state->rx_active = true; - dap_main_view_set_rx(app->main_view, state->rx_active); - need_to_update = true; - notification_message(app->notifications, &sequence_blink_start_green); - } - } else { - if(state->rx_active) { - state->rx_active = false; - dap_main_view_set_rx(app->main_view, state->rx_active); - need_to_update = true; - notification_message(app->notifications, &sequence_blink_stop); - } - } - - if(need_to_update) { - dap_main_view_update(app->main_view); - } - - *prev_state = next_state; - return true; -} - -static void dap_scene_main_on_left(void* context) { - DapGuiApp* app = (DapGuiApp*)context; - view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventConfig); -} - -void dap_scene_main_on_enter(void* context) { - DapGuiApp* app = context; - DapSceneMainState* state = malloc(sizeof(DapSceneMainState)); - dap_main_view_set_left_callback(app->main_view, dap_scene_main_on_left, app); - view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewMainView); - scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)state); -} - -bool dap_scene_main_on_event(void* context, SceneManagerEvent event) { - DapGuiApp* app = context; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DapAppCustomEventConfig) { - scene_manager_next_scene(app->scene_manager, DapSceneConfig); - return true; - } - } else if(event.type == SceneManagerEventTypeTick) { - return process_dap_state(app); - } - - return false; -} - -void dap_scene_main_on_exit(void* context) { - DapGuiApp* app = context; - DapSceneMainState* state = - (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); - scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)NULL); - FURI_SW_MEMBARRIER(); - free(state); - notification_message(app->notifications, &sequence_blink_stop); -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/views/dap_main_view.c b/applications/external/dap_link/gui/views/dap_main_view.c deleted file mode 100644 index f54c5e3d5..000000000 --- a/applications/external/dap_link/gui/views/dap_main_view.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "dap_main_view.h" -#include "dap_link_icons.h" -#include - -// extern const Icon I_ArrowDownEmpty_12x18; -// extern const Icon I_ArrowDownFilled_12x18; -// extern const Icon I_ArrowUpEmpty_12x18; -// extern const Icon I_ArrowUpFilled_12x18; - -struct DapMainView { - View* view; - DapMainViewButtonCallback cb_left; - void* cb_context; -}; - -typedef struct { - DapMainViewMode mode; - DapMainViewVersion version; - bool usb_connected; - uint32_t baudrate; - bool dap_active; - bool tx_active; - bool rx_active; -} DapMainViewModel; - -static void dap_main_view_draw_callback(Canvas* canvas, void* _model) { - DapMainViewModel* model = _model; - UNUSED(model); - canvas_clear(canvas); - elements_button_left(canvas, "Config"); - - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, 0, 127, 11); - canvas_set_color(canvas, ColorWhite); - - const char* header_string; - if(model->usb_connected) { - if(model->version == DapMainViewVersionV1) { - header_string = "DAP Link V1 Connected"; - } else if(model->version == DapMainViewVersionV2) { - header_string = "DAP Link V2 Connected"; - } else { - header_string = "DAP Link Connected"; - } - } else { - header_string = "DAP Link"; - } - - canvas_draw_str_aligned(canvas, 64, 9, AlignCenter, AlignBottom, header_string); - - canvas_set_color(canvas, ColorBlack); - if(model->dap_active) { - canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18); - canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpFilled_12x18, IconRotation180); - } else { - canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18); - canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpEmpty_12x18, IconRotation180); - } - - switch(model->mode) { - case DapMainViewModeDisconnected: - canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "----"); - break; - case DapMainViewModeSWD: - canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "SWD"); - break; - case DapMainViewModeJTAG: - canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "JTAG"); - break; - } - - if(model->tx_active) { - canvas_draw_icon(canvas, 87, 16, &I_ArrowUpFilled_12x18); - } else { - canvas_draw_icon(canvas, 87, 16, &I_ArrowUpEmpty_12x18); - } - - if(model->rx_active) { - canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpFilled_12x18, IconRotation180); - } else { - canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpEmpty_12x18, IconRotation180); - } - - canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART"); - - canvas_draw_line(canvas, 44, 52, 123, 52); - if(model->baudrate == 0) { - canvas_draw_str(canvas, 45, 62, "Baud: ????"); - } else { - char baudrate_str[18]; - snprintf(baudrate_str, 18, "Baud: %lu", model->baudrate); - canvas_draw_str(canvas, 45, 62, baudrate_str); - } -} - -static bool dap_main_view_input_callback(InputEvent* event, void* context) { - furi_assert(context); - DapMainView* dap_main_view = context; - bool consumed = false; - - if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft) { - if(dap_main_view->cb_left) { - dap_main_view->cb_left(dap_main_view->cb_context); - } - consumed = true; - } - } - - return consumed; -} - -DapMainView* dap_main_view_alloc() { - DapMainView* dap_main_view = malloc(sizeof(DapMainView)); - - dap_main_view->view = view_alloc(); - view_allocate_model(dap_main_view->view, ViewModelTypeLocking, sizeof(DapMainViewModel)); - view_set_context(dap_main_view->view, dap_main_view); - view_set_draw_callback(dap_main_view->view, dap_main_view_draw_callback); - view_set_input_callback(dap_main_view->view, dap_main_view_input_callback); - return dap_main_view; -} - -void dap_main_view_free(DapMainView* dap_main_view) { - view_free(dap_main_view->view); - free(dap_main_view); -} - -View* dap_main_view_get_view(DapMainView* dap_main_view) { - return dap_main_view->view; -} - -void dap_main_view_set_left_callback( - DapMainView* dap_main_view, - DapMainViewButtonCallback callback, - void* context) { - with_view_model( - dap_main_view->view, - DapMainViewModel * model, - { - UNUSED(model); - dap_main_view->cb_left = callback; - dap_main_view->cb_context = context; - }, - true); -} - -void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->mode = mode; }, false); -} - -void dap_main_view_set_dap(DapMainView* dap_main_view, bool active) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->dap_active = active; }, false); -} - -void dap_main_view_set_tx(DapMainView* dap_main_view, bool active) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->tx_active = active; }, false); -} - -void dap_main_view_set_rx(DapMainView* dap_main_view, bool active) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->rx_active = active; }, false); -} - -void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->baudrate = baudrate; }, false); -} - -void dap_main_view_update(DapMainView* dap_main_view) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { UNUSED(model); }, true); -} - -void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->version = version; }, false); -} - -void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected) { - with_view_model( - dap_main_view->view, - DapMainViewModel * model, - { model->usb_connected = connected; }, - false); -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/views/dap_main_view.h b/applications/external/dap_link/gui/views/dap_main_view.h deleted file mode 100644 index 1fd900452..000000000 --- a/applications/external/dap_link/gui/views/dap_main_view.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include - -typedef struct DapMainView DapMainView; - -typedef void (*DapMainViewButtonCallback)(void* context); - -typedef enum { - DapMainViewVersionUnknown, - DapMainViewVersionV1, - DapMainViewVersionV2, -} DapMainViewVersion; - -typedef enum { - DapMainViewModeDisconnected, - DapMainViewModeSWD, - DapMainViewModeJTAG, -} DapMainViewMode; - -DapMainView* dap_main_view_alloc(); - -void dap_main_view_free(DapMainView* dap_main_view); - -View* dap_main_view_get_view(DapMainView* dap_main_view); - -void dap_main_view_set_left_callback( - DapMainView* dap_main_view, - DapMainViewButtonCallback callback, - void* context); - -void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode); - -void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version); - -void dap_main_view_set_dap(DapMainView* dap_main_view, bool active); - -void dap_main_view_set_tx(DapMainView* dap_main_view, bool active); - -void dap_main_view_set_rx(DapMainView* dap_main_view, bool active); - -void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected); - -void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate); - -void dap_main_view_update(DapMainView* dap_main_view); \ No newline at end of file diff --git a/applications/external/dap_link/icons/ActiveConnection_50x64.png b/applications/external/dap_link/icons/ActiveConnection_50x64.png deleted file mode 100644 index 1d7686dddf8a33b724c7528ed36435514b7518b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3842 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 G5}E*=05`}0 diff --git a/applications/external/dap_link/icons/ArrowUpFilled_12x18.png b/applications/external/dap_link/icons/ArrowUpFilled_12x18.png deleted file mode 100644 index dc481517ebae2d875880d96c3a6863c9ca985690..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eO!3HGrSK5O(jKx9jP7LeL$-D$|(mh=qLo_Cy zop_M%fC7he^yB~ki^>fomK}Y&?Q6<{^IZXxHgu>k9N2PUPNa~E?*^7bKA+0AH5l)R zS}i4d#BjFRWa(K?7>YJFIonF7y_qz@_V#wWmAe;3-!N4C_Ce~$H&1(?8x60XU0}74 WwRe~lwXYaxC4;A{pUXO@geCw-ib4GV diff --git a/applications/external/dap_link/lib/free-dap b/applications/external/dap_link/lib/free-dap deleted file mode 160000 index e7752beb5..000000000 --- a/applications/external/dap_link/lib/free-dap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e7752beb5e8a69119af67b70b9179cb3c90f3ac5 diff --git a/applications/external/dap_link/usb/dap_v2_usb.c b/applications/external/dap_link/usb/dap_v2_usb.c deleted file mode 100644 index cba786648..000000000 --- a/applications/external/dap_link/usb/dap_v2_usb.c +++ /dev/null @@ -1,977 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "dap_v2_usb.h" - -// #define DAP_USB_LOG - -#define HID_EP_IN 0x80 -#define HID_EP_OUT 0x00 - -#define DAP_HID_EP_SEND 1 -#define DAP_HID_EP_RECV 2 -#define DAP_HID_EP_BULK_RECV 3 -#define DAP_HID_EP_BULK_SEND 4 -#define DAP_CDC_EP_COMM 5 -#define DAP_CDC_EP_SEND 6 -#define DAP_CDC_EP_RECV 7 - -#define DAP_HID_EP_IN (HID_EP_IN | DAP_HID_EP_SEND) -#define DAP_HID_EP_OUT (HID_EP_OUT | DAP_HID_EP_RECV) -#define DAP_HID_EP_BULK_IN (HID_EP_IN | DAP_HID_EP_BULK_SEND) -#define DAP_HID_EP_BULK_OUT (HID_EP_OUT | DAP_HID_EP_BULK_RECV) - -#define DAP_HID_EP_SIZE 64 -#define DAP_CDC_COMM_EP_SIZE 8 -#define DAP_CDC_EP_SIZE 64 - -#define DAP_BULK_INTERVAL 0 -#define DAP_HID_INTERVAL 1 -#define DAP_CDC_INTERVAL 0 -#define DAP_CDC_COMM_INTERVAL 1 - -#define DAP_HID_VID 0x0483 -#define DAP_HID_PID 0x5740 - -#define DAP_USB_EP0_SIZE 8 - -#define EP_CFG_DECONFIGURE 0 -#define EP_CFG_CONFIGURE 1 - -enum { - USB_INTF_HID, - USB_INTF_BULK, - USB_INTF_CDC_COMM, - USB_INTF_CDC_DATA, - USB_INTF_COUNT, -}; - -enum { - USB_STR_ZERO, - USB_STR_MANUFACTURER, - USB_STR_PRODUCT, - USB_STR_SERIAL_NUMBER, - USB_STR_CMSIS_DAP_V1, - USB_STR_CMSIS_DAP_V2, - USB_STR_COM_PORT, - USB_STR_COUNT, -}; - -// static const char* usb_str[] = { -// [USB_STR_MANUFACTURER] = "Flipper Devices Inc.", -// [USB_STR_PRODUCT] = "Combined VCP and CMSIS-DAP Adapter", -// [USB_STR_COM_PORT] = "Virtual COM-Port", -// [USB_STR_CMSIS_DAP_V1] = "CMSIS-DAP v1 Adapter", -// [USB_STR_CMSIS_DAP_V2] = "CMSIS-DAP v2 Adapter", -// [USB_STR_SERIAL_NUMBER] = "01234567890ABCDEF", -// }; - -static const struct usb_string_descriptor dev_manuf_descr = - USB_STRING_DESC("Flipper Devices Inc."); - -static const struct usb_string_descriptor dev_prod_descr = - USB_STRING_DESC("Combined VCP and CMSIS-DAP Adapter"); - -static struct usb_string_descriptor* dev_serial_descr = NULL; - -static const struct usb_string_descriptor dev_dap_v1_descr = - USB_STRING_DESC("CMSIS-DAP v1 Adapter"); - -static const struct usb_string_descriptor dev_dap_v2_descr = - USB_STRING_DESC("CMSIS-DAP v2 Adapter"); - -static const struct usb_string_descriptor dev_com_descr = USB_STRING_DESC("Virtual COM-Port"); - -struct HidConfigDescriptor { - struct usb_config_descriptor configuration; - - // CMSIS-DAP v1 - struct usb_interface_descriptor hid_interface; - struct usb_hid_descriptor hid; - struct usb_endpoint_descriptor hid_ep_in; - struct usb_endpoint_descriptor hid_ep_out; - - // CMSIS-DAP v2 - struct usb_interface_descriptor bulk_interface; - struct usb_endpoint_descriptor bulk_ep_out; - struct usb_endpoint_descriptor bulk_ep_in; - - // CDC - struct usb_iad_descriptor iad; - struct usb_interface_descriptor interface_comm; - struct usb_cdc_header_desc cdc_header; - struct usb_cdc_call_mgmt_desc cdc_acm; - struct usb_cdc_acm_desc cdc_call_mgmt; - struct usb_cdc_union_desc cdc_union; - struct usb_endpoint_descriptor ep_comm; - struct usb_interface_descriptor interface_data; - struct usb_endpoint_descriptor ep_in; - struct usb_endpoint_descriptor ep_out; - -} __attribute__((packed)); - -static const struct usb_device_descriptor hid_device_desc = { - .bLength = sizeof(struct usb_device_descriptor), - .bDescriptorType = USB_DTYPE_DEVICE, - .bcdUSB = VERSION_BCD(2, 1, 0), - .bDeviceClass = USB_CLASS_MISC, - .bDeviceSubClass = USB_SUBCLASS_IAD, - .bDeviceProtocol = USB_PROTO_IAD, - .bMaxPacketSize0 = DAP_USB_EP0_SIZE, - .idVendor = DAP_HID_VID, - .idProduct = DAP_HID_PID, - .bcdDevice = VERSION_BCD(1, 0, 0), - .iManufacturer = USB_STR_MANUFACTURER, - .iProduct = USB_STR_PRODUCT, - .iSerialNumber = USB_STR_SERIAL_NUMBER, - .bNumConfigurations = 1, -}; - -static const uint8_t hid_report_desc[] = { - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x00, // Usage (Undefined) - 0xa1, 0x01, // Collection (Application) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xff, 0x00, // Logical Maximum (255) - 0x75, 0x08, // Report Size (8) - 0x95, 0x40, // Report Count (64) - 0x09, 0x00, // Usage (Undefined) - 0x81, 0x82, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x75, 0x08, // Report Size (8) - 0x95, 0x40, // Report Count (64) - 0x09, 0x00, // Usage (Undefined) - 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) - 0xc0, // End Collection -}; - -static const struct HidConfigDescriptor hid_cfg_desc = { - .configuration = - { - .bLength = sizeof(struct usb_config_descriptor), - .bDescriptorType = USB_DTYPE_CONFIGURATION, - .wTotalLength = sizeof(struct HidConfigDescriptor), - .bNumInterfaces = USB_INTF_COUNT, - .bConfigurationValue = 1, - .iConfiguration = NO_DESCRIPTOR, - .bmAttributes = USB_CFG_ATTR_RESERVED, - .bMaxPower = USB_CFG_POWER_MA(500), - }, - - // CMSIS-DAP v1 - .hid_interface = - { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = USB_INTF_HID, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT, - .bInterfaceProtocol = USB_HID_PROTO_NONBOOT, - .iInterface = USB_STR_CMSIS_DAP_V1, - }, - - .hid = - { - .bLength = sizeof(struct usb_hid_descriptor), - .bDescriptorType = USB_DTYPE_HID, - .bcdHID = VERSION_BCD(1, 1, 1), - .bCountryCode = USB_HID_COUNTRY_NONE, - .bNumDescriptors = 1, - .bDescriptorType0 = USB_DTYPE_HID_REPORT, - .wDescriptorLength0 = sizeof(hid_report_desc), - }, - - .hid_ep_in = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = DAP_HID_EP_IN, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = DAP_HID_EP_SIZE, - .bInterval = DAP_HID_INTERVAL, - }, - - .hid_ep_out = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = DAP_HID_EP_OUT, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = DAP_HID_EP_SIZE, - .bInterval = DAP_HID_INTERVAL, - }, - - // CMSIS-DAP v2 - .bulk_interface = - { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = USB_INTF_BULK, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = USB_STR_CMSIS_DAP_V2, - }, - - .bulk_ep_out = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = DAP_HID_EP_BULK_OUT, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = DAP_HID_EP_SIZE, - .bInterval = DAP_BULK_INTERVAL, - }, - - .bulk_ep_in = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = DAP_HID_EP_BULK_IN, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = DAP_HID_EP_SIZE, - .bInterval = DAP_BULK_INTERVAL, - }, - - // CDC - .iad = - { - .bLength = sizeof(struct usb_iad_descriptor), - .bDescriptorType = USB_DTYPE_INTERFASEASSOC, - .bFirstInterface = USB_INTF_CDC_COMM, - .bInterfaceCount = 2, - .bFunctionClass = USB_CLASS_CDC, - .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, - .bFunctionProtocol = USB_PROTO_NONE, - .iFunction = USB_STR_COM_PORT, - }, - .interface_comm = - { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = USB_INTF_CDC_COMM, - .bAlternateSetting = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_CDC, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_PROTO_NONE, - .iInterface = 0, - }, - - .cdc_header = - { - .bFunctionLength = sizeof(struct usb_cdc_header_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_HEADER, - .bcdCDC = VERSION_BCD(1, 1, 0), - }, - - .cdc_acm = - { - .bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT, - // .bmCapabilities = USB_CDC_CAP_LINE | USB_CDC_CAP_BRK, - .bmCapabilities = 0, - }, - - .cdc_call_mgmt = - { - .bFunctionLength = sizeof(struct usb_cdc_acm_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_ACM, - .bmCapabilities = USB_CDC_CALL_MGMT_CAP_DATA_INTF, - // .bDataInterface = USB_INTF_CDC_DATA, - }, - - .cdc_union = - { - .bFunctionLength = sizeof(struct usb_cdc_union_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_UNION, - .bMasterInterface0 = USB_INTF_CDC_COMM, - .bSlaveInterface0 = USB_INTF_CDC_DATA, - }, - - .ep_comm = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_COMM, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = DAP_CDC_COMM_EP_SIZE, - .bInterval = DAP_CDC_COMM_INTERVAL, - }, - - .interface_data = - { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = USB_INTF_CDC_DATA, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = USB_SUBCLASS_NONE, - .bInterfaceProtocol = USB_PROTO_NONE, - .iInterface = NO_DESCRIPTOR, - }, - - .ep_in = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_SEND, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = DAP_CDC_EP_SIZE, - .bInterval = DAP_CDC_INTERVAL, - }, - - .ep_out = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = HID_EP_OUT | DAP_CDC_EP_RECV, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = DAP_CDC_EP_SIZE, - .bInterval = DAP_CDC_INTERVAL, - }, -}; - -// WinUSB -#include "usb_winusb.h" - -typedef struct USB_PACK { - usb_binary_object_store_descriptor_t bos; - usb_winusb_capability_descriptor_t winusb; -} usb_bos_hierarchy_t; - -typedef struct USB_PACK { - usb_winusb_subset_header_function_t header; - usb_winusb_feature_compatble_id_t comp_id; - usb_winusb_feature_reg_property_guids_t property; -} usb_msos_descriptor_subset_t; - -typedef struct USB_PACK { - usb_winusb_set_header_descriptor_t header; - usb_msos_descriptor_subset_t subset; -} usb_msos_descriptor_set_t; - -#define USB_DTYPE_BINARY_OBJECT_STORE 15 -#define USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR 16 -#define USB_DC_TYPE_PLATFORM 5 - -const usb_bos_hierarchy_t usb_bos_hierarchy = { - .bos = - { - .bLength = sizeof(usb_binary_object_store_descriptor_t), - .bDescriptorType = USB_DTYPE_BINARY_OBJECT_STORE, - .wTotalLength = sizeof(usb_bos_hierarchy_t), - .bNumDeviceCaps = 1, - }, - .winusb = - { - .bLength = sizeof(usb_winusb_capability_descriptor_t), - .bDescriptorType = USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR, - .bDevCapabilityType = USB_DC_TYPE_PLATFORM, - .bReserved = 0, - .PlatformCapabilityUUID = USB_WINUSB_PLATFORM_CAPABILITY_ID, - .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, - .wMSOSDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), - .bMS_VendorCode = USB_WINUSB_VENDOR_CODE, - .bAltEnumCode = 0, - }, -}; - -const usb_msos_descriptor_set_t usb_msos_descriptor_set = { - .header = - { - .wLength = sizeof(usb_winusb_set_header_descriptor_t), - .wDescriptorType = USB_WINUSB_SET_HEADER_DESCRIPTOR, - .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, - .wDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), - }, - - .subset = - { - .header = - { - .wLength = sizeof(usb_winusb_subset_header_function_t), - .wDescriptorType = USB_WINUSB_SUBSET_HEADER_FUNCTION, - .bFirstInterface = USB_INTF_BULK, - .bReserved = 0, - .wSubsetLength = sizeof(usb_msos_descriptor_subset_t), - }, - - .comp_id = - { - .wLength = sizeof(usb_winusb_feature_compatble_id_t), - .wDescriptorType = USB_WINUSB_FEATURE_COMPATBLE_ID, - .CompatibleID = "WINUSB\0\0", - .SubCompatibleID = {0}, - }, - - .property = - { - .wLength = sizeof(usb_winusb_feature_reg_property_guids_t), - .wDescriptorType = USB_WINUSB_FEATURE_REG_PROPERTY, - .wPropertyDataType = USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ, - .wPropertyNameLength = - sizeof(usb_msos_descriptor_set.subset.property.PropertyName), - .PropertyName = {'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 'I', 0, - 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, - 'e', 0, 'G', 0, 'U', 0, 'I', 0, 'D', 0, 's', 0, 0, 0}, - .wPropertyDataLength = - sizeof(usb_msos_descriptor_set.subset.property.PropertyData), - .PropertyData = {'{', 0, 'C', 0, 'D', 0, 'B', 0, '3', 0, 'B', 0, '5', 0, - 'A', 0, 'D', 0, '-', 0, '2', 0, '9', 0, '3', 0, 'B', 0, - '-', 0, '4', 0, '6', 0, '6', 0, '3', 0, '-', 0, 'A', 0, - 'A', 0, '3', 0, '6', 0, '-', 0, '1', 0, 'A', 0, 'A', 0, - 'E', 0, '4', 0, '6', 0, '4', 0, '6', 0, '3', 0, '7', 0, - '7', 0, '6', 0, '}', 0, 0, 0, 0, 0}, - }, - }, -}; - -typedef struct { - FuriSemaphore* semaphore_v1; - FuriSemaphore* semaphore_v2; - FuriSemaphore* semaphore_cdc; - bool connected; - usbd_device* usb_dev; - DapStateCallback state_callback; - DapRxCallback rx_callback_v1; - DapRxCallback rx_callback_v2; - DapRxCallback rx_callback_cdc; - DapCDCControlLineCallback control_line_callback_cdc; - DapCDCConfigCallback config_callback_cdc; - void* context; - void* context_cdc; -} DAPState; - -static DAPState dap_state = { - .semaphore_v1 = NULL, - .semaphore_v2 = NULL, - .semaphore_cdc = NULL, - .connected = false, - .usb_dev = NULL, - .state_callback = NULL, - .rx_callback_v1 = NULL, - .rx_callback_v2 = NULL, - .rx_callback_cdc = NULL, - .control_line_callback_cdc = NULL, - .config_callback_cdc = NULL, - .context = NULL, - .context_cdc = NULL, -}; - -static struct usb_cdc_line_coding cdc_config = {0}; -static uint8_t cdc_ctrl_line_state = 0; - -#ifdef DAP_USB_LOG -void furi_console_log_printf(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); - -void furi_console_log_printf(const char* format, ...) { - char buffer[256]; - va_list args; - va_start(args, format); - vsnprintf(buffer, sizeof(buffer), format, args); - va_end(args); - furi_hal_console_puts(buffer); - furi_hal_console_puts("\r\n"); - UNUSED(format); -} -#else -#define furi_console_log_printf(...) -#endif - -int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size) { - if((dap_state.semaphore_v1 == NULL) || (dap_state.connected == false)) return 0; - - furi_check(furi_semaphore_acquire(dap_state.semaphore_v1, FuriWaitForever) == FuriStatusOk); - - if(dap_state.connected) { - int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_IN, buffer, size); - furi_console_log_printf("v1 tx %ld", len); - return len; - } else { - return 0; - } -} - -int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size) { - if((dap_state.semaphore_v2 == NULL) || (dap_state.connected == false)) return 0; - - furi_check(furi_semaphore_acquire(dap_state.semaphore_v2, FuriWaitForever) == FuriStatusOk); - - if(dap_state.connected) { - int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_BULK_IN, buffer, size); - furi_console_log_printf("v2 tx %ld", len); - return len; - } else { - return 0; - } -} - -int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size) { - if((dap_state.semaphore_cdc == NULL) || (dap_state.connected == false)) return 0; - - furi_check(furi_semaphore_acquire(dap_state.semaphore_cdc, FuriWaitForever) == FuriStatusOk); - - if(dap_state.connected) { - int32_t len = usbd_ep_write(dap_state.usb_dev, HID_EP_IN | DAP_CDC_EP_SEND, buffer, size); - furi_console_log_printf("cdc tx %ld", len); - return len; - } else { - return 0; - } -} - -void dap_v1_usb_set_rx_callback(DapRxCallback callback) { - dap_state.rx_callback_v1 = callback; -} - -void dap_v2_usb_set_rx_callback(DapRxCallback callback) { - dap_state.rx_callback_v2 = callback; -} - -void dap_cdc_usb_set_rx_callback(DapRxCallback callback) { - dap_state.rx_callback_cdc = callback; -} - -void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback) { - dap_state.control_line_callback_cdc = callback; -} - -void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback) { - dap_state.config_callback_cdc = callback; -} - -void dap_cdc_usb_set_context(void* context) { - dap_state.context_cdc = context; -} - -void dap_common_usb_set_context(void* context) { - dap_state.context = context; -} - -void dap_common_usb_set_state_callback(DapStateCallback callback) { - dap_state.state_callback = callback; -} - -static void* dap_usb_alloc_string_descr(const char* str) { - furi_assert(str); - - size_t len = strlen(str); - size_t wlen = (len + 1) * sizeof(uint16_t); - struct usb_string_descriptor* dev_str_desc = malloc(wlen); - dev_str_desc->bLength = wlen; - dev_str_desc->bDescriptorType = USB_DTYPE_STRING; - for(size_t i = 0; i < len; i++) { - dev_str_desc->wString[i] = str[i]; - } - - return dev_str_desc; -} - -void dap_common_usb_alloc_name(const char* name) { - dev_serial_descr = dap_usb_alloc_string_descr(name); -} - -void dap_common_usb_free_name() { - free(dev_serial_descr); -} - -static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); -static void hid_deinit(usbd_device* dev); -static void hid_on_wakeup(usbd_device* dev); -static void hid_on_suspend(usbd_device* dev); - -static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg); -static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); - -FuriHalUsbInterface dap_v2_usb_hid = { - .init = hid_init, - .deinit = hid_deinit, - .wakeup = hid_on_wakeup, - .suspend = hid_on_suspend, - .dev_descr = (struct usb_device_descriptor*)&hid_device_desc, - .cfg_descr = (void*)&hid_cfg_desc, -}; - -static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { - UNUSED(intf); - UNUSED(ctx); - - dap_v2_usb_hid.str_manuf_descr = (void*)&dev_manuf_descr; - dap_v2_usb_hid.str_prod_descr = (void*)&dev_prod_descr; - dap_v2_usb_hid.str_serial_descr = (void*)dev_serial_descr; - - dap_state.usb_dev = dev; - if(dap_state.semaphore_v1 == NULL) dap_state.semaphore_v1 = furi_semaphore_alloc(1, 1); - if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1); - if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1); - - usbd_reg_config(dev, hid_ep_config); - usbd_reg_control(dev, hid_control); - - usbd_connect(dev, true); -} - -static void hid_deinit(usbd_device* dev) { - dap_state.usb_dev = NULL; - - furi_semaphore_free(dap_state.semaphore_v1); - furi_semaphore_free(dap_state.semaphore_v2); - furi_semaphore_free(dap_state.semaphore_cdc); - dap_state.semaphore_v1 = NULL; - dap_state.semaphore_v2 = NULL; - dap_state.semaphore_cdc = NULL; - - usbd_reg_config(dev, NULL); - usbd_reg_control(dev, NULL); -} - -static void hid_on_wakeup(usbd_device* dev) { - UNUSED(dev); - if(!dap_state.connected) { - dap_state.connected = true; - if(dap_state.state_callback != NULL) { - dap_state.state_callback(dap_state.connected, dap_state.context); - } - } -} - -static void hid_on_suspend(usbd_device* dev) { - UNUSED(dev); - if(dap_state.connected) { - dap_state.connected = false; - if(dap_state.state_callback != NULL) { - dap_state.state_callback(dap_state.connected, dap_state.context); - } - } -} - -size_t dap_v1_usb_rx(uint8_t* buffer, size_t size) { - size_t len = 0; - - if(dap_state.connected) { - len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_OUT, buffer, size); - } - - return len; -} - -size_t dap_v2_usb_rx(uint8_t* buffer, size_t size) { - size_t len = 0; - - if(dap_state.connected) { - len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_BULK_OUT, buffer, size); - } - - return len; -} - -size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size) { - size_t len = 0; - - if(dap_state.connected) { - len = usbd_ep_read(dap_state.usb_dev, HID_EP_OUT | DAP_CDC_EP_RECV, buffer, size); - } - - return len; -} - -static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(ep); - - switch(event) { - case usbd_evt_eptx: - furi_semaphore_release(dap_state.semaphore_v1); - furi_console_log_printf("hid tx complete"); - break; - case usbd_evt_eprx: - if(dap_state.rx_callback_v1 != NULL) { - dap_state.rx_callback_v1(dap_state.context); - } - break; - default: - furi_console_log_printf("hid %d, %d", event, ep); - break; - } -} - -static void hid_txrx_ep_bulk_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(ep); - - switch(event) { - case usbd_evt_eptx: - furi_semaphore_release(dap_state.semaphore_v2); - furi_console_log_printf("bulk tx complete"); - break; - case usbd_evt_eprx: - if(dap_state.rx_callback_v2 != NULL) { - dap_state.rx_callback_v2(dap_state.context); - } - break; - default: - furi_console_log_printf("bulk %d, %d", event, ep); - break; - } -} - -static void cdc_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(ep); - - switch(event) { - case usbd_evt_eptx: - furi_semaphore_release(dap_state.semaphore_cdc); - furi_console_log_printf("cdc tx complete"); - break; - case usbd_evt_eprx: - if(dap_state.rx_callback_cdc != NULL) { - dap_state.rx_callback_cdc(dap_state.context_cdc); - } - break; - default: - furi_console_log_printf("cdc %d, %d", event, ep); - break; - } -} - -static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { - switch(cfg) { - case EP_CFG_DECONFIGURE: - usbd_ep_deconfig(dev, DAP_HID_EP_OUT); - usbd_ep_deconfig(dev, DAP_HID_EP_IN); - usbd_ep_deconfig(dev, DAP_HID_EP_BULK_IN); - usbd_ep_deconfig(dev, DAP_HID_EP_BULK_OUT); - usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_COMM); - usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_SEND); - usbd_ep_deconfig(dev, HID_EP_OUT | DAP_CDC_EP_RECV); - usbd_reg_endpoint(dev, DAP_HID_EP_OUT, NULL); - usbd_reg_endpoint(dev, DAP_HID_EP_IN, NULL); - usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, NULL); - usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, NULL); - usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, 0); - usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, 0); - return usbd_ack; - case EP_CFG_CONFIGURE: - usbd_ep_config(dev, DAP_HID_EP_IN, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); - usbd_ep_config(dev, DAP_HID_EP_OUT, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); - usbd_ep_config(dev, DAP_HID_EP_BULK_OUT, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); - usbd_ep_config(dev, DAP_HID_EP_BULK_IN, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); - usbd_ep_config(dev, HID_EP_OUT | DAP_CDC_EP_RECV, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); - usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_SEND, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); - usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_COMM, USB_EPTYPE_INTERRUPT, DAP_CDC_EP_SIZE); - usbd_reg_endpoint(dev, DAP_HID_EP_IN, hid_txrx_ep_callback); - usbd_reg_endpoint(dev, DAP_HID_EP_OUT, hid_txrx_ep_callback); - usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, hid_txrx_ep_bulk_callback); - usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, hid_txrx_ep_bulk_callback); - usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, cdc_txrx_ep_callback); - usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, cdc_txrx_ep_callback); - // usbd_ep_write(dev, DAP_HID_EP_IN, NULL, 0); - // usbd_ep_write(dev, DAP_HID_EP_BULK_IN, NULL, 0); - // usbd_ep_write(dev, HID_EP_IN | DAP_CDC_EP_SEND, NULL, 0); - return usbd_ack; - default: - return usbd_fail; - } -} - -#ifdef DAP_USB_LOG -static void dump_request_type(uint8_t type) { - switch(type & USB_REQ_DIRECTION) { - case USB_REQ_HOSTTODEV: - furi_hal_console_puts("host to dev, "); - break; - case USB_REQ_DEVTOHOST: - furi_hal_console_puts("dev to host, "); - break; - } - - switch(type & USB_REQ_TYPE) { - case USB_REQ_STANDARD: - furi_hal_console_puts("standard, "); - break; - case USB_REQ_CLASS: - furi_hal_console_puts("class, "); - break; - case USB_REQ_VENDOR: - furi_hal_console_puts("vendor, "); - break; - } - - switch(type & USB_REQ_RECIPIENT) { - case USB_REQ_DEVICE: - furi_hal_console_puts("device"); - break; - case USB_REQ_INTERFACE: - furi_hal_console_puts("interface"); - break; - case USB_REQ_ENDPOINT: - furi_hal_console_puts("endpoint"); - break; - case USB_REQ_OTHER: - furi_hal_console_puts("other"); - break; - } - - furi_hal_console_puts("\r\n"); -} -#else -#define dump_request_type(...) -#endif - -static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { - UNUSED(callback); - - dump_request_type(req->bmRequestType); - furi_console_log_printf( - "control: RT %02x, R %02x, V %04x, I %04x, L %04x", - req->bmRequestType, - req->bRequest, - req->wValue, - req->wIndex, - req->wLength); - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE | USB_REQ_DIRECTION) & req->bmRequestType) == - (USB_REQ_STANDARD | USB_REQ_VENDOR | USB_REQ_DEVTOHOST)) { - // vendor request, device to host - furi_console_log_printf("vendor request"); - if(USB_WINUSB_VENDOR_CODE == req->bRequest) { - // WINUSB request - if(USB_WINUSB_DESCRIPTOR_INDEX == req->wIndex) { - furi_console_log_printf("WINUSB descriptor"); - uint16_t length = req->wLength; - if(length > sizeof(usb_msos_descriptor_set_t)) { - length = sizeof(usb_msos_descriptor_set_t); - } - - dev->status.data_ptr = (uint8_t*)&usb_msos_descriptor_set; - dev->status.data_count = length; - return usbd_ack; - } - } - } - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_STANDARD | USB_REQ_DEVICE)) { - // device request - if(req->bRequest == USB_STD_GET_DESCRIPTOR) { - const uint8_t dtype = req->wValue >> 8; - const uint8_t dnumber = req->wValue & 0xFF; - // get string descriptor - if(USB_DTYPE_STRING == dtype) { - if(dnumber == USB_STR_CMSIS_DAP_V1) { - furi_console_log_printf("str CMSIS-DAP v1"); - dev->status.data_ptr = (uint8_t*)&dev_dap_v1_descr; - dev->status.data_count = dev_dap_v1_descr.bLength; - return usbd_ack; - } else if(dnumber == USB_STR_CMSIS_DAP_V2) { - furi_console_log_printf("str CMSIS-DAP v2"); - dev->status.data_ptr = (uint8_t*)&dev_dap_v2_descr; - dev->status.data_count = dev_dap_v2_descr.bLength; - return usbd_ack; - } else if(dnumber == USB_STR_COM_PORT) { - furi_console_log_printf("str COM port"); - dev->status.data_ptr = (uint8_t*)&dev_com_descr; - dev->status.data_count = dev_com_descr.bLength; - return usbd_ack; - } - } else if(USB_DTYPE_BINARY_OBJECT_STORE == dtype) { - furi_console_log_printf("BOS descriptor"); - uint16_t length = req->wLength; - if(length > sizeof(usb_bos_hierarchy_t)) { - length = sizeof(usb_bos_hierarchy_t); - } - dev->status.data_ptr = (uint8_t*)&usb_bos_hierarchy; - dev->status.data_count = length; - return usbd_ack; - } - } - } - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_INTERFACE | USB_REQ_CLASS) && - req->wIndex == 0) { - // class request - switch(req->bRequest) { - // get hid descriptor - case USB_HID_GETREPORT: - furi_console_log_printf("get report"); - return usbd_fail; - // set hid idle - case USB_HID_SETIDLE: - furi_console_log_printf("set idle"); - return usbd_ack; - default: - break; - } - } - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_INTERFACE | USB_REQ_CLASS) && - req->wIndex == 2) { - // class request - switch(req->bRequest) { - // control line state - case USB_CDC_SET_CONTROL_LINE_STATE: - furi_console_log_printf("set control line state"); - cdc_ctrl_line_state = req->wValue; - if(dap_state.control_line_callback_cdc != NULL) { - dap_state.control_line_callback_cdc(cdc_ctrl_line_state, dap_state.context_cdc); - } - return usbd_ack; - // set cdc line coding - case USB_CDC_SET_LINE_CODING: - furi_console_log_printf("set line coding"); - memcpy(&cdc_config, req->data, sizeof(cdc_config)); - if(dap_state.config_callback_cdc != NULL) { - dap_state.config_callback_cdc(&cdc_config, dap_state.context_cdc); - } - return usbd_ack; - // get cdc line coding - case USB_CDC_GET_LINE_CODING: - furi_console_log_printf("get line coding"); - dev->status.data_ptr = &cdc_config; - dev->status.data_count = sizeof(cdc_config); - return usbd_ack; - default: - break; - } - } - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_INTERFACE | USB_REQ_STANDARD) && - req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) { - // standard request - switch(req->wValue >> 8) { - // get hid descriptor - case USB_DTYPE_HID: - furi_console_log_printf("get hid descriptor"); - dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.hid); - dev->status.data_count = sizeof(hid_cfg_desc.hid); - return usbd_ack; - // get hid report descriptor - case USB_DTYPE_HID_REPORT: - furi_console_log_printf("get hid report descriptor"); - dev->status.data_ptr = (uint8_t*)hid_report_desc; - dev->status.data_count = sizeof(hid_report_desc); - return usbd_ack; - default: - break; - } - } - - return usbd_fail; -} diff --git a/applications/external/dap_link/usb/dap_v2_usb.h b/applications/external/dap_link/usb/dap_v2_usb.h deleted file mode 100644 index 3f1534ffd..000000000 --- a/applications/external/dap_link/usb/dap_v2_usb.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -#include -#include - -extern FuriHalUsbInterface dap_v2_usb_hid; - -// receive callback type -typedef void (*DapRxCallback)(void* context); - -typedef void (*DapStateCallback)(bool state, void* context); - -/************************************ V1 ***************************************/ - -int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size); - -size_t dap_v1_usb_rx(uint8_t* buffer, size_t size); - -void dap_v1_usb_set_rx_callback(DapRxCallback callback); - -/************************************ V2 ***************************************/ - -int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size); - -size_t dap_v2_usb_rx(uint8_t* buffer, size_t size); - -void dap_v2_usb_set_rx_callback(DapRxCallback callback); - -/************************************ CDC **************************************/ - -typedef void (*DapCDCControlLineCallback)(uint8_t state, void* context); -typedef void (*DapCDCConfigCallback)(struct usb_cdc_line_coding* config, void* context); - -int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size); - -size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size); - -void dap_cdc_usb_set_rx_callback(DapRxCallback callback); - -void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback); - -void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback); - -void dap_cdc_usb_set_context(void* context); - -/*********************************** Common ************************************/ - -void dap_common_usb_set_context(void* context); - -void dap_common_usb_set_state_callback(DapStateCallback callback); - -void dap_common_usb_alloc_name(const char* name); - -void dap_common_usb_free_name(); diff --git a/applications/external/dap_link/usb/usb_winusb.h b/applications/external/dap_link/usb/usb_winusb.h deleted file mode 100644 index 9c3a172dc..000000000 --- a/applications/external/dap_link/usb/usb_winusb.h +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once -#include - -/*- Definitions -------------------------------------------------------------*/ - -#define USB_PACK __attribute__((packed)) - -#define USB_WINUSB_VENDOR_CODE 0x20 - -#define USB_WINUSB_WINDOWS_VERSION 0x06030000 // Windows 8.1 - -#define USB_WINUSB_PLATFORM_CAPABILITY_ID \ - { \ - 0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c, 0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, \ - 0x9f \ - } - -enum // WinUSB Microsoft OS 2.0 descriptor request codes -{ - USB_WINUSB_DESCRIPTOR_INDEX = 0x07, - USB_WINUSB_SET_ALT_ENUMERATION = 0x08, -}; - -enum // wDescriptorType -{ - USB_WINUSB_SET_HEADER_DESCRIPTOR = 0x00, - USB_WINUSB_SUBSET_HEADER_CONFIGURATION = 0x01, - USB_WINUSB_SUBSET_HEADER_FUNCTION = 0x02, - USB_WINUSB_FEATURE_COMPATBLE_ID = 0x03, - USB_WINUSB_FEATURE_REG_PROPERTY = 0x04, - USB_WINUSB_FEATURE_MIN_RESUME_TIME = 0x05, - USB_WINUSB_FEATURE_MODEL_ID = 0x06, - USB_WINUSB_FEATURE_CCGP_DEVICE = 0x07, - USB_WINUSB_FEATURE_VENDOR_REVISION = 0x08, -}; - -enum // wPropertyDataType -{ - USB_WINUSB_PROPERTY_DATA_TYPE_SZ = 1, - USB_WINUSB_PROPERTY_DATA_TYPE_EXPAND_SZ = 2, - USB_WINUSB_PROPERTY_DATA_TYPE_BINARY = 3, - USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_LITTLE_ENDIAN = 4, - USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_BIG_ENDIAN = 5, - USB_WINUSB_PROPERTY_DATA_TYPE_LINK = 6, - USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ = 7, -}; - -/*- Types BOS -------------------------------------------------------------------*/ - -typedef struct USB_PACK { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t wTotalLength; - uint8_t bNumDeviceCaps; -} usb_binary_object_store_descriptor_t; - -/*- Types WinUSB -------------------------------------------------------------------*/ - -typedef struct USB_PACK { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bDevCapabilityType; - uint8_t bReserved; - uint8_t PlatformCapabilityUUID[16]; - uint32_t dwWindowsVersion; - uint16_t wMSOSDescriptorSetTotalLength; - uint8_t bMS_VendorCode; - uint8_t bAltEnumCode; -} usb_winusb_capability_descriptor_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint32_t dwWindowsVersion; - uint16_t wDescriptorSetTotalLength; -} usb_winusb_set_header_descriptor_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t bConfigurationValue; - uint8_t bReserved; - uint16_t wTotalLength; -} usb_winusb_subset_header_configuration_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t bFirstInterface; - uint8_t bReserved; - uint16_t wSubsetLength; -} usb_winusb_subset_header_function_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t CompatibleID[8]; - uint8_t SubCompatibleID[8]; -} usb_winusb_feature_compatble_id_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint16_t wPropertyDataType; - //uint16_t wPropertyNameLength; - //uint8_t PropertyName[...]; - //uint16_t wPropertyDataLength - //uint8_t PropertyData[...]; -} usb_winusb_feature_reg_property_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint16_t wPropertyDataType; - uint16_t wPropertyNameLength; - uint8_t PropertyName[42]; - uint16_t wPropertyDataLength; - uint8_t PropertyData[80]; -} usb_winusb_feature_reg_property_guids_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t bResumeRecoveryTime; - uint8_t bResumeSignalingTime; -} usb_winusb_feature_min_resume_time_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t ModelID[16]; -} usb_winusb_feature_model_id_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; -} usb_winusb_feature_ccgp_device_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint16_t VendorRevision; -} usb_winusb_feature_vendor_revision_t; \ No newline at end of file diff --git a/applications/external/hid_app/application.fam b/applications/external/hid_app/application.fam deleted file mode 100644 index a9d8305dd..000000000 --- a/applications/external/hid_app/application.fam +++ /dev/null @@ -1,24 +0,0 @@ -App( - appid="hid_usb", - name="Remote", - apptype=FlipperAppType.EXTERNAL, - entry_point="hid_usb_app", - stack_size=1 * 1024, - fap_category="USB", - fap_icon="hid_usb_10px.png", - fap_icon_assets="assets", - fap_icon_assets_symbol="hid", -) - - -App( - appid="hid_ble", - name="Remote", - apptype=FlipperAppType.EXTERNAL, - entry_point="hid_ble_app", - stack_size=1 * 1024, - fap_category="Bluetooth", - fap_icon="hid_ble_10px.png", - fap_icon_assets="assets", - fap_icon_assets_symbol="hid", -) diff --git a/applications/external/hid_app/assets/Arr_dwn_7x9.png b/applications/external/hid_app/assets/Arr_dwn_7x9.png deleted file mode 100644 index d4034efc432b102f6f751d001dc6891b0763c55c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3602 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 JTZ;Aw|2NmVF(Lo} diff --git a/applications/external/hid_app/assets/Arr_up_7x9.png b/applications/external/hid_app/assets/Arr_up_7x9.png deleted file mode 100644 index 28b4236a292708b412629ffafe30f6d011491505..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3605 zcmaJ@c{r47|9>2^ZpP~Xl@BrVPMsS}}K`)IgU>xHk zuQ{^ZlqErKm`jmL$vXO)Qi=!THE;DRyVh>Cu@O^m&WRUIOpLs&>}nu;QTm<4xaRG| z^LOGewyt~#yA#k?we+cd{qb9i$>Mo_d8b5;q-?6ak*i6hYyoEX*7xU|8X7;0L#(2t zwb_88WI07MXiZB5SdK6^-v_Rdcn*jJ_sB>BHTbL=*siz@g)f+lqau+PL~6Ln`yC}C zl>n>IM9e+F%2p(jpRVH$QdDDFIb(FP#G03|=i1|;y#5P&&&`q={yo&Yr+iZW$@q$~h)jgQ$2h=l<@&01Q) zz=aGz$#%}u{EvO5ij(@nN@bLpS7;+`qP!&y10_5?A-nZD98~uynUa1XWm-Y%LNe44 zQN{}I=U)LpPO`Ev+xfNN4*AlK4%0+|{0YM^FT^*%zP@AY6P-nDD**Vwjp$l8fR^u! zJRly)SiikzM$G@XOwQ@0OMYbvR*!+4sR7S<_GWEtZe6M9@1GbSe|N9}<4tPy3}2_! zov86#JN0LT`RdZ*`{y6EqY%fU?8KJe*S%VB%H7p@RqBH8(5EE3)h99=s~SDv1_$2? zqQ26Y>$bo|T;}C@L@qc1b9L{_J>46WkD~@Fq86hjz=M+(B4Npf`Nznj-yC%niQJlx zO8_ue$*O&$Cn*}~fBr)!Z)4VS%`RsT5b5V|H4p%f?2-LmfsDBTb3i#qrr&9F5V7ZGWJl?*n~frD0s->K~iJmWR}N zJe5bY6~2=svupLLqNK#En)?ZviT(gwA}E4hLllTGa5 zZWjq44||O{H0Kv&+)>+S$p@MNMD%KGl^y(ARGBOKjqGD=MZVe23%0jqUQ@X6%p{eZ ztk;}JJJFX-Z%w`~@>dv0vcNXMYCi9fFlsmjgEZD-9_}}gN+GvB1Q*K|HSTvGJ3i8Rw)M}3 z9li*79MRrDt8ZJ>$ZH0mea$iB{PFs6qjB|d%{gyrzOPl_-DUTWdTy;J52{TlP8d&!Q_~UF9(OX` zhVyR`wwfdz!Iaz*xZQV+%inH%IuqG`Ud6#Nx8(Nqo}K=x{!8@xpSjPr4qxBxoc7wY zyKTzubJ}Oo1)i*2tn&G$c$%JC)((jsG&SCi`{_>i)Os$dH4$KD@UQ8U844LJ52C(6 z|EzLytMv7Q*LAL|>q7|zh4%_a3S~UzJ=zFK1;^dPOKm-j+{X%}-lP_J6!H&!bys(% z6&%QqE2QPK2$pvvyw(!Lz3QFnU9fjua~_@;t7-(vkk!hA4KxGfiegVknKbA;Z0|pN zM!zzBO{4M>y0G9D5^HqO$g|vS{+geq#8`UZ@(r%D)TCZs+I+;t5vAF^ANQ)?Gj^(g zQ;!A|rlzG5i|mVBi|oEuo0d-J@$XgJRC=vM$y+xa)IF+eM@#D1!k={ScOTA^&Qrmo zQH!OJ!hl@$Ta`H83ufL-diL|*U{8* z#DBrhWV+!i?(MyI!0CWfQ~Rs-+wFZBCRu3sTf}76WY*iP(I-Aff{z#o@&!++4rSv< z?s?4!s+ciHkY2e&k0Zy*ZAL2_eXb}`VQF}1)PJFOb zzz~F!XuhhnCofCuXHu$D!k>lzwuY9Fi|dy!(m0|K5%h?oggT5G$?Ui>V;TN(A$1B$ zBX%lwzB3vVY;W7!K_1Mu=X%#`|=i@IWI7YWY(kviZ>W#zA)#C@bi-E^Jgmy3T zv&ysTrt=5y&zR28XX1u#zB0bKH`~i7=yiQF_Py&wm!-_j>#%^);s_V4OBC(#q!yG6 zP4+B#``}3~uW*Spt7`Ghf^&1sV$9rZ1To@u;+0v=ljbLFF7>SJ6EUOMb6OjejnIuQ zATM%{2u(C0$~wyXmzCwvvzjjwEm4EiZ)N?{)|YcCtd*^kqD!JDYD+Zzn}5GjqPaAg z-jUovmybCV@wxA{1nCp$QhkK1ZcJQ^XRKu+JD#|+3!Y}e>l(rajpDxJQgI_$G`I`$ zzTrU=eTzcKN%H}-XU5Mg8zFvPuX>4mqQfc2T}X(2sVVc+^U>Am`M8h#k1}Ins_D?? zW9*Py9d!#ac`5~vZ3d`RE2ntp{n!3wt*D=`a(U0(cHW*u>5w{&IvN<-W!e@04trF8 zxAUC6K0fs7@5xmrA=)pEat$UbF6b6qsdAEY8qPvxt7M)5F%W1}HT?Y5i5-kDcSBkfI8A=N<_dXMj=)KjKD5Ft5{a&;uv?5cB zviG%5zbbDXykd4^_U6X)wz_Q}t_pHv9X$;-h@Yy9Pa@0A149O-$CS71i#;q}Z2t73 zK%dd;QZ((ERvJ;Q6N(RrI$qlvUHe!h;H!*>^h8Yf*P*x5$6Sa|uhGY(@3DM!3+051 zrAmXUY0Br`=?w)>sK>EdUt|njdsI-=P(kVR>-L-aG-8 z_YQhjEv;F!JRkHB@xb@`^-@oYq zy3qu;q`rM$?c|$&eZJ10~S zzMj(K(o}h)GPAVeXh6kGX!YYTzojYlY_pExh3b$$R5tp0vytfG>iJOC(#xgAQI+8c zj_z7VTV+2_cc!GurRv0j)wFd#b~vur(tCaA-R#i0lQq1Y`K}?mCGnW^o$JYqNeb94 zNf}9Pv2w9rv-evdksmENYg4Ov*iK5PPPXd$?e(@&RTXH&a_`r-9bM^Nx6(?43~sm+`Zpb9x*8e?DAvf1S6IqLz}f zAtstWzdCDjEn4_rsm8S-a@|>eTpo!-1*|D7UnJ$N^L?$d^i^GtuDL$`@b|oq`5?n&4r0HkRs7w-4n| z-9w!Tzk^;800GS7)gaQmImjnuCoMHx{g3;i=bWy_frWpzb{RQC$puztMiikf1 z!m>D2kQoGSNQS{+ATuO{N+BV9jr>St0}uj+fJ5QJ$IK9JhC&#j;7HKl11xmNq4=TP zaJGND6YkJpe=e7eftxd%|(NS!Tu);2KygbX3*c264neFOkzXf5ZGo`KY)1r|AsOc|Dc1o zZq)zA`~M0D5klBhs2eqib(%vKo}Hi8rYklI%b}9EEDnLiI`yNFhx}PwR**l74MG?} z;2=FbiA-m1TK4`$!Q)X5%pfj_Nv1mB&|skmgifcR%;2U*FcU1!2#Z0&;WoJaSgaY= z2xEf7?Z;qEk(eJ`9E*IKL1l7(a4G-g+WeHe*$@o2&@+z8p`W2rY&k3j=&!6%^q)gyir`390UNO7E~>RB=X1PyRqDR|dedGy-I3dS}z{FW`l zMNSyxg1Htho2ag(A|ib>R^?v5oOA7N3kw0I=B!x$`1tVaa?aY~S4I1TCROgoUw#mK zwRK}G^nw3}s#n;`x{ZyFXoSYG@prgqTH$sxbj+ z;W8hUz)e*?U_A_lIt;E6dIj(W^@s@rHTD@bu>CRHQeQA>C-}mz@YS#rjctX)WdXC0 zcuWppX2}=MO;vXVvIGFHHj?)Q;G_e1X7n-P(cap^a)mB5Az^)lz1AwJU zM(uk|Vg7Kx%VV9K?M2f~tE_`SxUbF40020JQ-k1J%S@Yu0RWd3q4n5YX{C0rc8%cv z+Fe7nV&AM+t6QJ?VrEU!aFkr>VB_Q%RvUeNbu%KA0Ve$h!xNl2aB3rRFn z>KjowvsSYzLPWs4S$GdoWgwQ%`zk>-URWV5YF(w)T0rKS8mJ{!)){P@XkZO@xrzt5 zSt~E0SwA6SPFTK7Jkkv4Mt+a3vVz}=D0N1^7k`GW$TQk^#qz$`J0CVYJwZMz;~nei zKJ<0Ndo%9}{iFsGOt4L`n$LTM^cv2>AdU5yC&t<$Nu;(X;3DzD#(j^E74cWbt&%#Q za0Fx`ENVmy1vnTG@qoEC!H(e2XPpPyucp6yK*UId|B7>+1~@6t_Nn^I-M=^N_11;Q z5UjOTKgcBPfl7zQVjGOqWa6;88WlHwvU&0l-!0Q^*-dv*oz>3I(6`>Fn$$Aj<6kO- zxTOs`+#EH@ovfeKn^c-qS@IO+dYc72Tz4JUbZI?vRB=jrN`Fd_oT_W?_8{G5IPV^Q zw?V>jO!2*Pmq*Sqd3*HFr6bxe%iGvy7vI0#v(Hb#Z;krsGyCQ4;oAosQr@|Dx6N98 zPWjBg!V#B&r?OXhRAIn@@G9vcyo=1oU6PH0$B5;}HqXI%SThjT@9U7hhidWfLtV5z{YOsC-;GEbu8y7I_RglHPG=!Sv#rmE>6{h0rP8 z*{3&AzNhU_1C{HV(PKqXpi~52UXHyMXB*iDNil(BC^Zf@S5F>guLhhP3+Z0vW|U>r z&F2k1S}P^O1o;Jf-}>?h}`E>p3)w_*OHMPZIu#|X-^8C56=n&@8q z@$vI)PQe;+QNiS^3G42J$pp%1M0dpF^jo8v=grUC9P1gGr=v!(msGcXwnMhNfZXtd zd=&n;2=fTfpElM*E~vbYH$@JTzn1pTn_thWFqbn=h%Anrsx4OWYyR~{vC7&^YDZ!R zRWiyc?DL0rLd0p}wfZn|ji{I?_h{32W-MV}7d*v)(=~(*9L0UZCF4diC~!x_Bb}oL zS|$aMGpGThm-;VF8zH_PZ+i(`g3Vdm{RoIwi6Q;$tI_ZC%Q55Jaj}U|g;Z$sNoMf9 zj=GhoT={&6j5ada%r4f!_}0J7rM2?puOD36!#Nl)8eFGbM*%~-47+0cuqU(*I4oIf z*@xWxHL=PdSnZ8ow)RxT6^;BGRdy0~!x_j-`SkN3nl2hy4ZnOd@kRiqK*c_(obrV- z?R&nhh#XbA^@e`!IrPA7p%(wL8%4W3bVSQBIiK;zH9u+zl~Ty=zOUQkS`o>GnTOlw z-hs$i-bPksVY> zk-OBVITSRd6vJqJoi=pqX?|ftg-@q%x9{xqh)$-bWO6~ubc!ThqJQA2#OSf7^Q&Ji z2B9hKnuC>>%dr&?UZY-Ak#k!*+K-sxAL3W=-|&VD-NVm_AJ^$!3re9?U-f_O9rUbP z+car;HR#6YX5Z`EOWv^AC|ffvi7S|0Pu`%NEOwv;%s26O^KS~NN|t}Dc;BnsjmEnq zd^kL3CE4`zt1a##M@Pa?!tIwkjpM3JT=3-Vn#kzd0SV;5`Rk!YV?sSYpI4?RL(gE+ zm(ndWT+=r^y**z#zBTFk@MR?AyVc;&Qg`%G9>GVK@h#MW*~p$G%2MZb?rrYHFv#yi zUW50`LuW`Gqi3WTi!Y_wW8D_p*Jh4X9qBl+^n$%qIykk*{e^q_Bjjn?7xov_R#J~+ zQ{|n?^pc7b{uK)$)z3nG*JhP6jXH)`s)K)%-~P~>i9iomFNZMJ-mI;T$`6OJG&Vch zD*HJa3&mBARi{_X=FR)D!!f<4o?AnGi$j;r)NrzvyN0aR1fwo@ZY8cJNMUy+q$RXP zOGM9Q8k-;x^jrm&65J!3O!Kjqu1UA9m4oPCr zAjBOXNDz(5LjwTHG>Azg`IFfoZ!(2SM}rqDUxPtZA2itAz#eAL#FG7})*&piYls7$ z6yi@p_<&7KK&T)jkAOyI6G1_=v-Ch@5E}dkFOs+3F+;(iKU~=UXz-t+2=-1OEQ3V` z8A0GWBp3_^GD1MeK15w_JzpY88>9=W8Df{r`8R(f;-hWV?|6 zqxT<)1M$I3GSr0}$T-I$@y^aybte=PiDi+AYz7O@V4VF?NGCrAn-S>8V1jh@AaIbT zJ&{DE?^q7~0kOA7+Ry{pL^_FVgF}OPBoHdq2Wbp5#~AYlILs0bhg;yx4UCM<4Y3%w z0TyRyY-#=ji(`<^(a3c653J9Bu$cde-DwCKlNT9BW>L?ReJoiF8t9L#k<@?CVri+5 z(>FGv;F0+i-FzIXDyH_N{#SL z^YvxW03Nin1C!rAXaP7WMBc(j6m!G#0-up`AMk?0U7xv`NbLe1qw#SdWH%b zzKO}1c_0x@pc3WdxE(xJ7xz zP+tN4r(cm+pl_&Wpbs}0sL=-KM=R%|)WnkfqLBRj96LgRY@?5^1L_1DeUQ75+zAN; zuqZGT?6`nBVIgYAb%S||8!(VPJY5_^IAl}%*|``Lc$i=Rx6f4*(G6O!%$6?Eu2`g+ z_E)I!3HFqj;YoHDIHH2#}J9|(o>FH3<^BV2haYO z-y5_sM4;GPjq%Ck6>60csmUj6EiNa>ORduPH4*)h!w|e3sE@(Z)z4*}Q$iC10Gods AV*mgE diff --git a/applications/external/hid_app/assets/ButtonF10_5x8.png b/applications/external/hid_app/assets/ButtonF10_5x8.png deleted file mode 100644 index d1a7a04f06dcc4b5446aad5a40a9863038bf56b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA~jDJ#}JO|$tewu|Ns9tHZU+a6e)0^L#NYq9;5F(PTxtKzKT}z3_A)0e;QviHwNlp N@O1TaS?83{1OPU#E%g8Z diff --git a/applications/external/hid_app/assets/ButtonF11_5x8.png b/applications/external/hid_app/assets/ButtonF11_5x8.png deleted file mode 100644 index 7e177358e81695342f2a283a220c7cacc7bda939..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOpBPSmNg(OQ{BTAg}b8}PkN*J7rQWHy3QxwWGOEMJPJ$(bh8~Mb6 ziqt(_978y+C#N(t{{R2q*ucQxP^7?t4xLU{IYmyznHN-MN(3-k$uq=X7x{LAUCkJ% Og~8L+&t;ucLK6T7Y%hHP diff --git a/applications/external/hid_app/assets/ButtonF12_5x8.png b/applications/external/hid_app/assets/ButtonF12_5x8.png deleted file mode 100644 index 50d2a7dc63b9d366ccfbacbc05e6bb0d9e335b5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk)EfEV+hCf+#W|R1_Pc$J^%l2ww|&zRcE|OcGEe{j diff --git a/applications/external/hid_app/assets/ButtonF1_5x8.png b/applications/external/hid_app/assets/ButtonF1_5x8.png deleted file mode 100644 index 7394d27105fd0a27067803bfd633a26bedd0f1d5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|$tewu|Ns9tHZU+a6e)0^L+9jy0|#0HPM+X+s?4mGa`wiPi$55Iw(~PB TTpxE5sExtX)z4*}Q$iB}`k6I% diff --git a/applications/external/hid_app/assets/ButtonF2_5x8.png b/applications/external/hid_app/assets/ButtonF2_5x8.png deleted file mode 100644 index 9d922a3858147116d65b6f03e2b36ea846b2f60c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk*=qUV+hCf)NV&U1_ho&w~qX;m*hWYIaS!#v1}4USoB8kx*gxNJa@^Tf4zarr_o#d UH)s04hd_-Cp00i_>zopr0BX-NbN~PV diff --git a/applications/external/hid_app/assets/ButtonF3_5x8.png b/applications/external/hid_app/assets/ButtonF3_5x8.png deleted file mode 100644 index 95c2dd4f4198e182a1a62927c4d3627400a7b883..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk&dT}V+hCf)GkXd1_g%0LLdIeuWPOlF*aSY$&;z#+9C5#-hV?Uh3H=_oX_AhhhO~n UO!>#_f%+IcUHx3vIVCg!073*Z7ytkO diff --git a/applications/external/hid_app/assets/ButtonF4_5x8.png b/applications/external/hid_app/assets/ButtonF4_5x8.png deleted file mode 100644 index 602466f4b667b6df4d5335517bd9d43e5f0b6e69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|vE3Va85}s64*o6Qr{DAJBNr3n8pa7vN_+nsOQj%x4ZT`lf}PS TtvtgA)W+cH>gTe~DWM4fb!sy& diff --git a/applications/external/hid_app/assets/ButtonF5_5x8.png b/applications/external/hid_app/assets/ButtonF5_5x8.png deleted file mode 100644 index d73b5405275f6c53f7a2f157df063db1ca351caa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOFVdQ&MBb@0Fom!%>V!Z diff --git a/applications/external/hid_app/assets/ButtonF6_5x8.png b/applications/external/hid_app/assets/ButtonF6_5x8.png deleted file mode 100644 index c50748257ab8e06f90007e93b913d5be4999d096..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOA;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|shy5|3<^BWD?a{@Kh_*L{p89i1p$qBwtDvdG5Wpwnq5@=JeG)jb@A^; T#rkJ}+88`t{an^LB{Ts5$FwwN diff --git a/applications/external/hid_app/assets/ButtonF7_5x8.png b/applications/external/hid_app/assets/ButtonF7_5x8.png deleted file mode 100644 index 396c98f5104f94b6310593ce6c7e6ce3d2369ef3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO+nf~Hxmr&P}udD~(3jVRo RuLQY`!PC{xWt~$(69B*FH3|R# diff --git a/applications/external/hid_app/assets/ButtonF8_5x8.png b/applications/external/hid_app/assets/ButtonF8_5x8.png deleted file mode 100644 index 6304d7fb888b2cf38c54e7124aaa604d1610629c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA}voB#}JO|wVjT93<^BWEB^mCmh0Jd#>H=GOE1@xHLh7tre2KydVRe*qgvi_@vq`% Sf29C*F?hQAxvXEZzkxv|` zNY~TFF@)oKa!Nzv|NsAu4GatpMG73~&^g&~GSNx=@BjbyU3DUS%F5hxSQ8l-I!}xL U>{@7g2&j?4)78&qol`;+0Ic0IyZ`_I diff --git a/applications/external/hid_app/assets/ButtonLeft_4x7.png b/applications/external/hid_app/assets/ButtonLeft_4x7.png deleted file mode 100644 index 0b4655d43247083aa705620e9836ac415b42ca46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1415 zcmbVM+iKK67*5rq)>aU2M7$VM1Vxif;vTv~W2u`S7ED{V3s&&L*<`XiG|9wd+THd> z5CnY!sdyuJtrvQyAo>KpiLcV|{Tkc)riAbluXfwSZCApL`ztB&p zx6LGKvks4K_4~)qD&oGa-YdJlW)hAKMNJd7<=t?6c^RI1>c$ifyjaM>^|&8!ey zB4!nh9u>5uen6Ve@<H5rru6h<2Ef#GQdQ*CmZOlQi~N!?9H`Rp;C% zU}CB21#?;r`&0|6C0}b-=jODa5|nEJ#ntxQ&{~jpgtwDta4hftr~G=#p@V36e4Zjh zq%J~{y26Jjn=1Nw-l*3%QW5YFE*v4z3gt0$&(*xf2en34c?JpH8+FYldo+Alvg8af-pG4(=!fyUi-Wsg z`g#n9VUcf(DFr{poMSNzw-lz>w+HV+n1ELr&SLA#LHUb0p(xWQ(1*vJ-i+1!`swxZ Z!O7;c$;lT_->m1Ovaz)0yuI`A$q$F8u*d)a diff --git a/applications/external/hid_app/assets/ButtonRight_4x7.png b/applications/external/hid_app/assets/ButtonRight_4x7.png deleted file mode 100644 index 8e1c74c1c0038ea55172f19ac875003fc80c2d06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1839 zcmcIlO>f*p7#Yw)M6zw!O+@VZ{?d|D~WYi~8rHRY?X-&T}Yen`g$^+EJ;z+|RV zE@PoDvZ9%#+_}3bC_5Cj8jDGq541mi{7F+&KF}W65sr$Xn5H|YrMQ2(J7%Yc%;(zO z57ax000=TsQ+1Ke@+w#iw3au3cGGQWY740k2ijH>P(6tD)S)be>gX6Tj7`<`b>di- zgWp$8Y+?i31~CzF0&E4uRlA=C(Mp~K`{74jEchB|)4DDK!ZVhSwdFyw0YIZ1cDh0S{OvfO-U_~ zvmRF*m9sWDXNH)GOyqS1Skhxbr6}s*7t&@~kFM(NW5}qh?Lu@lJ}HE;FDiLdGO>LO z5pS*%E2grR)l^;|?O5b_?u0me&c1U}%jrk8*%=Wk%i)8yp2P|kuxmKg<=(u_`oQRI_0 zS`-DNysBx=#3&qSkgA@hJP>~D+ZM(s5jI6Owp`?yE=3e`YGUqkVOp#Cp=3wR3O4hX zX6BLsN3UBzV(vI5;|SZHgOb=HD0VFjpTyfFW}GnQuh>2*Q`k>*cAmA#iUT7EXSpo# zkPm5~#I-o^cpgfe#P$=4-Pi*SpT!-@nJgp8L347xe>5EKl`=_ZFc8XGy+_j=_R_7! z@vZZMowS1GJ?Zw)eetks%~G{BTR>T}9|jt0j3Btyb*C3-`C?fwY3EY`q*oYZ39DpM z&uJ;PCZPLs4QO1Jd_|A1PF)azZJ)RZ`^-VMWr6e#XUOA%3eLG_Ch@BDOHzMk*MF0G zCo7xMd?Mg*HMIXw%nNz?%60fZiZPlqb?GqUpXO`F&Yi!okZl(n>P@r1P2i)yk3DgRwbHeNn6e|;J^SK4TM LH~i+q&mR8;k>NTA diff --git a/applications/external/hid_app/assets/ButtonUp_7x4.png b/applications/external/hid_app/assets/ButtonUp_7x4.png deleted file mode 100644 index 1be79328b40a93297a5609756328406565c437c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)I!3HFqj;YoHDIHH2#}J8d-yTOk1_O>mFaFD) zeWb+ZHz{mGZZ1QpXe09^4tcYT#4oe=UbmGC^A-KE*|F&zP#=S*tDnm{r-UX30HgpM AM*si- diff --git a/applications/external/hid_app/assets/Button_18x18.png b/applications/external/hid_app/assets/Button_18x18.png deleted file mode 100644 index 30a5b4fab236d8b57242559ef94fb1c5dbb5d10a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3609 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 diff --git a/applications/external/hid_app/assets/Circles_47x47.png b/applications/external/hid_app/assets/Circles_47x47.png deleted file mode 100644 index 6a16ebf7bbe999fa143c683bef713a6e2f466cbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3712 zcmaJ^c|26@-#)fNS+a&?jCd-`%-Bu#v5X=b+o)7y3;Soh36 zP7A&OfYn&SO_E-Dk~aX%Wl1T^hNu`(4;k4VSxEQ#i(R6~?3m%)z2*K^*J6&wx*s>5 zQRy#yb}pPVJ-zyIwQ@Xbe65YqE)lsyN+WSBFAy+6MVZ2TR1%z#_03h0{IbYFL6GDa zyUt&z0RUzN81x9*Ba1b@ha`X>Ab08Pk!l>;yj0<$;R%2efkCj;_%=Q!3TV=CYmxz) zb^?!FpZbad$p8?{IBN|C?u!9a3l8Q&Ku=LpzdX>Bx2s4Ph~op&_uB8_w|ohla=(Dm z;;*d(a#@yO9l_cXzDTdU+DkufA$`U++&Ha;kI{K6zz ze#@zyIdwZLqeTR*nuMh>s_>W{KJh)^HevbnctJ1*sedD~05lOJa|GPbL@D4evJOo2 zMymbLrpTDY9k*Oz_BDZYudQ9Hw1*{McydJG1AmC+i+d`H*WTn(J81e6-jS(!K^=;v zyUik>=M{Dw`W8Y1&RvVgMs~o&{jPt)9KU|W_S99hqDG?}b`)*kkzjyTMjM67D%Iv- zIKq4QV`K)N6KX24Kc%xB6)jI1<6te4R98tf_HA|TBqmUKhj#1^FjE2 z4E)wn2SRSB3&izGk+gnDhI(tJ9D-e-o!|8?1MiRL20$ig6(XN6?Y2#Om)05dZR^DN z#HEF>?PAelml}~idliBd&L|Y_EK`7_JKhy~pO)U_2K}h3lRCkLm#{F$>58NdlobWhz*UtT^%hw{24{{H>ij>`778#bbp~6rJ zF6~E7=2xFwzqo=GdlDUGmm7`Dcf*#wQHWEOd!vh+LtA%KJOn1Sf^Itb9DA}neX0@@bZkGlhl{fZ-sje5g- zt9yN>DbsS(lf9e}a<*l*R`w#C0Oy8?R2WtqsfeoR3u*su{vJEYm=IZfyC^>Kxx;>u zu#mqf|DDs#=}<9(>I)k(6@p>L*x42)_FK?Re0j(0<)M2!*Z~!Z^#S=E4*7qTYs_5n z|7t*&H}_+acKNXMzu@|VOff!q-M)hQf`*ameXYqs8GaQVrSEAiElpbetR7bLRJ=)7 zR!|P6`cq}!T3pl}+pLCzv4*jYslBOZ*+QvKsa)1g4|5NO$D+qamP7aPNv%mjw`Z`6 zl4s`jOn4^y`Mu)I;`-1`!hp=MOv1j-eT%NdUf9&yl;~8()Rt+JCCrlg5@D%bxn-A> za`yq+fwL4^NK0rixpJ~#NdI+FebMU)Pk$x<+tloN1Npm$m~5%E&@_2hLgBSS;;nFY z%BbQ@Md!2ki}{%^Gy97_5k7owF>5&YVAV+{Q>oeewHe21VU~*?KHc&)yD+n`Zk{;~ zIT3oo>%?l+Zs(_28adriLQ`M;vB4_#nNx6cGu%qsgn;=QbN*Z5x2{y*tp*R6RjWmG zN2Et=UCUWLu)_stbx2o(cpBs0gMD-q~s(6esj@3uL>w zto3#gF)tNL5~)`Hhte`uuisxQqeJ$saJKAGr4?w4hU4z;9r4la!UK{Kq`S+G6D`k$ zV+QSmW6D+V3hDC8=VbQn*S)Xv{Ya@R?KF+6)y*35TJ^7rpGzpZ{^CGi;B!i-KPxa8 z6^xzAERQU|Uw(mp<)`gjniNfXkI3}Zk@}u`v#VdJ{NuqHdRZeGZmBeE$!LGx3;D5$ zHg-;!sh5El^Q>{yO{uge7NeIy)-I5p&ZC7yCuQj$mouZBZL9O*@{T+%D?ey@V=UVv zWy$#SfpdtJfM{pCkT-fF&L~YrqQZ?AYV%GWHr-!X?VnD6(l$xXO3unhiQ!XAH9tbj z_Le#OX=)~kjWEUtZs9mcU{#=1*SqLhv0|mUxKX8(go9sb zx5EP$<6BEx-?j=EU<{^@wLE9_{kUzIzZ9N*-ka^QUi_e}`jbX)cg^RpGxOq?lw}Wm z;UrI0KGURo236UfTO@YQT>PA%=%Z9oGZyi=+&;{?At&L?oikgPY&nyGG*WQ?!dlC^;licR{FXIW`vz6opFxRI~z3fo2S&5l_1bKZ3 z`S2KN631mvdzzNe7Mvyzba39EUkR-3qJI4OQOElhql)upN~w&f@p)Iddd1?;(4}el zFwq&ue(&%E`op#A-u3TWS0uilFWq>It0fHnJXL$D{k4|_M_lAe&PMX)`zu48_AT~Z zYIbUI3E3(tN@9vtKYZJgh6ox=o`Wr$EG6Vm|6xzuJgdkCH zAOjskZ7fV*7i46j12cr0=;~{MbfGXK2-FAy)6<5+;7~)jo(brm1I(*N@%4kFZ0!E2 z#T%J{186id90Cao3)2bH(;-p(AutmY69`lnqN}UTLugYOL>h*!O{A**R+HbD!f4PQ#alUpG5&`u0jN$k{d(r!& z-alO5KYP*tBNxIm1NpVD|7)Lr-{OVmSNGr4@&^Cr9!KPbox)4C?9}%J-W##S#nH`n zb90l|b+3CL!E2HoY^>bqy;G?sQngTFfsRd!?EP0Hv_eg1tl7i-zBctc!@fr=HS*x6(|+l1S)TBgWjCP}EhD_i3C!C# zW_0QGnT2_!N{&S~=WfI!^Wu$(&ALtQg88e}>7UgNt17G8mLO9J{pTOoNN^F;BQaeJ biU<_Yn+9Io=xs3K`2!qm58ISjpSt)z2v?8| diff --git a/applications/external/hid_app/assets/Left_mouse_icon_9x9.png b/applications/external/hid_app/assets/Left_mouse_icon_9x9.png deleted file mode 100644 index c533d85729f9b778cba33227141c90dea298f5e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3622 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 diff --git a/applications/external/hid_app/assets/Like_def_11x9.png b/applications/external/hid_app/assets/Like_def_11x9.png deleted file mode 100644 index 555bea3d4b0239ae1be630d0306316d3e9494c4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3616 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 diff --git a/applications/external/hid_app/assets/Pin_arrow_down_7x9.png b/applications/external/hid_app/assets/Pin_arrow_down_7x9.png deleted file mode 100644 index 9687397afa81c92fb727bd40542830392fabdccb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3607 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 QX&eA7&1_6djNPvM5BL~PlmGw# diff --git a/applications/external/hid_app/assets/Pin_arrow_left_9x7.png b/applications/external/hid_app/assets/Pin_arrow_left_9x7.png deleted file mode 100644 index fb4ded78fde8d1bf4f053ba0b353e7acf14f031a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3603 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 diff --git a/applications/external/hid_app/assets/Pin_arrow_up_7x9.png b/applications/external/hid_app/assets/Pin_arrow_up_7x9.png deleted file mode 100644 index a91a6fd5e99a72112e28865cd8a004c7d1933fff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3603 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 TFC+3}9cE|kU{!4CefEC<&8td2 diff --git a/applications/external/hid_app/assets/Pressed_Button_13x13.png b/applications/external/hid_app/assets/Pressed_Button_13x13.png deleted file mode 100644 index 823926b842b8f9868fd70d6f1434dff071c04d5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3606 zcmaJ@c|26@+dsCllQkq`#8X*jY{g{k%P3o88U`9wuDcQ1RO(?0Mk|NnE zLbfQ9B|8a?C1mX#&+qB^y??yD=kqz|zV7S3zTay-pL4D`*jWkj%kl#NAY_d&NA9dU zH!m0aX`w4&2LSwLI5RT`Ycn$tnL_fx;jsWf@5^=!MkTFE84j&tMO;jK=bxnEF9KjC zCU29dTb}4m0DW0h%(x*cn%_l2a!(e*x&Bf&KO#GNH1}YIugUf3Q!&nG^u8+$6g~?J zVa?5LeA=j*%9`42XLN`}>=9E*oXqnF^pQ~puwI3DdqjP6bp)p*Vwf8wI@$8tm!|;$ z=D8U3aN1*|O^!z-fD<5hYa9@39QhSl>7e2YfD(aWu-KFUM*It@G8k zo>9Wo&lH~ZqaklQV4egvR9qO^uDZd=4T#!xu=+eECVIHYjU0~yYXgc-1AQ)l z-_V-7c0XV4DgO5%YcUMHP2>GJcO04wBV*Vkz41`#Gn#n+*Av>cUpsq0UjACuh_ouP>mkRXBic8yPQ< ziROyUDWhW37qk`>Qn&b$f`tI)75h57=ewV^;OoM_b8yB8qq>3swK9jur8*<&u*+&vj1qGhi%^@OH|#m-!uAxrP_+?(@y zZ`Bn(Zj&ZnakL^VdXHCJFSwmoIz5gXj7I3(j3@w2M@yUpH#AWSIEzgE6WtL?i|P~! z{n#_c>k0i$Ag$}0*Q=~FlP{K@7g6#FTxztXYj);3iYFT%N?|5Yx-Rj$7mm zC6*_MB-r2FXnr$ZE&*$Z9<|}iJAf=m7CWwsHJaeQdt1viJ@>)MwxXPmybq#bw@+CU za)TToj#rDsbpkV#+cKrhS_;(jyWeNvd~vIOkZD>a-(ci^i?sJ?T>)QrPftxp{sj-&-QLNY1FkD~CfR6W@uYz*1aN z!c(RmI5|_Djk*~R1e_i^i#$B*5_Zqh`KiNL5#L9thuuZ;&M%9Ol(Zv*k?{^4Cq43O zJhm>aV}wetL|NuuLF7AO%HPVwDoVZ8!Y-gpdnhhkGim|1Y`spGuFcv6@odNiLC)Ja zno%G4FntnzvM0~AaR|SCGCZ&UIqP`4V!KfLd37#zBlRae{>47U;l)S$Li%d@yyhr# zQgbtXtUz+Makg6aGK>IQ4dkmlQhBm6saUQ-V<-re?fgg!+6c1w&Z{epUTd%546_SCba=(FSB_zPQN=VAO~IZ zxvGCNHtMcLR>Sd_BQcGseW{@>JgK&+tIS(2hAs@3WtUG(>z*?+YBPi$SG5x`k+k0ki@7&{GqNx%Z|i8&DqUa{@IM#U32;?=oRG^!b*pH>pn60o@2CQ zp%hwRYY?7XHB&I6^QNf2=*_gNubl54YW9+@^t}@aEn;awY0{2_!s~^^+aWC}6SChc zyPkbm&d+?AIZ*tW@Nuve-VpY1!&W0xuG#$!oMrN3eib!(u5~QCFthOWQo?Vo0;ZBLt)-c)wzG@krlJ9u4B~Qt%Lt9mB_V?_GyVAisBpOb-w`Mcl`kXg<*a{zA zp@5S~mtG5#ICNO+fyTF!WsbCSv{khp=D6F2Z*|;4e9?^;$NK%BQ-XY%{&*xFGn-iv zQSqSSBK_)5i-j~Xn)m^}xohL~z4h>GV^q#5e1>+`c!pCd4O22PkoQ7*a=N`GC)mJE z*DWDbFY1<9TB*@QB*@eOve$m1kZ3C}zIZt^%HEtf#Xh1v1>+-G(DFT@HaiultQokfV%BC~F3|ZnJEM)_^uS!3?_cXl%QH?nDQG3W|``en5 zz$K~B>V(G*6_20xR?yuRhQYNKFQt@X9HoObG~JPv-gMl2S6GW*OKIws!zc>ryy(vu zSd2qPcHO;erh3U$C#5L4xrJErecI*1Vd)ePCYgD^cXKm{nSvQ2bJeZ((eY}3lkWFd=7oyo7GfvlJP60X(C&ozFUPf& zwY_WO(nageoo;>3>|eZdB!49&`+|Fm%U1Ej@|w>oeLb~w?Sjx&}b>tXH)4to3d#pAueVK}PpRXeS0Iz!WE0>=rhL^yt!pU1Bh)1VMGuYLZ zIah-c+7H{AW1XxI7uNmjx~ZRje$sHi&8TL*os}ymstoR{P_A758MHDd9nAmTX23lp zp8jaFrf=)p?sbuG7s|GuVCx9OKRxR_JKng7u!Q-p=4>bb`fzom%c|9?Tgg%>Ha=TH zK~6}vdeOT*X{4~UP`u+^xXUlb4E5pE(AMb2i4N3e@4UcTOh;`AqiBi3dRX)b)~M8| zP}RG*`RxdAYMCdE;VgFUi z&@50iN0JXM7)`+fCf+13EXbOG_QfKxXm7^3W~>1KaH-&&P&AaS4GcpfXrOm&H0T5} z8w~&kMszY76M&_Gys*AFA{@+mSqlc?yy0M1U0bLv*$nH4LxfPUjv;nVn2-RBzBky& z5M)4yu?YxR8X80=;E7Zi9S;7R7si%%)DSS}ZxdPo9Q>c4P__;rGZF<0I;x?mj)6j< zpriU4-e@m0#>-0$qy^Q|gg|v5nmX!GC`?-)rlSM;=K{0cQM`R%NOQ}7oUwOsupf;^ zhCv{~!ND5A+8QK^FGN#cUmpV1f@o=}vn|xA3?dCpS0_@HelwV3sTc~5Ov90gpdCiE z7b%bi2eU){PYwj~zqCZ^KXqbP3_?efA(|S{ot%Cf+S>mArUb&j)>Il2``>u~PhzSQ zgN%hBu~bqZ1;g%~kJ64SGR%yEMbk(WClU$&yNnKgBpQk8MECm;Y^|qvt2%x{ShT;Agi>bvQ`ToIr z|1lO*%Rgcv>|h`}z5QRk{;gsU(2n@;=(0Ee4nLO2o_Gp-v?B%|jk8~iT@E%*7VLFn zW8+olk$r4Q+1lL1iQebs$<4V-6wuDa^WJ8EKPjE`j~qYo##DjQV;r1}R+k1F9+3x|HZ(so6H=Bupj;vWfZuSsJsEF5FNt0sU&UBN1>d!x z*-7w%>@YFG;_-^Aa(trZQF2*B61H^*jEuNsS~8h(_@J1++G=89I*%er`Kc?A@fiXvLda|NDkjVwOw7a=Z1E?2)w_-?q4eu^{Msu0-SlI;aInz>dIRK=%l z#e8CMskc_(+2Cl*9hJAodUoBXCe$`L^(M4|rx*1&0^`;5&be`ZvrrNxFl(pQ0bsd` zR`)@fmowNiY_f~ByQIHul6edW_AtBS0|4i73J`o-nSL`b0N^r1RG%8ktkxY;tK~jY zw|}%wV9Q1421cQ=9wUn3cMm?oa8W4=#VAK~Je5^-fqpQM)vC4ij7XphL+Tw~3Zv;F z--)~#b;{Ktd|ZYtya$PL!%-ZrHwp5wyizIQ8*+7~Tw*Z_pw=jHTd+mEwkgc+CLZKq zD!Ytk>_bGJHGUO;vIT&LZbej^!0v{W+M+)QzQ9)I=^nme{7~S%I}?@~Cz+Y{p7H!J z`j$@C-1|aLk>NN!Y_mq~=R-W2jh8eaO%0f5C)D^7+}fXkiv$as4nI9z#90-+=GOI$ z#U&PERLiHs#lnDyM-5F0mIUiT(>%}-1+4?ae7by`H*D*bzzKO4&lO)C_@nWVD;yR{ zFjbT97mGUx6%CBSHtH&fMPuPgmAChqJ$sDr5$iGT@wStnSIbY+GCeGx&^qkyRmy|7 zs|GsW5kExGmGrvhxd99drEn(Q=WWgzB({=@2GXsd&i#kd6Umc zpE*}qf*$*4l54r__+M@_SZ^`9W?Ey^Z7m`7CIE9pZaPqV^7XMnHO0= z&ZFV=9|t*YM{_$hST@*TAKPX=yD(kd1QKwQF7s29^AakIxE!M0sQ9d7=;{^Ks^o3i zsu*-Zeij0&X|Cy5X18+JL!W0l*=OTE)0%HiIX7t~=;pZilFF2dOpcaiC5&{|s~|Bc zkx*z_Xj^FVwMM68AvZmz#;D3^Gep?1*<9(Yk_kDkbAS4r{gC}wE`P416&kr#0x9sy zmdUEZvEF#+E+%KZJ|CQ6Ny{DgubKOPnL~VMc$gL=+XkqomYBAN$ zsxn6<=cMIH%jS-E9S=MDQ?%32umSj7+FaT|+C+uR8NV}X<$2{VNoJ)pXL6ht%d5S^ z&mf$#2@Yq@l^GYO7a!}dDz3^skXvb;U|pEePi}bndwFYleuebY*+K4+l5%SKH6qzn zid^xwq+v0kCgIwvYrk%zd4wW|gbQWQ$Oid7XNV(DBga!a?=R|Kd%K!A4{Xam}ra8c1V&QBu%DitfgkgoVn(6ZZe=}Ej_I)t$rbI zeF%B|k zbckVy^S;fEfU9zEV)cBcD-9(K<3fu=XX}dPJX?OdT`adgm)sfONf8b| z74*6PJrD5{F{U9%P$@hz+%ZBwmL5eo+zm_8W_6EZeJ60=af!I`G&0Nv@kHHRTUD0KWoonUs!;s^qwTB759>Gj0c!b;>+`jo(Qpj0xn#=Ry;wpD(O^Ga7*= zbtsQig_UC~AH6}ntS05Qc6OZ9$3Moe;=ki{7JJ5C5C=BAyBB2wtG{Xe);Ho@y}qs2 z`g+8H!@;W0qmQ&{wpq5WUlLs~zmd2}Jy&c^^;u}sQl0;+k?j2#q}Tm zY9ieH%j=!=C6>C7j*!Ez_nW5V={WzH`E|aD^`k<_;VZWSizaz`f4L${mW5u#q%Nl# zr`e}&I=ec*vU#W1-T!4gV9R9W7m@o~C?|jO6?`jYcs{f@fxO&xEB#*jwIIkJqb?&4 z%LC`!IwvlQ(3W0_GADbCc4OvFR-f!VyZn;5Tsks)(D9{X>J#Jz>KEo0)J{ULO>@=# zs??IovtE^p0W~iIJ=W)CGITq~R%`r!m)z~|%Rr#VYE}Yh>u=ZBCM3s#7)sln?Nvi8 zrN!cEo9YXz1`CEm*s;hyednFg!KKmb7i(FWE8U|e>)hdCT|4n>aU$6LaVc@_5ke7P zGfwCs5L5b$?fI=-Y?phNVusYt!=3gLDM@J1M&H+g&hF&ytfb|ngg4Zy+1p=gze+zD zX{v8J`nuIm6Lx;}^yWexYm_Cs^k_oFX67pBy7I2)AJ5k8-{)>7NGBxha&acFY%OWu z4Q2mVN;8cJOnaIKlSO2Z07G}0D+y#qC6Y;YB%-^&Pb&!p0G!GcJb_8DvP8Pks1V|w z55$j3XQKfCrSC^4x_Ob9AXgHZ;*AC`RlNa&DDG&mqqdcX6&*|Rq?iUUNcI8Nc((vA zH-tM_Uk`-xL$V2|BqkB$N4@0ji}XW-|Kvro_j_h281$zL(+ds$OBBKC6bMUWkU+W+ zn7W&Wh6YF%0U@~);jWqn zx>3CMEGmCOtgMh`-o8wtw;Ra}hX%7rAQXx_5{rOoVRcUE!ZeJvU@#+`Ar5;2gM(wR zx^PVx0004&%004{+008|`004nN004b?008NW002DY000@xb3BE2000Uv zX+uL$Nkc;*aB^>EX>4Tx07%E3mUmQC*A|D*y?1({%`nm#dXp|Nfb=dP9RyJrW(F9_ z0K*JTY>22pL=h1IMUbF?0i&TvtcYSED5zi$NDxqBFp8+CWJcCXe0h2A<>mLsz2Dkr z?{oLrd!Mx~03=TzE-wX^0w9?u;0Jm*(^rK@(6Rjh26%u0rT{Qm>8ZX!?!iDLFE@L0LWj&=4?(nOT_siPRbOditRHZrp6?S8Agej zFG^6va$=5K|`EW#NwP&*~x4%_lS6VhL9s-#7D#h8C*`Lh;NHnGf9}t z74chfY%+(L4giWIwhK6{coCb3n8XhbbP@4#0C1$ZFF5847I3lz;zPNlq-OKEaq$AW zE=!MYYHiJ+dvY?9I0Av8Ka-Wn(gPeepdb@piwLhwjRWWeSr7baCBSDM=|p zK0Q5^$>Pur|2)M1IPkCYSQ^NQ`z*p zYmq4Rp8z$=2uR(a0_5jDfT9oq5_wSE_22vEgAWDbn-``!u{igi1^xT3aEbVl&W-yV z=Mor9X9@Wki)-R*3DAH5Bmou30~MeFbb%o-16IHmI084Y0{DSo5DwM?7KjJQfDbZ3 zF4znTKoQsl_JT@K1L{E|XaOfc2RIEbfXm=IxC!on2Vew@gXdrdyaDqN1YsdEM1kZX zRY(gmfXpBUWDmJPK2RVO4n;$85DyYUxzHA<2r7jtp<1XB`W89`U4X7a1JFHa6qn9`(3jA6(BtSg7z~Dn z(ZN_@JTc*z1k5^2G3EfK6>}alfEmNgVzF3xtO3>z>xX4x1=s@Ye(W*qIqV>I9QzhW z#Hr%UaPGJW91oX=E5|kA&f*4f6S#T26kZE&gZIO;@!9wid_BGke*-^`pC?EYbO?5Y zU_t_6GogaeLbybDNO(mg64i;;!~i0fxQSRnJWjkq93{RZ$&mC(E~H43khGI@gmj*C zkMxR6CTo)&$q{4$c_+D%e3AT^{8oY@VI<)t!Is!4Q6EtGo7CCWGzL)D>rQ4^>|)NiQ$)EQYB*=4e!vRSfKvS(yRXb4T4 z=0!`QmC#PmhG_4XC@*nZ!dbFoNz0PKC3A9$a*lEwxk9;CxjS<2<>~Tn@`>`hkG4N#KjNU~z;vi{c;cwx$aZXSoN&@}N^m;n^upQ1neW`@Jm+HLvfkyqE8^^jVTFG14;RpP@{Py@g^4IZC^Zz~o6W||E74S6BG%z=? zH;57x71R{;CfGT+B=|vyZiq0XJ5(|>GPE&tF3dHoG;Cy*@v8N!u7@jxbHh6$uo0mV z4H2`e-B#~iJsxQhSr9q2MrTddnyYIS)+Vhz6D1kNj5-;Ojt+}%ivGa#W7aWeW4vOj zV`f+`tbMHKY)5t(dx~SnDdkMW+QpW}PR7~A?TMR;cZe^KpXR!7E4eQdJQHdX<`Vr9 zk0dT6g(bBnMJ7e%MIVY;#n-+v{i@=tg`KfG`%5fK4(`J2;_VvR?Xdf3 zsdQ;h>DV6MJ?&-mvcj_0d!zPVEnik%vyZS(xNoGwr=oMe=Kfv#KUBt7-l=k~YOPkP z-cdbwfPG-_pyR=o8s(azn)ipehwj#T)V9}Y*Oec}9L_lWv_7=H_iM)2jSUJ7MGYU1 z@Q#ce4LsV@Xw}%*q|{W>3^xm#r;bG)yZMdlH=QkpEw!z*)}rI!xbXP1Z==5*I^lhy z`y}IJ%XeDeRku;v3frOf?DmPgz@Xmo#D^7KH*><&kZ}k0<(`u)y&d8oAIZHU3 ze|F(q&bit1spqFJ#9bKcj_Q7Jan;4!Jpn!am%J}sx$J)VVy{#0xhr;8PG7aTdg>bE zTE}(E>+O9OeQiHj{Lt2K+24M{>PF{H>ziEz%LmR5It*U8<$CM#ZLizc@2tEtFcdO$ zcQ|r*xkvZnNio#z9&IX9*nWZ zp8u5o(}(f=r{t&Q6RH!9lV+2rr`)G*K3n~4{CVp0`RRh6rGKt|q5I;yUmSnwn^`q8 z{*wQ4;n(6<@~@7(UiP|s)_?Z#o8&k1bA@l^-yVI(c-Q+r?ES=i<_GMDijR69yFPh; zdbp6hu<#rAg!B8%JG^WF000SaNLh0L01m_e01m_fl`9S#0000PbVXQnQ*UN;cVTj6 z06}DLVr3vnZDD6+Qe|Oed2z{QJOBUyO-V#SR9Hvt&&vq_APfZ2?Z4?5q7fA<73t;DzTElPZdnb+W-vX2=^0GVV0s4AyTEkxc3v0wl(p9E_klFChyj!; VN_%sSbR7Ty002ovPDHLkV1hy!X)pi) diff --git a/applications/external/hid_app/assets/Space_65x18.png b/applications/external/hid_app/assets/Space_65x18.png deleted file mode 100644 index b60ae50970b8be827ae32ddbd9e1b0d28c8b3a9a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3619 zcmaJ@c{r5q+kR|?vSeS9G2*Q(Gqz$f_GOf18r!JE7=ytq%?xHFO-U))vSm#u)X=6# zwu*&|8!^APw~9?k(a6Vz_{`1J?VwO%hlm80mweJ_2Ll%+w5Jal|B#ZToHj zkX!A1wWV(yKRGcrJmE7L$o^5EyA?1;0vjpK4{lZ7;N}HH?K{|g9^>OZJmdzhM?p0K zMW=v17r<|D)m^7wAm^muyU^8WhW>`hzU({5Mni?Yg1dIjs(9V0f{sQT{n8mG4Mm49 zbG~l%ht2_K(@oNfYx5#D&tizdC8*fR7G5(g;>x7*Rzu{4&DevTBf5`It4m&=M_(&P zg6$d@FHi{BFL>ue9`qCWpjMUz{dO z@9>n#el1gZMS$0|kzX961dH0^726AL=a){4^e8+sFE>V1@t?G01xkRvR~uHtV6d@Jy=*+_LjJ^<;I%HkfZ+ zJ{WS&*3q1L--qRs;FC3Rwv9{p?cw*6_FMFK^@Q9yZ8!?f0Ei>znMIVlCNa;%nYvD_=OIcyvaxrpYxGcGRWZCqbo>reG^tc8h zWbb>x%$fc-l1kK>Pg=_9^WFC8k{QaNGP~oK)fB= zk~}W=y`t;c`=z{$ml*@ap9mj5x5DesKUlZZ%#d$#e*hBLJ2$e|kFK?B#{H}rW-Lg}+w*yHz2X|@s=6q5@hMLLk0Ngx@77A0z{8^GG<=3FCsOHJ6w{_pD*!j4k8!wLb`#+}y`?CB4 zQGwW*jB;lA{ql?St3NI0Q^jcF`vqpNjn(zm!LN-{xhDhDbu!1&ol`GQ%#aWnhw%cUor3tn<%~!N%j(>i+!K$>%8wb|oXB!X zUe^D7^t}0+-xUX|ptm{#4k$H7g6z!~%8Pa`7Cm2B9iPsA(lAKMOv=nd3E@*p)jmSY z4wO0gsHr6ijWH$&&GLy?n^(q^SE-Brl7W%7oq46G5~Q${Eu>J5eoE#Py&O@6IQcl%pM`Lo~JAQ5D{F{9M=h7QdD!DVxX< zG|G9wpE0lyi;C#Fd)Hj;lB;fVQBqS2vE;|e7g$M5vbQtaKehXm%Y{SI$sQ~+tFYwf zBdhX>5m$SU?yw~Wp|9`Dv9jjbX~cB?G?BI9R`c*!mA`5CyDM`-#q#qpezqJMP@wb32zU+0*_sQsBVDnwlp9 z1k~Y}eFzwNJcCK<%a~0Mc}6~YNcgqs_^ZDL?}eQkMSi{0{$}7!+hE#-vL*g$1VgP0 zRujb1$Rp&y?^LnB-pI>RIHO=)UG^)Stu=}bYS4>w&Cba>0H0qSyOcOu;9ZcNWp51s zkT$?rvE4`ua6jQ*ND(zN(xGR}RjlKca_;?=KGcDxu~0=Et)Zw@0K zo+3@-R$69V4NGW0?52-)vfp1=^RMlue*F1S)BQH1iv4y*zKp2)d2hK&#nR8<o8NY>iF~_Iy7d@WOBnj;S?k&H#!ZAREO0e@E9uw!tHWK^t=8Sj zR?0DPS&EACLUL6L-tCFQ1y2gZJDS5?ele!04<-jUN7j#bpf`HwcCAKt)RZua7Afop zMGs*O$_4u!*bGtM^Q3;}>g74L+mq3vv8SQ0@K zvyIWD6UZDk02mt6$rx+^jt26=`QnLiF#BZ<7=-tRgI)FPpmt<)oF5($O2IjX+B;!G z1F#0(U}GbYAsxmMAmC^i5S7-)K9yf9cVFLjVMR9g!I)rDy3YCxed9RrxIF6f^D=D4GH`@m2ZR{uET z?BHNO8jTEtKte)7G(&VWNfcj*mVto*1gZ_u*4E%4G^h+B4MW!;Qk8!zSm3Bw3Z6{E zlZc>gMT{3Ihz199LjBJf2;_fdiPV4c#K{#)rmpIK~Oj6!<%hNIw#dMD-()LE1W+P|yK8 z3>Ht^wjBJMVrK`lAyR1=A{J+30S9wLH1T+En5mAg1yo=Eh@P&MzLu7yxtX4op5xA}2IPRCO?t!+X9zvoGZ%D;b1(Y*s+gO-7(fhnSIU^r{GM99afXqQXz`L$cAW!v1IOaB9=s#hui diff --git a/applications/external/hid_app/assets/Voldwn_6x6.png b/applications/external/hid_app/assets/Voldwn_6x6.png deleted file mode 100644 index d7a82a2df8262667a9a03419f437ff9b350e645f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3593 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 - -#define TAG "HidApp" - -enum HidDebugSubmenuIndex { - HidSubmenuIndexKeynote, - HidSubmenuIndexKeynoteVertical, - HidSubmenuIndexKeyboard, - HidSubmenuIndexMedia, - HidSubmenuIndexTikTok, - HidSubmenuIndexMouse, - HidSubmenuIndexMouseClicker, - HidSubmenuIndexMouseJiggler, -}; - -static void hid_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - Hid* app = context; - if(index == HidSubmenuIndexKeynote) { - app->view_id = HidViewKeynote; - hid_keynote_set_orientation(app->hid_keynote, false); - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); - } else if(index == HidSubmenuIndexKeynoteVertical) { - app->view_id = HidViewKeynote; - hid_keynote_set_orientation(app->hid_keynote, true); - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeynote); - } else if(index == HidSubmenuIndexKeyboard) { - app->view_id = HidViewKeyboard; - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewKeyboard); - } else if(index == HidSubmenuIndexMedia) { - app->view_id = HidViewMedia; - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia); - } else if(index == HidSubmenuIndexMouse) { - app->view_id = HidViewMouse; - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); - } else if(index == HidSubmenuIndexTikTok) { - app->view_id = BtHidViewTikTok; - view_dispatcher_switch_to_view(app->view_dispatcher, BtHidViewTikTok); - } else if(index == HidSubmenuIndexMouseClicker) { - app->view_id = HidViewMouseClicker; - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseClicker); - } else if(index == HidSubmenuIndexMouseJiggler) { - app->view_id = HidViewMouseJiggler; - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); - } -} - -static void bt_hid_connection_status_changed_callback(BtStatus status, void* context) { - furi_assert(context); - Hid* hid = context; - bool connected = (status == BtStatusConnected); - if(hid->transport == HidTransportBle) { - if(connected) { - notification_internal_message(hid->notifications, &sequence_set_blue_255); - } else { - notification_internal_message(hid->notifications, &sequence_reset_blue); - } - } - hid_keynote_set_connected_status(hid->hid_keynote, connected); - hid_keyboard_set_connected_status(hid->hid_keyboard, connected); - hid_media_set_connected_status(hid->hid_media, connected); - hid_mouse_set_connected_status(hid->hid_mouse, connected); - hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); - hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); - hid_tiktok_set_connected_status(hid->hid_tiktok, connected); -} - -static void hid_dialog_callback(DialogExResult result, void* context) { - furi_assert(context); - Hid* app = context; - if(result == DialogExResultLeft) { - view_dispatcher_stop(app->view_dispatcher); - } else if(result == DialogExResultRight) { - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view - } else if(result == DialogExResultCenter) { - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu); - } -} - -static uint32_t hid_exit_confirm_view(void* context) { - UNUSED(context); - return HidViewExitConfirm; -} - -static uint32_t hid_exit(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -Hid* hid_alloc(HidTransport transport) { - Hid* app = malloc(sizeof(Hid)); - app->transport = transport; - - // Gui - app->gui = furi_record_open(RECORD_GUI); - - // Bt - app->bt = furi_record_open(RECORD_BT); - - // Notifications - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - // View dispatcher - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - // Device Type Submenu view - app->device_type_submenu = submenu_alloc(); - submenu_add_item( - app->device_type_submenu, "Keynote", HidSubmenuIndexKeynote, hid_submenu_callback, app); - submenu_add_item( - app->device_type_submenu, - "Keynote Vertical", - HidSubmenuIndexKeynoteVertical, - hid_submenu_callback, - app); - submenu_add_item( - app->device_type_submenu, "Keyboard", HidSubmenuIndexKeyboard, hid_submenu_callback, app); - submenu_add_item( - app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app); - submenu_add_item( - app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app); - if(app->transport == HidTransportBle) { - submenu_add_item( - app->device_type_submenu, - "TikTok Controller", - HidSubmenuIndexTikTok, - hid_submenu_callback, - app); - } - submenu_add_item( - app->device_type_submenu, - "Mouse Clicker", - HidSubmenuIndexMouseClicker, - hid_submenu_callback, - app); - submenu_add_item( - app->device_type_submenu, - "Mouse Jiggler", - HidSubmenuIndexMouseJiggler, - hid_submenu_callback, - app); - view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); - view_dispatcher_add_view( - app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); - app->view_id = HidViewSubmenu; - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); - return app; -} - -Hid* hid_app_alloc_view(void* context) { - furi_assert(context); - Hid* app = context; - // Dialog view - app->dialog = dialog_ex_alloc(); - dialog_ex_set_result_callback(app->dialog, hid_dialog_callback); - dialog_ex_set_context(app->dialog, app); - dialog_ex_set_left_button_text(app->dialog, "Exit"); - dialog_ex_set_right_button_text(app->dialog, "Stay"); - dialog_ex_set_center_button_text(app->dialog, "Menu"); - dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); - view_dispatcher_add_view( - app->view_dispatcher, HidViewExitConfirm, dialog_ex_get_view(app->dialog)); - - // Keynote view - app->hid_keynote = hid_keynote_alloc(app); - view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); - - // Keyboard view - app->hid_keyboard = hid_keyboard_alloc(app); - view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard)); - - // Media view - app->hid_media = hid_media_alloc(app); - view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media)); - - // TikTok view - app->hid_tiktok = hid_tiktok_alloc(app); - view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); - - // Mouse view - app->hid_mouse = hid_mouse_alloc(app); - view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); - - // Mouse clicker view - app->hid_mouse_clicker = hid_mouse_clicker_alloc(app); - view_set_previous_callback( - hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, - HidViewMouseClicker, - hid_mouse_clicker_get_view(app->hid_mouse_clicker)); - - // Mouse jiggler view - app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); - view_set_previous_callback( - hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_exit_confirm_view); - view_dispatcher_add_view( - app->view_dispatcher, - HidViewMouseJiggler, - hid_mouse_jiggler_get_view(app->hid_mouse_jiggler)); - - return app; -} - -void hid_free(Hid* app) { - furi_assert(app); - - // Reset notification - if(app->transport == HidTransportBle) { - notification_internal_message(app->notifications, &sequence_reset_blue); - } - - // Free views - view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu); - submenu_free(app->device_type_submenu); - view_dispatcher_remove_view(app->view_dispatcher, HidViewExitConfirm); - dialog_ex_free(app->dialog); - view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); - hid_keynote_free(app->hid_keynote); - view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); - hid_keyboard_free(app->hid_keyboard); - view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); - hid_media_free(app->hid_media); - view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); - hid_mouse_free(app->hid_mouse); - view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker); - hid_mouse_clicker_free(app->hid_mouse_clicker); - view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); - hid_mouse_jiggler_free(app->hid_mouse_jiggler); - view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); - hid_tiktok_free(app->hid_tiktok); - view_dispatcher_free(app->view_dispatcher); - - // Close records - furi_record_close(RECORD_GUI); - app->gui = NULL; - furi_record_close(RECORD_NOTIFICATION); - app->notifications = NULL; - furi_record_close(RECORD_BT); - app->bt = NULL; - - // Free rest - free(app); -} - -void hid_hal_keyboard_press(Hid* instance, uint16_t event) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_kb_press(event); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_kb_press(event); - } else { - furi_crash(NULL); - } -} - -void hid_hal_keyboard_release(Hid* instance, uint16_t event) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_kb_release(event); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_kb_release(event); - } else { - furi_crash(NULL); - } -} - -void hid_hal_keyboard_release_all(Hid* instance) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_kb_release_all(); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_kb_release_all(); - } else { - furi_crash(NULL); - } -} - -void hid_hal_consumer_key_press(Hid* instance, uint16_t event) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_consumer_key_press(event); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_consumer_key_press(event); - } else { - furi_crash(NULL); - } -} - -void hid_hal_consumer_key_release(Hid* instance, uint16_t event) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_consumer_key_release(event); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_consumer_key_release(event); - } else { - furi_crash(NULL); - } -} - -void hid_hal_consumer_key_release_all(Hid* instance) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_consumer_key_release_all(); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_kb_release_all(); - } else { - furi_crash(NULL); - } -} - -void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_mouse_move(dx, dy); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_mouse_move(dx, dy); - } else { - furi_crash(NULL); - } -} - -void hid_hal_mouse_scroll(Hid* instance, int8_t delta) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_mouse_scroll(delta); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_mouse_scroll(delta); - } else { - furi_crash(NULL); - } -} - -void hid_hal_mouse_press(Hid* instance, uint16_t event) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_mouse_press(event); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_mouse_press(event); - } else { - furi_crash(NULL); - } -} - -void hid_hal_mouse_release(Hid* instance, uint16_t event) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_mouse_release(event); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_mouse_release(event); - } else { - furi_crash(NULL); - } -} - -void hid_hal_mouse_release_all(Hid* instance) { - furi_assert(instance); - if(instance->transport == HidTransportBle) { - furi_hal_bt_hid_mouse_release_all(); - } else if(instance->transport == HidTransportUsb) { - furi_hal_hid_mouse_release(HID_MOUSE_BTN_LEFT); - furi_hal_hid_mouse_release(HID_MOUSE_BTN_RIGHT); - } else { - furi_crash(NULL); - } -} - -int32_t hid_usb_app(void* p) { - UNUSED(p); - Hid* app = hid_alloc(HidTransportUsb); - app = hid_app_alloc_view(app); - FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); - furi_hal_usb_unlock(); - furi_check(furi_hal_usb_set_config(&usb_hid, NULL) == true); - - bt_hid_connection_status_changed_callback(BtStatusConnected, app); - - view_dispatcher_run(app->view_dispatcher); - - furi_hal_usb_set_config(usb_mode_prev, NULL); - - hid_free(app); - - return 0; -} - -int32_t hid_ble_app(void* p) { - UNUSED(p); - Hid* app = hid_alloc(HidTransportBle); - app = hid_app_alloc_view(app); - - bt_disconnect(app->bt); - - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - - // Migrate data from old sd-card folder - Storage* storage = furi_record_open(RECORD_STORAGE); - - storage_common_migrate( - storage, - EXT_PATH("apps/Tools/" HID_BT_KEYS_STORAGE_NAME), - APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); - - bt_keys_storage_set_storage_path(app->bt, APP_DATA_PATH(HID_BT_KEYS_STORAGE_NAME)); - - furi_record_close(RECORD_STORAGE); - - if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { - FURI_LOG_E(TAG, "Failed to switch to HID profile"); - } - - furi_hal_bt_start_advertising(); - bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); - - view_dispatcher_run(app->view_dispatcher); - - bt_set_status_changed_callback(app->bt, NULL, NULL); - - bt_disconnect(app->bt); - - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - - bt_keys_storage_set_default_path(app->bt); - - if(!bt_set_profile(app->bt, BtProfileSerial)) { - FURI_LOG_E(TAG, "Failed to switch to Serial profile"); - } - - hid_free(app); - - return 0; -} diff --git a/applications/external/hid_app/hid.h b/applications/external/hid_app/hid.h deleted file mode 100644 index 49d8b4e04..000000000 --- a/applications/external/hid_app/hid.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "views/hid_keynote.h" -#include "views/hid_keyboard.h" -#include "views/hid_media.h" -#include "views/hid_mouse.h" -#include "views/hid_mouse_clicker.h" -#include "views/hid_mouse_jiggler.h" -#include "views/hid_tiktok.h" - -#define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" - -typedef enum { - HidTransportUsb, - HidTransportBle, -} HidTransport; - -typedef struct Hid Hid; - -struct Hid { - Bt* bt; - Gui* gui; - NotificationApp* notifications; - ViewDispatcher* view_dispatcher; - Submenu* device_type_submenu; - DialogEx* dialog; - HidKeynote* hid_keynote; - HidKeyboard* hid_keyboard; - HidMedia* hid_media; - HidMouse* hid_mouse; - HidMouseClicker* hid_mouse_clicker; - HidMouseJiggler* hid_mouse_jiggler; - HidTikTok* hid_tiktok; - - HidTransport transport; - uint32_t view_id; -}; - -void hid_hal_keyboard_press(Hid* instance, uint16_t event); -void hid_hal_keyboard_release(Hid* instance, uint16_t event); -void hid_hal_keyboard_release_all(Hid* instance); - -void hid_hal_consumer_key_press(Hid* instance, uint16_t event); -void hid_hal_consumer_key_release(Hid* instance, uint16_t event); -void hid_hal_consumer_key_release_all(Hid* instance); - -void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy); -void hid_hal_mouse_scroll(Hid* instance, int8_t delta); -void hid_hal_mouse_press(Hid* instance, uint16_t event); -void hid_hal_mouse_release(Hid* instance, uint16_t event); -void hid_hal_mouse_release_all(Hid* instance); \ No newline at end of file diff --git a/applications/external/hid_app/hid_ble_10px.png b/applications/external/hid_app/hid_ble_10px.png deleted file mode 100644 index d4d30afe0465e3bd0d87355a09bbdfc6c44aa5d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&u?6^qxc>kDAIJlDSNs& zhE&W+PH5C8xG diff --git a/applications/external/hid_app/hid_usb_10px.png b/applications/external/hid_app/hid_usb_10px.png deleted file mode 100644 index 415de7d2304fe982c025b2b9a942abbf0a2b6dd0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 969 zcmaJ=J#W)M7(OY0prTdS3e(9IQYsOjefe;0)l|h!Xha;MG(g5)>`P;{_8I$1oJb(V z#>Co{9{@i91|(QuX5%*?v9pwOnxqT_55D(az0dQ0J@>lZy1%+|YXtzX+Ss!@;>_%o zt2y!i@N?&lIBxPQduOaNrjU+e?;YX%)UR2L%LyN@}>atRF6-9xXE~}dAVr@YBcOX_U zM#>gat3`~BQpG5%aP~Eh*gj89{x|#<%&i_M$ zU=f}04!x-NpTtRb98uJv2|I~hvAe-WmMSu=m=ez7E@Q{@LAHmCvt-C3h|9793l4Gp zF!O9qA&z4-!i1C1r48GZ1c~hXo`JDuS-aJ0wOrd$)taqWN;ON>tZH4WV;fs@tj*k$ zfQEdI^)9g5QfwxOAQG8v8vD3;Z_1q-{ zl$i_hipxU&G!&YTg}8q|eDGX6j4SPCw{~`RCd@~lzrPU2?S{SEO@H(cJnv<$o(G-l ph0_~rZ>7^_`EovpzT_W+OY7j;8rXcd{ -#include -#include -#include "../hid.h" -#include "hid_icons.h" - -#define TAG "HidKeyboard" - -struct HidKeyboard { - View* view; - Hid* hid; -}; - -typedef struct { - bool shift; - bool alt; - bool ctrl; - bool gui; - uint8_t x; - uint8_t y; - uint8_t last_key_code; - uint16_t modifier_code; - bool ok_pressed; - bool back_pressed; - bool connected; - char key_string[5]; - HidTransport transport; -} HidKeyboardModel; - -typedef struct { - uint8_t width; - char* key; - const Icon* icon; - char* shift_key; - uint8_t value; -} HidKeyboardKey; - -typedef struct { - int8_t x; - int8_t y; -} HidKeyboardPoint; -// 4 BY 12 -#define MARGIN_TOP 0 -#define MARGIN_LEFT 4 -#define KEY_WIDTH 9 -#define KEY_HEIGHT 12 -#define KEY_PADDING 1 -#define ROW_COUNT 7 -#define COLUMN_COUNT 12 - -// 0 width items are not drawn, but there value is used -const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { - { - {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1}, - {.width = 1, .icon = &I_ButtonF2_5x8, .value = HID_KEYBOARD_F2}, - {.width = 1, .icon = &I_ButtonF3_5x8, .value = HID_KEYBOARD_F3}, - {.width = 1, .icon = &I_ButtonF4_5x8, .value = HID_KEYBOARD_F4}, - {.width = 1, .icon = &I_ButtonF5_5x8, .value = HID_KEYBOARD_F5}, - {.width = 1, .icon = &I_ButtonF6_5x8, .value = HID_KEYBOARD_F6}, - {.width = 1, .icon = &I_ButtonF7_5x8, .value = HID_KEYBOARD_F7}, - {.width = 1, .icon = &I_ButtonF8_5x8, .value = HID_KEYBOARD_F8}, - {.width = 1, .icon = &I_ButtonF9_5x8, .value = HID_KEYBOARD_F9}, - {.width = 1, .icon = &I_ButtonF10_5x8, .value = HID_KEYBOARD_F10}, - {.width = 1, .icon = &I_ButtonF11_5x8, .value = HID_KEYBOARD_F11}, - {.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12}, - }, - { - {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, - {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, - {.width = 1, .icon = NULL, .key = "3", .shift_key = "#", .value = HID_KEYBOARD_3}, - {.width = 1, .icon = NULL, .key = "4", .shift_key = "$", .value = HID_KEYBOARD_4}, - {.width = 1, .icon = NULL, .key = "5", .shift_key = "%", .value = HID_KEYBOARD_5}, - {.width = 1, .icon = NULL, .key = "6", .shift_key = "^", .value = HID_KEYBOARD_6}, - {.width = 1, .icon = NULL, .key = "7", .shift_key = "&", .value = HID_KEYBOARD_7}, - {.width = 1, .icon = NULL, .key = "8", .shift_key = "*", .value = HID_KEYBOARD_8}, - {.width = 1, .icon = NULL, .key = "9", .shift_key = "(", .value = HID_KEYBOARD_9}, - {.width = 1, .icon = NULL, .key = "0", .shift_key = ")", .value = HID_KEYBOARD_0}, - {.width = 2, .icon = &I_Pin_arrow_left_9x7, .value = HID_KEYBOARD_DELETE}, - {.width = 0, .value = HID_KEYBOARD_DELETE}, - }, - { - {.width = 1, .icon = NULL, .key = "q", .shift_key = "Q", .value = HID_KEYBOARD_Q}, - {.width = 1, .icon = NULL, .key = "w", .shift_key = "W", .value = HID_KEYBOARD_W}, - {.width = 1, .icon = NULL, .key = "e", .shift_key = "E", .value = HID_KEYBOARD_E}, - {.width = 1, .icon = NULL, .key = "r", .shift_key = "R", .value = HID_KEYBOARD_R}, - {.width = 1, .icon = NULL, .key = "t", .shift_key = "T", .value = HID_KEYBOARD_T}, - {.width = 1, .icon = NULL, .key = "y", .shift_key = "Y", .value = HID_KEYBOARD_Y}, - {.width = 1, .icon = NULL, .key = "u", .shift_key = "U", .value = HID_KEYBOARD_U}, - {.width = 1, .icon = NULL, .key = "i", .shift_key = "I", .value = HID_KEYBOARD_I}, - {.width = 1, .icon = NULL, .key = "o", .shift_key = "O", .value = HID_KEYBOARD_O}, - {.width = 1, .icon = NULL, .key = "p", .shift_key = "P", .value = HID_KEYBOARD_P}, - {.width = 1, .icon = NULL, .key = "[", .shift_key = "{", .value = HID_KEYBOARD_OPEN_BRACKET}, - {.width = 1, - .icon = NULL, - .key = "]", - .shift_key = "}", - .value = HID_KEYBOARD_CLOSE_BRACKET}, - }, - { - {.width = 1, .icon = NULL, .key = "a", .shift_key = "A", .value = HID_KEYBOARD_A}, - {.width = 1, .icon = NULL, .key = "s", .shift_key = "S", .value = HID_KEYBOARD_S}, - {.width = 1, .icon = NULL, .key = "d", .shift_key = "D", .value = HID_KEYBOARD_D}, - {.width = 1, .icon = NULL, .key = "f", .shift_key = "F", .value = HID_KEYBOARD_F}, - {.width = 1, .icon = NULL, .key = "g", .shift_key = "G", .value = HID_KEYBOARD_G}, - {.width = 1, .icon = NULL, .key = "h", .shift_key = "H", .value = HID_KEYBOARD_H}, - {.width = 1, .icon = NULL, .key = "j", .shift_key = "J", .value = HID_KEYBOARD_J}, - {.width = 1, .icon = NULL, .key = "k", .shift_key = "K", .value = HID_KEYBOARD_K}, - {.width = 1, .icon = NULL, .key = "l", .shift_key = "L", .value = HID_KEYBOARD_L}, - {.width = 1, .icon = NULL, .key = ";", .shift_key = ":", .value = HID_KEYBOARD_SEMICOLON}, - {.width = 2, .icon = &I_Pin_arrow_right_9x7, .value = HID_KEYBOARD_RETURN}, - {.width = 0, .value = HID_KEYBOARD_RETURN}, - }, - { - {.width = 1, .icon = NULL, .key = "z", .shift_key = "Z", .value = HID_KEYBOARD_Z}, - {.width = 1, .icon = NULL, .key = "x", .shift_key = "X", .value = HID_KEYBOARD_X}, - {.width = 1, .icon = NULL, .key = "c", .shift_key = "C", .value = HID_KEYBOARD_C}, - {.width = 1, .icon = NULL, .key = "v", .shift_key = "V", .value = HID_KEYBOARD_V}, - {.width = 1, .icon = NULL, .key = "b", .shift_key = "B", .value = HID_KEYBOARD_B}, - {.width = 1, .icon = NULL, .key = "n", .shift_key = "N", .value = HID_KEYBOARD_N}, - {.width = 1, .icon = NULL, .key = "m", .shift_key = "M", .value = HID_KEYBOARD_M}, - {.width = 1, .icon = NULL, .key = "/", .shift_key = "?", .value = HID_KEYBOARD_SLASH}, - {.width = 1, .icon = NULL, .key = "\\", .shift_key = "|", .value = HID_KEYBOARD_BACKSLASH}, - {.width = 1, .icon = NULL, .key = "`", .shift_key = "~", .value = HID_KEYBOARD_GRAVE_ACCENT}, - {.width = 1, .icon = &I_ButtonUp_7x4, .value = HID_KEYBOARD_UP_ARROW}, - {.width = 1, .icon = NULL, .key = "-", .shift_key = "_", .value = HID_KEYBOARD_MINUS}, - }, - { - {.width = 1, .icon = &I_Pin_arrow_up_7x9, .value = HID_KEYBOARD_L_SHIFT}, - {.width = 1, .icon = NULL, .key = ",", .shift_key = "<", .value = HID_KEYBOARD_COMMA}, - {.width = 1, .icon = NULL, .key = ".", .shift_key = ">", .value = HID_KEYBOARD_DOT}, - {.width = 4, .icon = NULL, .key = " ", .value = HID_KEYBOARD_SPACEBAR}, - {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, - {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, - {.width = 0, .value = HID_KEYBOARD_SPACEBAR}, - {.width = 1, .icon = NULL, .key = "'", .shift_key = "\"", .value = HID_KEYBOARD_APOSTROPHE}, - {.width = 1, .icon = NULL, .key = "=", .shift_key = "+", .value = HID_KEYBOARD_EQUAL_SIGN}, - {.width = 1, .icon = &I_ButtonLeft_4x7, .value = HID_KEYBOARD_LEFT_ARROW}, - {.width = 1, .icon = &I_ButtonDown_7x4, .value = HID_KEYBOARD_DOWN_ARROW}, - {.width = 1, .icon = &I_ButtonRight_4x7, .value = HID_KEYBOARD_RIGHT_ARROW}, - }, - { - {.width = 3, .icon = NULL, .key = "Ctrl", .value = HID_KEYBOARD_L_CTRL}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_CTRL}, - {.width = 3, .icon = NULL, .key = "Alt", .value = HID_KEYBOARD_L_ALT}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_ALT}, - {.width = 3, .icon = NULL, .key = "Cmd", .value = HID_KEYBOARD_L_GUI}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_L_GUI}, - {.width = 3, .icon = NULL, .key = "Tab", .value = HID_KEYBOARD_TAB}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, - {.width = 0, .icon = NULL, .value = HID_KEYBOARD_TAB}, - }, -}; - -static void hid_keyboard_to_upper(char* str) { - while(*str) { - *str = toupper((unsigned char)*str); - str++; - } -} - -static void hid_keyboard_draw_key( - Canvas* canvas, - HidKeyboardModel* model, - uint8_t x, - uint8_t y, - HidKeyboardKey key, - bool selected) { - if(!key.width) return; - - canvas_set_color(canvas, ColorBlack); - uint8_t keyWidth = KEY_WIDTH * key.width + KEY_PADDING * (key.width - 1); - if(selected) { - // Draw a filled box - elements_slightly_rounded_box( - canvas, - MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), - MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), - keyWidth, - KEY_HEIGHT); - canvas_set_color(canvas, ColorWhite); - } else { - // Draw a framed box - elements_slightly_rounded_frame( - canvas, - MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING), - MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING), - keyWidth, - KEY_HEIGHT); - } - if(key.icon != NULL) { - // Draw the icon centered on the button - canvas_draw_icon( - canvas, - MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 - key.icon->width / 2, - MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2 - key.icon->height / 2, - key.icon); - } else { - // If shift is toggled use the shift key when available - strcpy(model->key_string, (model->shift && key.shift_key != 0) ? key.shift_key : key.key); - // Upper case if ctrl or alt was toggled true - if((model->ctrl && key.value == HID_KEYBOARD_L_CTRL) || - (model->alt && key.value == HID_KEYBOARD_L_ALT) || - (model->gui && key.value == HID_KEYBOARD_L_GUI)) { - hid_keyboard_to_upper(model->key_string); - } - canvas_draw_str_aligned( - canvas, - MARGIN_LEFT + x * (KEY_WIDTH + KEY_PADDING) + keyWidth / 2 + 1, - MARGIN_TOP + y * (KEY_HEIGHT + KEY_PADDING) + KEY_HEIGHT / 2, - AlignCenter, - AlignCenter, - model->key_string); - } -} - -static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidKeyboardModel* model = context; - - // Header - if((!model->connected) && (model->transport == HidTransportBle)) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keyboard"); - - canvas_draw_icon(canvas, 68, 3, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 127, 4, AlignRight, AlignTop, "Hold to exit"); - - elements_multiline_text_aligned( - canvas, 4, 60, AlignLeft, AlignBottom, "Waiting for Connection..."); - return; // Dont render the keyboard if we are not yet connected - } - - canvas_set_font(canvas, FontKeyboard); - // Start shifting the all keys up if on the next row (Scrolling) - uint8_t initY = model->y == 0 ? 0 : 1; - - if(model->y > 5) { - initY = model->y - 4; - } - - for(uint8_t y = initY; y < ROW_COUNT; y++) { - const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y]; - uint8_t x = 0; - for(uint8_t i = 0; i < COLUMN_COUNT; i++) { - HidKeyboardKey key = keyboardKeyRow[i]; - // Select when the button is hovered - // Select if the button is hovered within its width - // Select if back is clicked and its the backspace key - // Deselect when the button clicked or not hovered - bool keySelected = (x <= model->x && model->x < (x + key.width)) && y == model->y; - bool backSelected = model->back_pressed && key.value == HID_KEYBOARD_DELETE; - hid_keyboard_draw_key( - canvas, - model, - x, - y - initY, - key, - (!model->ok_pressed && keySelected) || backSelected); - x += key.width; - } - } -} - -static uint8_t hid_keyboard_get_selected_key(HidKeyboardModel* model) { - HidKeyboardKey key = hid_keyboard_keyset[model->y][model->x]; - return key.value; -} - -static void hid_keyboard_get_select_key(HidKeyboardModel* model, HidKeyboardPoint delta) { - // Keep going until a valid spot is found, this allows for nulls and zero width keys in the map - do { - const int delta_sum = model->y + delta.y; - model->y = delta_sum < 0 ? ROW_COUNT - 1 : delta_sum % ROW_COUNT; - } while(delta.y != 0 && hid_keyboard_keyset[model->y][model->x].value == 0); - - do { - const int delta_sum = model->x + delta.x; - model->x = delta_sum < 0 ? COLUMN_COUNT - 1 : delta_sum % COLUMN_COUNT; - } while(delta.x != 0 && hid_keyboard_keyset[model->y][model->x].width == - 0); // Skip zero width keys, pretend they are one key -} - -static void hid_keyboard_process(HidKeyboard* hid_keyboard, InputEvent* event) { - with_view_model( - hid_keyboard->view, - HidKeyboardModel * model, - { - if(event->key == InputKeyOk) { - if(event->type == InputTypePress) { - model->ok_pressed = true; - } else if(event->type == InputTypeLong || event->type == InputTypeShort) { - model->last_key_code = hid_keyboard_get_selected_key(model); - - // Toggle the modifier key when clicked, and click the key - if(model->last_key_code == HID_KEYBOARD_L_SHIFT) { - model->shift = !model->shift; - if(model->shift) - model->modifier_code |= KEY_MOD_LEFT_SHIFT; - else - model->modifier_code &= ~KEY_MOD_LEFT_SHIFT; - } else if(model->last_key_code == HID_KEYBOARD_L_ALT) { - model->alt = !model->alt; - if(model->alt) - model->modifier_code |= KEY_MOD_LEFT_ALT; - else - model->modifier_code &= ~KEY_MOD_LEFT_ALT; - } else if(model->last_key_code == HID_KEYBOARD_L_CTRL) { - model->ctrl = !model->ctrl; - if(model->ctrl) - model->modifier_code |= KEY_MOD_LEFT_CTRL; - else - model->modifier_code &= ~KEY_MOD_LEFT_CTRL; - } else if(model->last_key_code == HID_KEYBOARD_L_GUI) { - model->gui = !model->gui; - if(model->gui) - model->modifier_code |= KEY_MOD_LEFT_GUI; - else - model->modifier_code &= ~KEY_MOD_LEFT_GUI; - } - hid_hal_keyboard_press( - hid_keyboard->hid, model->modifier_code | model->last_key_code); - } else if(event->type == InputTypeRelease) { - // Release happens after short and long presses - hid_hal_keyboard_release( - hid_keyboard->hid, model->modifier_code | model->last_key_code); - model->ok_pressed = false; - } - } else if(event->key == InputKeyBack) { - // If back is pressed for a short time, backspace - if(event->type == InputTypePress) { - model->back_pressed = true; - } else if(event->type == InputTypeShort) { - hid_hal_keyboard_press(hid_keyboard->hid, HID_KEYBOARD_DELETE); - hid_hal_keyboard_release(hid_keyboard->hid, HID_KEYBOARD_DELETE); - } else if(event->type == InputTypeRelease) { - model->back_pressed = false; - } - } else if(event->type == InputTypePress || event->type == InputTypeRepeat) { - // Cycle the selected keys - if(event->key == InputKeyUp) { - hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = -1}); - } else if(event->key == InputKeyDown) { - hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 0, .y = 1}); - } else if(event->key == InputKeyLeft) { - hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = -1, .y = 0}); - } else if(event->key == InputKeyRight) { - hid_keyboard_get_select_key(model, (HidKeyboardPoint){.x = 1, .y = 0}); - } - } - }, - true); -} - -static bool hid_keyboard_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidKeyboard* hid_keyboard = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - hid_hal_keyboard_release_all(hid_keyboard->hid); - } else { - hid_keyboard_process(hid_keyboard, event); - consumed = true; - } - - return consumed; -} - -HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) { - HidKeyboard* hid_keyboard = malloc(sizeof(HidKeyboard)); - hid_keyboard->view = view_alloc(); - hid_keyboard->hid = bt_hid; - view_set_context(hid_keyboard->view, hid_keyboard); - view_allocate_model(hid_keyboard->view, ViewModelTypeLocking, sizeof(HidKeyboardModel)); - view_set_draw_callback(hid_keyboard->view, hid_keyboard_draw_callback); - view_set_input_callback(hid_keyboard->view, hid_keyboard_input_callback); - - with_view_model( - hid_keyboard->view, - HidKeyboardModel * model, - { - model->transport = bt_hid->transport; - model->y = 1; - }, - true); - - return hid_keyboard; -} - -void hid_keyboard_free(HidKeyboard* hid_keyboard) { - furi_assert(hid_keyboard); - view_free(hid_keyboard->view); - free(hid_keyboard); -} - -View* hid_keyboard_get_view(HidKeyboard* hid_keyboard) { - furi_assert(hid_keyboard); - return hid_keyboard->view; -} - -void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected) { - furi_assert(hid_keyboard); - with_view_model( - hid_keyboard->view, HidKeyboardModel * model, { model->connected = connected; }, true); -} diff --git a/applications/external/hid_app/views/hid_keyboard.h b/applications/external/hid_app/views/hid_keyboard.h deleted file mode 100644 index 712771364..000000000 --- a/applications/external/hid_app/views/hid_keyboard.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -typedef struct Hid Hid; -typedef struct HidKeyboard HidKeyboard; - -HidKeyboard* hid_keyboard_alloc(Hid* bt_hid); - -void hid_keyboard_free(HidKeyboard* hid_keyboard); - -View* hid_keyboard_get_view(HidKeyboard* hid_keyboard); - -void hid_keyboard_set_connected_status(HidKeyboard* hid_keyboard, bool connected); diff --git a/applications/external/hid_app/views/hid_keynote.c b/applications/external/hid_app/views/hid_keynote.c deleted file mode 100644 index 543363bf6..000000000 --- a/applications/external/hid_app/views/hid_keynote.c +++ /dev/null @@ -1,312 +0,0 @@ -#include "hid_keynote.h" -#include -#include "../hid.h" - -#include "hid_icons.h" - -#define TAG "HidKeynote" - -struct HidKeynote { - View* view; - Hid* hid; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool back_pressed; - bool connected; - HidTransport transport; -} HidKeynoteModel; - -static void hid_keynote_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { - canvas_draw_triangle(canvas, x, y, 5, 3, dir); - if(dir == CanvasDirectionBottomToTop) { - canvas_draw_line(canvas, x, y + 6, x, y - 1); - } else if(dir == CanvasDirectionTopToBottom) { - canvas_draw_line(canvas, x, y - 6, x, y + 1); - } else if(dir == CanvasDirectionRightToLeft) { - canvas_draw_line(canvas, x + 6, y, x - 1, y); - } else if(dir == CanvasDirectionLeftToRight) { - canvas_draw_line(canvas, x - 6, y, x + 1, y); - } -} - -static void hid_keynote_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidKeynoteModel* model = context; - - // Header - if(model->transport == HidTransportBle) { - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - } - } - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Keynote"); - - canvas_draw_icon(canvas, 68, 2, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 127, 3, AlignRight, AlignTop, "Hold to exit"); - - // Up - canvas_draw_icon(canvas, 21, 24, &I_Button_18x18); - if(model->up_pressed) { - elements_slightly_rounded_box(canvas, 24, 26, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_draw_arrow(canvas, 30, 30, CanvasDirectionBottomToTop); - canvas_set_color(canvas, ColorBlack); - - // Down - canvas_draw_icon(canvas, 21, 45, &I_Button_18x18); - if(model->down_pressed) { - elements_slightly_rounded_box(canvas, 24, 47, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_draw_arrow(canvas, 30, 55, CanvasDirectionTopToBottom); - canvas_set_color(canvas, ColorBlack); - - // Left - canvas_draw_icon(canvas, 0, 45, &I_Button_18x18); - if(model->left_pressed) { - elements_slightly_rounded_box(canvas, 3, 47, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_draw_arrow(canvas, 7, 53, CanvasDirectionRightToLeft); - canvas_set_color(canvas, ColorBlack); - - // Right - canvas_draw_icon(canvas, 42, 45, &I_Button_18x18); - if(model->right_pressed) { - elements_slightly_rounded_box(canvas, 45, 47, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_draw_arrow(canvas, 53, 53, CanvasDirectionLeftToRight); - canvas_set_color(canvas, ColorBlack); - - // Ok - canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); - if(model->ok_pressed) { - elements_slightly_rounded_box(canvas, 66, 27, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Space"); - canvas_set_color(canvas, ColorBlack); - - // Back - canvas_draw_icon(canvas, 63, 45, &I_Space_65x18); - if(model->back_pressed) { - elements_slightly_rounded_box(canvas, 66, 47, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); - elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Back"); -} - -static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidKeynoteModel* model = context; - - // Header - if(model->transport == HidTransportBle) { - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - } - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote"); - } else { - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote"); - } - - canvas_draw_icon(canvas, 2, 18, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 15, 19, AlignLeft, AlignTop, "Hold to exit"); - - const uint8_t x_2 = 23; - const uint8_t x_1 = 2; - const uint8_t x_3 = 44; - - const uint8_t y_1 = 44; - const uint8_t y_2 = 65; - - // Up - canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); - if(model->up_pressed) { - elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_draw_arrow(canvas, x_2 + 9, y_1 + 6, CanvasDirectionBottomToTop); - canvas_set_color(canvas, ColorBlack); - - // Down - canvas_draw_icon(canvas, x_2, y_2, &I_Button_18x18); - if(model->down_pressed) { - elements_slightly_rounded_box(canvas, x_2 + 3, y_2 + 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_draw_arrow(canvas, x_2 + 9, y_2 + 10, CanvasDirectionTopToBottom); - canvas_set_color(canvas, ColorBlack); - - // Left - canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); - if(model->left_pressed) { - elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_draw_arrow(canvas, x_1 + 7, y_2 + 8, CanvasDirectionRightToLeft); - canvas_set_color(canvas, ColorBlack); - - // Right - canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); - if(model->right_pressed) { - elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); - canvas_set_color(canvas, ColorWhite); - } - hid_keynote_draw_arrow(canvas, x_3 + 11, y_2 + 8, CanvasDirectionLeftToRight); - canvas_set_color(canvas, ColorBlack); - - // Ok - canvas_draw_icon(canvas, 2, 86, &I_Space_60x18); - if(model->ok_pressed) { - elements_slightly_rounded_box(canvas, 5, 88, 55, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 11, 90, &I_Ok_btn_9x9); - elements_multiline_text_aligned(canvas, 26, 98, AlignLeft, AlignBottom, "Space"); - canvas_set_color(canvas, ColorBlack); - - // Back - canvas_draw_icon(canvas, 2, 107, &I_Space_60x18); - if(model->back_pressed) { - elements_slightly_rounded_box(canvas, 5, 109, 55, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 11, 111, &I_Pin_back_arrow_10x8); - elements_multiline_text_aligned(canvas, 26, 119, AlignLeft, AlignBottom, "Back"); -} - -static void hid_keynote_process(HidKeynote* hid_keynote, InputEvent* event) { - with_view_model( - hid_keynote->view, - HidKeynoteModel * model, - { - if(event->type == InputTypePress) { - if(event->key == InputKeyUp) { - model->up_pressed = true; - hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); - } else if(event->key == InputKeyBack) { - model->back_pressed = true; - } - } else if(event->type == InputTypeRelease) { - if(event->key == InputKeyUp) { - model->up_pressed = false; - hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_UP_ARROW); - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DOWN_ARROW); - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_LEFT_ARROW); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_RIGHT_ARROW); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_SPACEBAR); - } else if(event->key == InputKeyBack) { - model->back_pressed = false; - } - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - hid_hal_keyboard_press(hid_keynote->hid, HID_KEYBOARD_DELETE); - hid_hal_keyboard_release(hid_keynote->hid, HID_KEYBOARD_DELETE); - hid_hal_consumer_key_press(hid_keynote->hid, HID_CONSUMER_AC_BACK); - hid_hal_consumer_key_release(hid_keynote->hid, HID_CONSUMER_AC_BACK); - } - } - }, - true); -} - -static bool hid_keynote_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidKeynote* hid_keynote = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - hid_hal_keyboard_release_all(hid_keynote->hid); - } else { - hid_keynote_process(hid_keynote, event); - consumed = true; - } - - return consumed; -} - -HidKeynote* hid_keynote_alloc(Hid* hid) { - HidKeynote* hid_keynote = malloc(sizeof(HidKeynote)); - hid_keynote->view = view_alloc(); - hid_keynote->hid = hid; - view_set_context(hid_keynote->view, hid_keynote); - view_allocate_model(hid_keynote->view, ViewModelTypeLocking, sizeof(HidKeynoteModel)); - view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); - view_set_input_callback(hid_keynote->view, hid_keynote_input_callback); - - with_view_model( - hid_keynote->view, HidKeynoteModel * model, { model->transport = hid->transport; }, true); - - return hid_keynote; -} - -void hid_keynote_free(HidKeynote* hid_keynote) { - furi_assert(hid_keynote); - view_free(hid_keynote->view); - free(hid_keynote); -} - -View* hid_keynote_get_view(HidKeynote* hid_keynote) { - furi_assert(hid_keynote); - return hid_keynote->view; -} - -void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected) { - furi_assert(hid_keynote); - with_view_model( - hid_keynote->view, HidKeynoteModel * model, { model->connected = connected; }, true); -} - -void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical) { - furi_assert(hid_keynote); - - if(vertical) { - view_set_draw_callback(hid_keynote->view, hid_keynote_draw_vertical_callback); - view_set_orientation(hid_keynote->view, ViewOrientationVerticalFlip); - - } else { - view_set_draw_callback(hid_keynote->view, hid_keynote_draw_callback); - view_set_orientation(hid_keynote->view, ViewOrientationHorizontal); - } -} diff --git a/applications/external/hid_app/views/hid_keynote.h b/applications/external/hid_app/views/hid_keynote.h deleted file mode 100644 index 84bfed4ce..000000000 --- a/applications/external/hid_app/views/hid_keynote.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -typedef struct Hid Hid; -typedef struct HidKeynote HidKeynote; - -HidKeynote* hid_keynote_alloc(Hid* bt_hid); - -void hid_keynote_free(HidKeynote* hid_keynote); - -View* hid_keynote_get_view(HidKeynote* hid_keynote); - -void hid_keynote_set_connected_status(HidKeynote* hid_keynote, bool connected); - -void hid_keynote_set_orientation(HidKeynote* hid_keynote, bool vertical); diff --git a/applications/external/hid_app/views/hid_media.c b/applications/external/hid_app/views/hid_media.c deleted file mode 100644 index 468529d56..000000000 --- a/applications/external/hid_app/views/hid_media.c +++ /dev/null @@ -1,218 +0,0 @@ -#include "hid_media.h" -#include -#include -#include -#include -#include "../hid.h" - -#include "hid_icons.h" - -#define TAG "HidMedia" - -struct HidMedia { - View* view; - Hid* hid; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool connected; - HidTransport transport; -} HidMediaModel; - -static void hid_media_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { - canvas_draw_triangle(canvas, x, y, 5, 3, dir); - if(dir == CanvasDirectionBottomToTop) { - canvas_draw_dot(canvas, x, y - 1); - } else if(dir == CanvasDirectionTopToBottom) { - canvas_draw_dot(canvas, x, y + 1); - } else if(dir == CanvasDirectionRightToLeft) { - canvas_draw_dot(canvas, x - 1, y); - } else if(dir == CanvasDirectionLeftToRight) { - canvas_draw_dot(canvas, x + 1, y); - } -} - -static void hid_media_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidMediaModel* model = context; - - // Header - if(model->transport == HidTransportBle) { - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - } - } - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Media"); - canvas_set_font(canvas, FontSecondary); - - // Keypad circles - canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); - - // Up - if(model->up_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 96, 12, &I_Volup_8x6); - canvas_set_color(canvas, ColorBlack); - - // Down - if(model->down_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 96, 45, &I_Voldwn_6x6); - canvas_set_color(canvas, ColorBlack); - - // Left - if(model->left_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - hid_media_draw_arrow(canvas, 82, 31, CanvasDirectionRightToLeft); - hid_media_draw_arrow(canvas, 86, 31, CanvasDirectionRightToLeft); - canvas_set_color(canvas, ColorBlack); - - // Right - if(model->right_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - hid_media_draw_arrow(canvas, 112, 31, CanvasDirectionLeftToRight); - hid_media_draw_arrow(canvas, 116, 31, CanvasDirectionLeftToRight); - canvas_set_color(canvas, ColorBlack); - - // Ok - if(model->ok_pressed) { - canvas_draw_icon(canvas, 93, 25, &I_Pressed_Button_13x13); - canvas_set_color(canvas, ColorWhite); - } - hid_media_draw_arrow(canvas, 96, 31, CanvasDirectionLeftToRight); - canvas_draw_line(canvas, 100, 29, 100, 33); - canvas_draw_line(canvas, 102, 29, 102, 33); - canvas_set_color(canvas, ColorBlack); - - // Exit - canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); -} - -static void hid_media_process_press(HidMedia* hid_media, InputEvent* event) { - with_view_model( - hid_media->view, - HidMediaModel * model, - { - if(event->key == InputKeyUp) { - model->up_pressed = true; - hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - hid_hal_consumer_key_press(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); - } - }, - true); -} - -static void hid_media_process_release(HidMedia* hid_media, InputEvent* event) { - with_view_model( - hid_media->view, - HidMediaModel * model, - { - if(event->key == InputKeyUp) { - model->up_pressed = false; - hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_PREVIOUS_TRACK); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_SCAN_NEXT_TRACK); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - hid_hal_consumer_key_release(hid_media->hid, HID_CONSUMER_PLAY_PAUSE); - } - }, - true); -} - -static bool hid_media_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidMedia* hid_media = context; - bool consumed = false; - - if(event->type == InputTypePress) { - hid_media_process_press(hid_media, event); - consumed = true; - } else if(event->type == InputTypeRelease) { - hid_media_process_release(hid_media, event); - consumed = true; - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - hid_hal_consumer_key_release_all(hid_media->hid); - } - } - - return consumed; -} - -HidMedia* hid_media_alloc(Hid* hid) { - HidMedia* hid_media = malloc(sizeof(HidMedia)); - hid_media->view = view_alloc(); - hid_media->hid = hid; - view_set_context(hid_media->view, hid_media); - view_allocate_model(hid_media->view, ViewModelTypeLocking, sizeof(HidMediaModel)); - view_set_draw_callback(hid_media->view, hid_media_draw_callback); - view_set_input_callback(hid_media->view, hid_media_input_callback); - - with_view_model( - hid_media->view, HidMediaModel * model, { model->transport = hid->transport; }, true); - - return hid_media; -} - -void hid_media_free(HidMedia* hid_media) { - furi_assert(hid_media); - view_free(hid_media->view); - free(hid_media); -} - -View* hid_media_get_view(HidMedia* hid_media) { - furi_assert(hid_media); - return hid_media->view; -} - -void hid_media_set_connected_status(HidMedia* hid_media, bool connected) { - furi_assert(hid_media); - with_view_model( - hid_media->view, HidMediaModel * model, { model->connected = connected; }, true); -} diff --git a/applications/external/hid_app/views/hid_media.h b/applications/external/hid_app/views/hid_media.h deleted file mode 100644 index 4aa51dc17..000000000 --- a/applications/external/hid_app/views/hid_media.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -typedef struct HidMedia HidMedia; - -HidMedia* hid_media_alloc(); - -void hid_media_free(HidMedia* hid_media); - -View* hid_media_get_view(HidMedia* hid_media); - -void hid_media_set_connected_status(HidMedia* hid_media, bool connected); diff --git a/applications/external/hid_app/views/hid_mouse.c b/applications/external/hid_app/views/hid_mouse.c deleted file mode 100644 index 30a9d9d06..000000000 --- a/applications/external/hid_app/views/hid_mouse.c +++ /dev/null @@ -1,226 +0,0 @@ -#include "hid_mouse.h" -#include -#include "../hid.h" - -#include "hid_icons.h" - -#define TAG "HidMouse" - -struct HidMouse { - View* view; - Hid* hid; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool left_mouse_pressed; - bool left_mouse_held; - bool right_mouse_pressed; - bool connected; - HidTransport transport; -} HidMouseModel; - -static void hid_mouse_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidMouseModel* model = context; - - // Header - if(model->transport == HidTransportBle) { - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - } - } - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse"); - canvas_set_font(canvas, FontSecondary); - - if(model->left_mouse_held == true) { - elements_multiline_text_aligned(canvas, 0, 62, AlignLeft, AlignBottom, "Selecting..."); - } else { - canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); - } - - // Keypad circles - canvas_draw_icon(canvas, 64, 8, &I_Circles_47x47); - - // Up - if(model->up_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 81, 9, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 84, 10, &I_Pin_arrow_up_7x9); - canvas_set_color(canvas, ColorBlack); - - // Down - if(model->down_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 81, 41, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 84, 43, &I_Pin_arrow_down_7x9); - canvas_set_color(canvas, ColorBlack); - - // Left - if(model->left_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 65, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 67, 28, &I_Pin_arrow_left_9x7); - canvas_set_color(canvas, ColorBlack); - - // Right - if(model->right_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 97, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 99, 28, &I_Pin_arrow_right_9x7); - canvas_set_color(canvas, ColorBlack); - - // Ok - if(model->left_mouse_pressed) { - canvas_draw_icon(canvas, 81, 25, &I_Ok_btn_pressed_13x13); - } else { - canvas_draw_icon(canvas, 83, 27, &I_Left_mouse_icon_9x9); - } - - // Back - if(model->right_mouse_pressed) { - canvas_draw_icon(canvas, 108, 48, &I_Ok_btn_pressed_13x13); - } else { - canvas_draw_icon(canvas, 110, 50, &I_Right_mouse_icon_9x9); - } -} - -static void hid_mouse_process(HidMouse* hid_mouse, InputEvent* event) { - with_view_model( - hid_mouse->view, - HidMouseModel * model, - { - if(event->key == InputKeyBack) { - if(event->type == InputTypeShort) { - hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); - hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_RIGHT); - } else if(event->type == InputTypePress) { - model->right_mouse_pressed = true; - } else if(event->type == InputTypeRelease) { - model->right_mouse_pressed = false; - } - } else if(event->key == InputKeyOk) { - if(event->type == InputTypeShort) { - // Just release if it was being held before - if(!model->left_mouse_held) - hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); - hid_hal_mouse_release(hid_mouse->hid, HID_MOUSE_BTN_LEFT); - model->left_mouse_held = false; - } else if(event->type == InputTypeLong) { - hid_hal_mouse_press(hid_mouse->hid, HID_MOUSE_BTN_LEFT); - model->left_mouse_held = true; - model->left_mouse_pressed = true; - } else if(event->type == InputTypePress) { - model->left_mouse_pressed = true; - } else if(event->type == InputTypeRelease) { - // Only release if it wasn't a long press - if(!model->left_mouse_held) model->left_mouse_pressed = false; - } - } else if(event->key == InputKeyRight) { - if(event->type == InputTypePress) { - model->right_pressed = true; - hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_SHORT, 0); - } else if(event->type == InputTypeRepeat) { - hid_hal_mouse_move(hid_mouse->hid, MOUSE_MOVE_LONG, 0); - } else if(event->type == InputTypeRelease) { - model->right_pressed = false; - } - } else if(event->key == InputKeyLeft) { - if(event->type == InputTypePress) { - model->left_pressed = true; - hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_SHORT, 0); - } else if(event->type == InputTypeRepeat) { - hid_hal_mouse_move(hid_mouse->hid, -MOUSE_MOVE_LONG, 0); - } else if(event->type == InputTypeRelease) { - model->left_pressed = false; - } - } else if(event->key == InputKeyDown) { - if(event->type == InputTypePress) { - model->down_pressed = true; - hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_SHORT); - } else if(event->type == InputTypeRepeat) { - hid_hal_mouse_move(hid_mouse->hid, 0, MOUSE_MOVE_LONG); - } else if(event->type == InputTypeRelease) { - model->down_pressed = false; - } - } else if(event->key == InputKeyUp) { - if(event->type == InputTypePress) { - model->up_pressed = true; - hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_SHORT); - } else if(event->type == InputTypeRepeat) { - hid_hal_mouse_move(hid_mouse->hid, 0, -MOUSE_MOVE_LONG); - } else if(event->type == InputTypeRelease) { - model->up_pressed = false; - } - } - }, - true); -} - -static bool hid_mouse_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidMouse* hid_mouse = context; - bool consumed = false; - - if(event->type == InputTypeLong && event->key == InputKeyBack) { - hid_hal_mouse_release_all(hid_mouse->hid); - } else { - hid_mouse_process(hid_mouse, event); - consumed = true; - } - - return consumed; -} - -HidMouse* hid_mouse_alloc(Hid* hid) { - HidMouse* hid_mouse = malloc(sizeof(HidMouse)); - hid_mouse->view = view_alloc(); - hid_mouse->hid = hid; - view_set_context(hid_mouse->view, hid_mouse); - view_allocate_model(hid_mouse->view, ViewModelTypeLocking, sizeof(HidMouseModel)); - view_set_draw_callback(hid_mouse->view, hid_mouse_draw_callback); - view_set_input_callback(hid_mouse->view, hid_mouse_input_callback); - - with_view_model( - hid_mouse->view, HidMouseModel * model, { model->transport = hid->transport; }, true); - - return hid_mouse; -} - -void hid_mouse_free(HidMouse* hid_mouse) { - furi_assert(hid_mouse); - view_free(hid_mouse->view); - free(hid_mouse); -} - -View* hid_mouse_get_view(HidMouse* hid_mouse) { - furi_assert(hid_mouse); - return hid_mouse->view; -} - -void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected) { - furi_assert(hid_mouse); - with_view_model( - hid_mouse->view, HidMouseModel * model, { model->connected = connected; }, true); -} diff --git a/applications/external/hid_app/views/hid_mouse.h b/applications/external/hid_app/views/hid_mouse.h deleted file mode 100644 index d9fb2fd88..000000000 --- a/applications/external/hid_app/views/hid_mouse.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 - -typedef struct Hid Hid; -typedef struct HidMouse HidMouse; - -HidMouse* hid_mouse_alloc(Hid* bt_hid); - -void hid_mouse_free(HidMouse* hid_mouse); - -View* hid_mouse_get_view(HidMouse* hid_mouse); - -void hid_mouse_set_connected_status(HidMouse* hid_mouse, bool connected); diff --git a/applications/external/hid_app/views/hid_mouse_clicker.c b/applications/external/hid_app/views/hid_mouse_clicker.c deleted file mode 100644 index d85affc43..000000000 --- a/applications/external/hid_app/views/hid_mouse_clicker.c +++ /dev/null @@ -1,214 +0,0 @@ -#include "hid_mouse_clicker.h" -#include -#include "../hid.h" - -#include "hid_icons.h" - -#define TAG "HidMouseClicker" -#define DEFAULT_CLICK_RATE 1 -#define MAXIMUM_CLICK_RATE 60 - -struct HidMouseClicker { - View* view; - Hid* hid; - FuriTimer* timer; -}; - -typedef struct { - bool connected; - bool running; - int rate; - HidTransport transport; -} HidMouseClickerModel; - -static void hid_mouse_clicker_start_or_restart_timer(void* context) { - furi_assert(context); - HidMouseClicker* hid_mouse_clicker = context; - - if(furi_timer_is_running(hid_mouse_clicker->timer)) { - furi_timer_stop(hid_mouse_clicker->timer); - } - - with_view_model( - hid_mouse_clicker->view, - HidMouseClickerModel * model, - { - furi_timer_start( - hid_mouse_clicker->timer, furi_kernel_get_tick_frequency() / model->rate); - }, - true); -} - -static void hid_mouse_clicker_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidMouseClickerModel* model = context; - - // Header - if(model->transport == HidTransportBle) { - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - } - } - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Clicker"); - - // Ok - canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); - if(model->running) { - canvas_set_font(canvas, FontPrimary); - - FuriString* rate_label = furi_string_alloc(); - furi_string_printf(rate_label, "%d clicks/s\n\nUp / Down", model->rate); - elements_multiline_text(canvas, AlignLeft, 35, furi_string_get_cstr(rate_label)); - canvas_set_font(canvas, FontSecondary); - furi_string_free(rate_label); - - elements_slightly_rounded_box(canvas, 66, 27, 60, 13); - canvas_set_color(canvas, ColorWhite); - } else { - canvas_set_font(canvas, FontPrimary); - elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto start\nclicking"); - canvas_set_font(canvas, FontSecondary); - } - canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); - if(model->running) { - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop"); - } else { - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start"); - } - canvas_set_color(canvas, ColorBlack); - - // Back - canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); - elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit"); -} - -static void hid_mouse_clicker_timer_callback(void* context) { - furi_assert(context); - HidMouseClicker* hid_mouse_clicker = context; - with_view_model( - hid_mouse_clicker->view, - HidMouseClickerModel * model, - { - if(model->running) { - hid_hal_mouse_press(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); - hid_hal_mouse_release(hid_mouse_clicker->hid, HID_MOUSE_BTN_LEFT); - } - }, - false); -} - -static void hid_mouse_clicker_enter_callback(void* context) { - hid_mouse_clicker_start_or_restart_timer(context); -} - -static void hid_mouse_clicker_exit_callback(void* context) { - furi_assert(context); - HidMouseClicker* hid_mouse_clicker = context; - furi_timer_stop(hid_mouse_clicker->timer); -} - -static bool hid_mouse_clicker_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidMouseClicker* hid_mouse_clicker = context; - - bool consumed = false; - bool rate_changed = false; - - if(event->type != InputTypeShort && event->type != InputTypeRepeat) { - return false; - } - - with_view_model( - hid_mouse_clicker->view, - HidMouseClickerModel * model, - { - switch(event->key) { - case InputKeyOk: - model->running = !model->running; - consumed = true; - break; - case InputKeyUp: - if(model->rate < MAXIMUM_CLICK_RATE) { - model->rate++; - } - rate_changed = true; - consumed = true; - break; - case InputKeyDown: - if(model->rate > 1) { - model->rate--; - } - rate_changed = true; - consumed = true; - break; - default: - consumed = true; - break; - } - }, - true); - - if(rate_changed) { - hid_mouse_clicker_start_or_restart_timer(context); - } - - return consumed; -} - -HidMouseClicker* hid_mouse_clicker_alloc(Hid* hid) { - HidMouseClicker* hid_mouse_clicker = malloc(sizeof(HidMouseClicker)); - - hid_mouse_clicker->view = view_alloc(); - view_set_context(hid_mouse_clicker->view, hid_mouse_clicker); - view_allocate_model( - hid_mouse_clicker->view, ViewModelTypeLocking, sizeof(HidMouseClickerModel)); - view_set_draw_callback(hid_mouse_clicker->view, hid_mouse_clicker_draw_callback); - view_set_input_callback(hid_mouse_clicker->view, hid_mouse_clicker_input_callback); - view_set_enter_callback(hid_mouse_clicker->view, hid_mouse_clicker_enter_callback); - view_set_exit_callback(hid_mouse_clicker->view, hid_mouse_clicker_exit_callback); - - hid_mouse_clicker->hid = hid; - - hid_mouse_clicker->timer = furi_timer_alloc( - hid_mouse_clicker_timer_callback, FuriTimerTypePeriodic, hid_mouse_clicker); - - with_view_model( - hid_mouse_clicker->view, - HidMouseClickerModel * model, - { - model->transport = hid->transport; - model->rate = DEFAULT_CLICK_RATE; - }, - true); - - return hid_mouse_clicker; -} - -void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker) { - furi_assert(hid_mouse_clicker); - - furi_timer_stop(hid_mouse_clicker->timer); - furi_timer_free(hid_mouse_clicker->timer); - - view_free(hid_mouse_clicker->view); - - free(hid_mouse_clicker); -} - -View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker) { - furi_assert(hid_mouse_clicker); - return hid_mouse_clicker->view; -} - -void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected) { - furi_assert(hid_mouse_clicker); - with_view_model( - hid_mouse_clicker->view, - HidMouseClickerModel * model, - { model->connected = connected; }, - true); -} diff --git a/applications/external/hid_app/views/hid_mouse_clicker.h b/applications/external/hid_app/views/hid_mouse_clicker.h deleted file mode 100644 index d72847baa..000000000 --- a/applications/external/hid_app/views/hid_mouse_clicker.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -typedef struct Hid Hid; -typedef struct HidMouseClicker HidMouseClicker; - -HidMouseClicker* hid_mouse_clicker_alloc(Hid* bt_hid); - -void hid_mouse_clicker_free(HidMouseClicker* hid_mouse_clicker); - -View* hid_mouse_clicker_get_view(HidMouseClicker* hid_mouse_clicker); - -void hid_mouse_clicker_set_connected_status(HidMouseClicker* hid_mouse_clicker, bool connected); diff --git a/applications/external/hid_app/views/hid_mouse_jiggler.c b/applications/external/hid_app/views/hid_mouse_jiggler.c deleted file mode 100644 index 15547eb26..000000000 --- a/applications/external/hid_app/views/hid_mouse_jiggler.c +++ /dev/null @@ -1,159 +0,0 @@ -#include "hid_mouse_jiggler.h" -#include -#include "../hid.h" - -#include "hid_icons.h" - -#define TAG "HidMouseJiggler" - -struct HidMouseJiggler { - View* view; - Hid* hid; - FuriTimer* timer; -}; - -typedef struct { - bool connected; - bool running; - uint8_t counter; - HidTransport transport; -} HidMouseJigglerModel; - -static void hid_mouse_jiggler_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidMouseJigglerModel* model = context; - - // Header - if(model->transport == HidTransportBle) { - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - } - } - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Mouse Jiggler"); - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text(canvas, AlignLeft, 35, "Press Start\nto jiggle"); - canvas_set_font(canvas, FontSecondary); - - // Ok - canvas_draw_icon(canvas, 63, 25, &I_Space_65x18); - if(model->running) { - elements_slightly_rounded_box(canvas, 66, 27, 60, 13); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 74, 29, &I_Ok_btn_9x9); - if(model->running) { - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Stop"); - } else { - elements_multiline_text_aligned(canvas, 91, 36, AlignLeft, AlignBottom, "Start"); - } - canvas_set_color(canvas, ColorBlack); - - // Back - canvas_draw_icon(canvas, 74, 49, &I_Pin_back_arrow_10x8); - elements_multiline_text_aligned(canvas, 91, 57, AlignLeft, AlignBottom, "Quit"); -} - -static void hid_mouse_jiggler_timer_callback(void* context) { - furi_assert(context); - HidMouseJiggler* hid_mouse_jiggler = context; - with_view_model( - hid_mouse_jiggler->view, - HidMouseJigglerModel * model, - { - if(model->running) { - model->counter++; - hid_hal_mouse_move( - hid_mouse_jiggler->hid, - (model->counter % 2 == 0) ? MOUSE_MOVE_SHORT : -MOUSE_MOVE_SHORT, - 0); - } - }, - false); -} - -static void hid_mouse_jiggler_enter_callback(void* context) { - furi_assert(context); - HidMouseJiggler* hid_mouse_jiggler = context; - - furi_timer_start(hid_mouse_jiggler->timer, 500); -} - -static void hid_mouse_jiggler_exit_callback(void* context) { - furi_assert(context); - HidMouseJiggler* hid_mouse_jiggler = context; - furi_timer_stop(hid_mouse_jiggler->timer); -} - -static bool hid_mouse_jiggler_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidMouseJiggler* hid_mouse_jiggler = context; - - bool consumed = false; - - if(event->type == InputTypeShort && event->key == InputKeyOk) { - with_view_model( - hid_mouse_jiggler->view, - HidMouseJigglerModel * model, - { model->running = !model->running; }, - true); - consumed = true; - } - - return consumed; -} - -HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* hid) { - HidMouseJiggler* hid_mouse_jiggler = malloc(sizeof(HidMouseJiggler)); - - hid_mouse_jiggler->view = view_alloc(); - view_set_context(hid_mouse_jiggler->view, hid_mouse_jiggler); - view_allocate_model( - hid_mouse_jiggler->view, ViewModelTypeLocking, sizeof(HidMouseJigglerModel)); - view_set_draw_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_draw_callback); - view_set_input_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_input_callback); - view_set_enter_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_enter_callback); - view_set_exit_callback(hid_mouse_jiggler->view, hid_mouse_jiggler_exit_callback); - - hid_mouse_jiggler->hid = hid; - - hid_mouse_jiggler->timer = furi_timer_alloc( - hid_mouse_jiggler_timer_callback, FuriTimerTypePeriodic, hid_mouse_jiggler); - - with_view_model( - hid_mouse_jiggler->view, - HidMouseJigglerModel * model, - { model->transport = hid->transport; }, - true); - - return hid_mouse_jiggler; -} - -void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler) { - furi_assert(hid_mouse_jiggler); - - furi_timer_stop(hid_mouse_jiggler->timer); - furi_timer_free(hid_mouse_jiggler->timer); - - view_free(hid_mouse_jiggler->view); - - free(hid_mouse_jiggler); -} - -View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler) { - furi_assert(hid_mouse_jiggler); - return hid_mouse_jiggler->view; -} - -void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected) { - furi_assert(hid_mouse_jiggler); - with_view_model( - hid_mouse_jiggler->view, - HidMouseJigglerModel * model, - { model->connected = connected; }, - true); -} diff --git a/applications/external/hid_app/views/hid_mouse_jiggler.h b/applications/external/hid_app/views/hid_mouse_jiggler.h deleted file mode 100644 index 0813b4351..000000000 --- a/applications/external/hid_app/views/hid_mouse_jiggler.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 - -typedef struct Hid Hid; -typedef struct HidMouseJiggler HidMouseJiggler; - -HidMouseJiggler* hid_mouse_jiggler_alloc(Hid* bt_hid); - -void hid_mouse_jiggler_free(HidMouseJiggler* hid_mouse_jiggler); - -View* hid_mouse_jiggler_get_view(HidMouseJiggler* hid_mouse_jiggler); - -void hid_mouse_jiggler_set_connected_status(HidMouseJiggler* hid_mouse_jiggler, bool connected); diff --git a/applications/external/hid_app/views/hid_tiktok.c b/applications/external/hid_app/views/hid_tiktok.c deleted file mode 100644 index e1f9f4bed..000000000 --- a/applications/external/hid_app/views/hid_tiktok.c +++ /dev/null @@ -1,241 +0,0 @@ -#include "hid_tiktok.h" -#include "../hid.h" -#include - -#include "hid_icons.h" - -#define TAG "HidTikTok" - -struct HidTikTok { - View* view; - Hid* hid; -}; - -typedef struct { - bool left_pressed; - bool up_pressed; - bool right_pressed; - bool down_pressed; - bool ok_pressed; - bool connected; - bool is_cursor_set; - HidTransport transport; -} HidTikTokModel; - -static void hid_tiktok_draw_callback(Canvas* canvas, void* context) { - furi_assert(context); - HidTikTokModel* model = context; - - // Header - if(model->transport == HidTransportBle) { - if(model->connected) { - canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); - } else { - canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); - } - } - - canvas_set_font(canvas, FontPrimary); - elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "TikTok"); - canvas_set_font(canvas, FontSecondary); - - // Keypad circles - canvas_draw_icon(canvas, 76, 8, &I_Circles_47x47); - - // Up - if(model->up_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 9, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 96, 11, &I_Arr_up_7x9); - canvas_set_color(canvas, ColorBlack); - - // Down - if(model->down_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 93, 41, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 96, 44, &I_Arr_dwn_7x9); - canvas_set_color(canvas, ColorBlack); - - // Left - if(model->left_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 77, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 81, 29, &I_Voldwn_6x6); - canvas_set_color(canvas, ColorBlack); - - // Right - if(model->right_pressed) { - canvas_set_bitmap_mode(canvas, 1); - canvas_draw_icon(canvas, 109, 25, &I_Pressed_Button_13x13); - canvas_set_bitmap_mode(canvas, 0); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_icon(canvas, 111, 29, &I_Volup_8x6); - canvas_set_color(canvas, ColorBlack); - - // Ok - if(model->ok_pressed) { - canvas_draw_icon(canvas, 91, 23, &I_Like_pressed_17x17); - } else { - canvas_draw_icon(canvas, 94, 27, &I_Like_def_11x9); - } - // Exit - canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); -} - -static void hid_tiktok_reset_cursor(HidTikTok* hid_tiktok) { - // Set cursor to the phone's left up corner - // Delays to guarantee one packet per connection interval - for(size_t i = 0; i < 8; i++) { - hid_hal_mouse_move(hid_tiktok->hid, -127, -127); - furi_delay_ms(50); - } - // Move cursor from the corner - hid_hal_mouse_move(hid_tiktok->hid, 20, 120); - furi_delay_ms(50); -} - -static void - hid_tiktok_process_press(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { - if(event->key == InputKeyUp) { - model->up_pressed = true; - } else if(event->key == InputKeyDown) { - model->down_pressed = true; - } else if(event->key == InputKeyLeft) { - model->left_pressed = true; - hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyRight) { - model->right_pressed = true; - hid_hal_consumer_key_press(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyOk) { - model->ok_pressed = true; - } -} - -static void - hid_tiktok_process_release(HidTikTok* hid_tiktok, HidTikTokModel* model, InputEvent* event) { - if(event->key == InputKeyUp) { - model->up_pressed = false; - } else if(event->key == InputKeyDown) { - model->down_pressed = false; - } else if(event->key == InputKeyLeft) { - model->left_pressed = false; - hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_DECREMENT); - } else if(event->key == InputKeyRight) { - model->right_pressed = false; - hid_hal_consumer_key_release(hid_tiktok->hid, HID_CONSUMER_VOLUME_INCREMENT); - } else if(event->key == InputKeyOk) { - model->ok_pressed = false; - } -} - -static bool hid_tiktok_input_callback(InputEvent* event, void* context) { - furi_assert(context); - HidTikTok* hid_tiktok = context; - bool consumed = false; - - with_view_model( - hid_tiktok->view, - HidTikTokModel * model, - { - if(event->type == InputTypePress) { - hid_tiktok_process_press(hid_tiktok, model, event); - if(model->connected && !model->is_cursor_set) { - hid_tiktok_reset_cursor(hid_tiktok); - model->is_cursor_set = true; - } - consumed = true; - } else if(event->type == InputTypeRelease) { - hid_tiktok_process_release(hid_tiktok, model, event); - consumed = true; - } else if(event->type == InputTypeShort) { - if(event->key == InputKeyOk) { - hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); - hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); - hid_hal_mouse_press(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - furi_delay_ms(50); - hid_hal_mouse_release(hid_tiktok->hid, HID_MOUSE_BTN_LEFT); - consumed = true; - } else if(event->key == InputKeyUp) { - // Emulate up swipe - hid_hal_mouse_scroll(hid_tiktok->hid, -6); - hid_hal_mouse_scroll(hid_tiktok->hid, -12); - hid_hal_mouse_scroll(hid_tiktok->hid, -19); - hid_hal_mouse_scroll(hid_tiktok->hid, -12); - hid_hal_mouse_scroll(hid_tiktok->hid, -6); - consumed = true; - } else if(event->key == InputKeyDown) { - // Emulate down swipe - hid_hal_mouse_scroll(hid_tiktok->hid, 6); - hid_hal_mouse_scroll(hid_tiktok->hid, 12); - hid_hal_mouse_scroll(hid_tiktok->hid, 19); - hid_hal_mouse_scroll(hid_tiktok->hid, 12); - hid_hal_mouse_scroll(hid_tiktok->hid, 6); - consumed = true; - } else if(event->key == InputKeyBack) { - hid_hal_consumer_key_release_all(hid_tiktok->hid); - consumed = true; - } - } else if(event->type == InputTypeLong) { - if(event->key == InputKeyBack) { - hid_hal_consumer_key_release_all(hid_tiktok->hid); - model->is_cursor_set = false; - consumed = false; - } - } - }, - true); - - return consumed; -} - -HidTikTok* hid_tiktok_alloc(Hid* bt_hid) { - HidTikTok* hid_tiktok = malloc(sizeof(HidTikTok)); - hid_tiktok->hid = bt_hid; - hid_tiktok->view = view_alloc(); - view_set_context(hid_tiktok->view, hid_tiktok); - view_allocate_model(hid_tiktok->view, ViewModelTypeLocking, sizeof(HidTikTokModel)); - view_set_draw_callback(hid_tiktok->view, hid_tiktok_draw_callback); - view_set_input_callback(hid_tiktok->view, hid_tiktok_input_callback); - - with_view_model( - hid_tiktok->view, HidTikTokModel * model, { model->transport = bt_hid->transport; }, true); - - return hid_tiktok; -} - -void hid_tiktok_free(HidTikTok* hid_tiktok) { - furi_assert(hid_tiktok); - view_free(hid_tiktok->view); - free(hid_tiktok); -} - -View* hid_tiktok_get_view(HidTikTok* hid_tiktok) { - furi_assert(hid_tiktok); - return hid_tiktok->view; -} - -void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected) { - furi_assert(hid_tiktok); - with_view_model( - hid_tiktok->view, - HidTikTokModel * model, - { - model->connected = connected; - model->is_cursor_set = false; - }, - true); -} diff --git a/applications/external/hid_app/views/hid_tiktok.h b/applications/external/hid_app/views/hid_tiktok.h deleted file mode 100644 index b2efc3692..000000000 --- a/applications/external/hid_app/views/hid_tiktok.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -typedef struct Hid Hid; -typedef struct HidTikTok HidTikTok; - -HidTikTok* hid_tiktok_alloc(Hid* bt_hid); - -void hid_tiktok_free(HidTikTok* hid_tiktok); - -View* hid_tiktok_get_view(HidTikTok* hid_tiktok); - -void hid_tiktok_set_connected_status(HidTikTok* hid_tiktok, bool connected); diff --git a/applications/external/mfkey32/application.fam b/applications/external/mfkey32/application.fam deleted file mode 100644 index 9a9cbf581..000000000 --- a/applications/external/mfkey32/application.fam +++ /dev/null @@ -1,17 +0,0 @@ -App( - appid="mfkey32", - name="Mfkey32", - apptype=FlipperAppType.EXTERNAL, - targets=["f7"], - entry_point="mfkey32_main", - requires=[ - "gui", - "storage", - ], - stack_size=1 * 1024, - fap_icon="mfkey.png", - fap_category="Nfc", - fap_author="noproto", - fap_icon_assets="images", - fap_weburl="https://github.com/noproto/FlipperMfkey", -) diff --git a/applications/external/mfkey32/images/mfkey.png b/applications/external/mfkey32/images/mfkey.png deleted file mode 100644 index 52ab29efb92bf5aa7c790b8043d2d07a7e74704d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9060 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 T2_+RDfDS|^SsNGcaF6&O#taN} diff --git a/applications/external/mfkey32/mfkey.png b/applications/external/mfkey32/mfkey.png deleted file mode 100644 index 52ab29efb92bf5aa7c790b8043d2d07a7e74704d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9060 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 T2_+RDfDS|^SsNGcaF6&O#taN} diff --git a/applications/external/mfkey32/mfkey32.c b/applications/external/mfkey32/mfkey32.c deleted file mode 100644 index 5e790b01f..000000000 --- a/applications/external/mfkey32/mfkey32.c +++ /dev/null @@ -1,1349 +0,0 @@ -#pragma GCC optimize("O3") -#pragma GCC optimize("-funroll-all-loops") - -// TODO: Add keys to top of the user dictionary, not the bottom -// TODO: More efficient dictionary bruteforce by scanning through hardcoded very common keys and previously found dictionary keys first? -// (a cache for napi_key_already_found_for_nonce) - -#include -#include -#include "time.h" -#include -#include -#include -#include -#include "mfkey32_icons.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc") -#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc") -#define MF_CLASSIC_NONCE_PATH EXT_PATH("nfc/.mfkey32.log") -#define TAG "Mfkey32" -#define NFC_MF_CLASSIC_KEY_LEN (13) - -#define MIN_RAM 115632 -#define LF_POLY_ODD (0x29CE5C) -#define LF_POLY_EVEN (0x870804) -#define CONST_M1_1 (LF_POLY_EVEN << 1 | 1) -#define CONST_M2_1 (LF_POLY_ODD << 1) -#define CONST_M1_2 (LF_POLY_ODD) -#define CONST_M2_2 (LF_POLY_EVEN << 1 | 1) -#define BIT(x, n) ((x) >> (n)&1) -#define BEBIT(x, n) BIT(x, (n) ^ 24) -#define SWAPENDIAN(x) \ - ((x) = ((x) >> 8 & 0xff00ff) | ((x)&0xff00ff) << 8, (x) = (x) >> 16 | (x) << 16) -//#define SIZEOF(arr) sizeof(arr) / sizeof(*arr) - -static int eta_round_time = 56; -static int eta_total_time = 900; -// MSB_LIMIT: Chunk size (out of 256) -static int MSB_LIMIT = 16; - -struct Crypto1State { - uint32_t odd, even; -}; -struct Crypto1Params { - uint64_t key; - uint32_t nr0_enc, uid_xor_nt0, uid_xor_nt1, nr1_enc, p64b, ar1_enc; -}; -struct Msb { - int tail; - uint32_t states[768]; -}; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType type; - InputEvent input; -} PluginEvent; - -typedef enum { - MissingNonces, - ZeroNonces, -} MfkeyError; - -typedef enum { - Ready, - Initializing, - DictionaryAttack, - MfkeyAttack, - Complete, - Error, - Help, -} MfkeyState; - -// TODO: Can we eliminate any of the members of this struct? -typedef struct { - FuriMutex* mutex; - MfkeyError err; - MfkeyState mfkey_state; - int cracked; - int unique_cracked; - int num_completed; - int total; - int dict_count; - int search; - int eta_timestamp; - int eta_total; - int eta_round; - bool is_thread_running; - bool close_thread_please; - FuriThread* mfkeythread; -} ProgramState; - -// TODO: Merge this with Crypto1Params? -typedef struct { - uint32_t uid; // serial number - uint32_t nt0; // tag challenge first - uint32_t nt1; // tag challenge second - uint32_t nr0_enc; // first encrypted reader challenge - uint32_t ar0_enc; // first encrypted reader response - uint32_t nr1_enc; // second encrypted reader challenge - uint32_t ar1_enc; // second encrypted reader response -} MfClassicNonce; - -typedef struct { - Stream* stream; - uint32_t total_nonces; - MfClassicNonce* remaining_nonce_array; - size_t remaining_nonces; -} MfClassicNonceArray; - -struct MfClassicDict { - Stream* stream; - uint32_t total_keys; -}; - -static const uint8_t table[256] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, - 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, - 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, - 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, - 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, - 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, - 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, - 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, - 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8}; -static const uint8_t lookup1[256] = { - 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0, - 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, - 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8, - 8, 24, 8, 8, 24, 24, 24, 24, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, - 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0, - 0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, - 0, 0, 16, 16, 0, 16, 0, 0, 0, 16, 0, 0, 16, 16, 16, 16, 0, 0, 16, 16, 0, 16, 0, 0, - 0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, - 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, 0, 0, 16, 16, 0, 16, 0, 0, - 0, 16, 0, 0, 16, 16, 16, 16, 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24, - 8, 8, 24, 24, 8, 24, 8, 8, 8, 24, 8, 8, 24, 24, 24, 24}; -static const uint8_t lookup2[256] = { - 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, - 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, - 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, - 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, - 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, - 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, - 4, 4, 0, 0, 4, 4, 0, 4, 0, 0, 0, 4, 0, 0, 4, 4, 4, 4, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, - 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, - 2, 6, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 6, 2, 2, 2, 6, 2, 2, 6, 6, 6, 6}; - -uint32_t prng_successor(uint32_t x, uint32_t n) { - SWAPENDIAN(x); - while(n--) x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; - return SWAPENDIAN(x); -} - -static inline int filter(uint32_t const x) { - uint32_t f; - f = lookup1[x & 0xff] | lookup2[(x >> 8) & 0xff]; - f |= 0x0d938 >> (x >> 16 & 0xf) & 1; - return BIT(0xEC57E80A, f); -} - -static inline uint8_t evenparity32(uint32_t x) { - if((table[x & 0xff] + table[(x >> 8) & 0xff] + table[(x >> 16) & 0xff] + table[x >> 24]) % 2 == - 0) { - return 0; - } else { - return 1; - } - //return ((table[x & 0xff] + table[(x >> 8) & 0xff] + table[(x >> 16) & 0xff] + table[x >> 24]) % 2) & 0xFF; -} - -static inline void update_contribution(unsigned int data[], int item, int mask1, int mask2) { - int p = data[item] >> 25; - p = p << 1 | evenparity32(data[item] & mask1); - p = p << 1 | evenparity32(data[item] & mask2); - data[item] = p << 24 | (data[item] & 0xffffff); -} - -void crypto1_get_lfsr(struct Crypto1State* state, uint64_t* lfsr) { - int i; - for(*lfsr = 0, i = 23; i >= 0; --i) { - *lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3); - *lfsr = *lfsr << 1 | BIT(state->even, i ^ 3); - } -} - -static inline uint32_t crypt_word(struct Crypto1State* s) { - // "in" and "x" are always 0 (last iteration) - uint32_t res_ret = 0; - uint32_t feedin, t; - for(int i = 0; i <= 31; i++) { - res_ret |= (filter(s->odd) << (24 ^ i)); //-V629 - feedin = LF_POLY_EVEN & s->even; - feedin ^= LF_POLY_ODD & s->odd; - s->even = s->even << 1 | (evenparity32(feedin)); - t = s->odd, s->odd = s->even, s->even = t; - } - return res_ret; -} - -static inline void crypt_word_noret(struct Crypto1State* s, uint32_t in, int x) { - uint8_t ret; - uint32_t feedin, t, next_in; - for(int i = 0; i <= 31; i++) { - next_in = BEBIT(in, i); - ret = filter(s->odd); - feedin = ret & (!!x); - feedin ^= LF_POLY_EVEN & s->even; - feedin ^= LF_POLY_ODD & s->odd; - feedin ^= !!next_in; - s->even = s->even << 1 | (evenparity32(feedin)); - t = s->odd, s->odd = s->even, s->even = t; - } - return; -} - -static inline void rollback_word_noret(struct Crypto1State* s, uint32_t in, int x) { - uint8_t ret; - uint32_t feedin, t, next_in; - for(int i = 31; i >= 0; i--) { - next_in = BEBIT(in, i); - s->odd &= 0xffffff; - t = s->odd, s->odd = s->even, s->even = t; - ret = filter(s->odd); - feedin = ret & (!!x); - feedin ^= s->even & 1; - feedin ^= LF_POLY_EVEN & (s->even >>= 1); - feedin ^= LF_POLY_ODD & s->odd; - feedin ^= !!next_in; - s->even |= (evenparity32(feedin)) << 23; - } - return; -} - -int key_already_found_for_nonce( - uint64_t* keyarray, - int keyarray_size, - uint32_t uid_xor_nt1, - uint32_t nr1_enc, - uint32_t p64b, - uint32_t ar1_enc) { - for(int k = 0; k < keyarray_size; k++) { - struct Crypto1State temp = {0, 0}; - - for(int i = 0; i < 24; i++) { - (&temp)->odd |= (BIT(keyarray[k], 2 * i + 1) << (i ^ 3)); - (&temp)->even |= (BIT(keyarray[k], 2 * i) << (i ^ 3)); - } - - crypt_word_noret(&temp, uid_xor_nt1, 0); - crypt_word_noret(&temp, nr1_enc, 1); - - if(ar1_enc == (crypt_word(&temp) ^ p64b)) { - return 1; - } - } - return 0; -} - -int check_state(struct Crypto1State* t, struct Crypto1Params* p) { - if(!(t->odd | t->even)) return 0; - rollback_word_noret(t, 0, 0); - rollback_word_noret(t, p->nr0_enc, 1); - rollback_word_noret(t, p->uid_xor_nt0, 0); - struct Crypto1State temp = {t->odd, t->even}; - crypt_word_noret(t, p->uid_xor_nt1, 0); - crypt_word_noret(t, p->nr1_enc, 1); - if(p->ar1_enc == (crypt_word(t) ^ p->p64b)) { - crypto1_get_lfsr(&temp, &(p->key)); - return 1; - } - return 0; -} - -static inline int state_loop(unsigned int* states_buffer, int xks, int m1, int m2) { - int states_tail = 0; - int round = 0, s = 0, xks_bit = 0; - - for(round = 1; round <= 12; round++) { - xks_bit = BIT(xks, round); - - for(s = 0; s <= states_tail; s++) { - states_buffer[s] <<= 1; - - if((filter(states_buffer[s]) ^ filter(states_buffer[s] | 1)) != 0) { - states_buffer[s] |= filter(states_buffer[s]) ^ xks_bit; - if(round > 4) { - update_contribution(states_buffer, s, m1, m2); - } - } else if(filter(states_buffer[s]) == xks_bit) { - // TODO: Refactor - if(round > 4) { - states_buffer[++states_tail] = states_buffer[s + 1]; - states_buffer[s + 1] = states_buffer[s] | 1; - update_contribution(states_buffer, s, m1, m2); - s++; - update_contribution(states_buffer, s, m1, m2); - } else { - states_buffer[++states_tail] = states_buffer[++s]; - states_buffer[s] = states_buffer[s - 1] | 1; - } - } else { - states_buffer[s--] = states_buffer[states_tail--]; - } - } - } - - return states_tail; -} - -int binsearch(unsigned int data[], int start, int stop) { - int mid, val = data[stop] & 0xff000000; - while(start != stop) { - mid = (stop - start) >> 1; - if((data[start + mid] ^ 0x80000000) > (val ^ 0x80000000)) - stop = start + mid; - else - start += mid + 1; - } - return start; -} -void quicksort(unsigned int array[], int low, int high) { - //if (SIZEOF(array) == 0) - // return; - if(low >= high) return; - int middle = low + (high - low) / 2; - unsigned int pivot = array[middle]; - int i = low, j = high; - while(i <= j) { - while(array[i] < pivot) { - i++; - } - while(array[j] > pivot) { - j--; - } - if(i <= j) { // swap - int temp = array[i]; - array[i] = array[j]; - array[j] = temp; - i++; - j--; - } - } - if(low < j) { - quicksort(array, low, j); - } - if(high > i) { - quicksort(array, i, high); - } -} -int extend_table(unsigned int data[], int tbl, int end, int bit, int m1, int m2) { - for(data[tbl] <<= 1; tbl <= end; data[++tbl] <<= 1) { - if((filter(data[tbl]) ^ filter(data[tbl] | 1)) != 0) { - data[tbl] |= filter(data[tbl]) ^ bit; - update_contribution(data, tbl, m1, m2); - } else if(filter(data[tbl]) == bit) { - data[++end] = data[tbl + 1]; - data[tbl + 1] = data[tbl] | 1; - update_contribution(data, tbl, m1, m2); - tbl++; - update_contribution(data, tbl, m1, m2); - } else { - data[tbl--] = data[end--]; - } - } - return end; -} - -int old_recover( - unsigned int odd[], - int o_head, - int o_tail, - int oks, - unsigned int even[], - int e_head, - int e_tail, - int eks, - int rem, - int s, - struct Crypto1Params* p, - int first_run) { - int o, e, i; - if(rem == -1) { - for(e = e_head; e <= e_tail; ++e) { - even[e] = (even[e] << 1) ^ evenparity32(even[e] & LF_POLY_EVEN); - for(o = o_head; o <= o_tail; ++o, ++s) { - struct Crypto1State temp = {0, 0}; - temp.even = odd[o]; - temp.odd = even[e] ^ evenparity32(odd[o] & LF_POLY_ODD); - if(check_state(&temp, p)) { - return -1; - } - } - } - return s; - } - if(first_run == 0) { - for(i = 0; (i < 4) && (rem-- != 0); i++) { - oks >>= 1; - eks >>= 1; - o_tail = extend_table( - odd, o_head, o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1); - if(o_head > o_tail) return s; - e_tail = - extend_table(even, e_head, e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1); - if(e_head > e_tail) return s; - } - } - first_run = 0; - quicksort(odd, o_head, o_tail); - quicksort(even, e_head, e_tail); - while(o_tail >= o_head && e_tail >= e_head) { - if(((odd[o_tail] ^ even[e_tail]) >> 24) == 0) { - o_tail = binsearch(odd, o_head, o = o_tail); - e_tail = binsearch(even, e_head, e = e_tail); - s = old_recover(odd, o_tail--, o, oks, even, e_tail--, e, eks, rem, s, p, first_run); - if(s == -1) { - break; - } - } else if((odd[o_tail] ^ 0x80000000) > (even[e_tail] ^ 0x80000000)) { - o_tail = binsearch(odd, o_head, o_tail) - 1; - } else { - e_tail = binsearch(even, e_head, e_tail) - 1; - } - } - return s; -} - -static inline int sync_state(ProgramState* program_state) { - int ts = furi_hal_rtc_get_timestamp(); - program_state->eta_round = program_state->eta_round - (ts - program_state->eta_timestamp); - program_state->eta_total = program_state->eta_total - (ts - program_state->eta_timestamp); - program_state->eta_timestamp = ts; - if(program_state->close_thread_please) { - return 1; - } - return 0; -} - -int calculate_msb_tables( - int oks, - int eks, - int msb_round, - struct Crypto1Params* p, - unsigned int* states_buffer, - struct Msb* odd_msbs, - struct Msb* even_msbs, - unsigned int* temp_states_odd, - unsigned int* temp_states_even, - ProgramState* program_state) { - //FURI_LOG_I(TAG, "MSB GO %i", msb_iter); // DEBUG - unsigned int msb_head = (MSB_LIMIT * msb_round); // msb_iter ranges from 0 to (256/MSB_LIMIT)-1 - unsigned int msb_tail = (MSB_LIMIT * (msb_round + 1)); - int states_tail = 0, tail = 0; - int i = 0, j = 0, semi_state = 0, found = 0; - unsigned int msb = 0; - // TODO: Why is this necessary? - memset(odd_msbs, 0, MSB_LIMIT * sizeof(struct Msb)); - memset(even_msbs, 0, MSB_LIMIT * sizeof(struct Msb)); - - for(semi_state = 1 << 20; semi_state >= 0; semi_state--) { - if(semi_state % 32768 == 0) { - if(sync_state(program_state) == 1) { - return 0; - } - } - - if(filter(semi_state) == (oks & 1)) { //-V547 - states_buffer[0] = semi_state; - states_tail = state_loop(states_buffer, oks, CONST_M1_1, CONST_M2_1); - - for(i = states_tail; i >= 0; i--) { - msb = states_buffer[i] >> 24; - if((msb >= msb_head) && (msb < msb_tail)) { - found = 0; - for(j = 0; j < odd_msbs[msb - msb_head].tail - 1; j++) { - if(odd_msbs[msb - msb_head].states[j] == states_buffer[i]) { - found = 1; - break; - } - } - - if(!found) { - tail = odd_msbs[msb - msb_head].tail++; - odd_msbs[msb - msb_head].states[tail] = states_buffer[i]; - } - } - } - } - - if(filter(semi_state) == (eks & 1)) { //-V547 - states_buffer[0] = semi_state; - states_tail = state_loop(states_buffer, eks, CONST_M1_2, CONST_M2_2); - - for(i = 0; i <= states_tail; i++) { - msb = states_buffer[i] >> 24; - if((msb >= msb_head) && (msb < msb_tail)) { - found = 0; - - for(j = 0; j < even_msbs[msb - msb_head].tail; j++) { - if(even_msbs[msb - msb_head].states[j] == states_buffer[i]) { - found = 1; - break; - } - } - - if(!found) { - tail = even_msbs[msb - msb_head].tail++; - even_msbs[msb - msb_head].states[tail] = states_buffer[i]; - } - } - } - } - } - - oks >>= 12; - eks >>= 12; - - for(i = 0; i < MSB_LIMIT; i++) { - if(sync_state(program_state) == 1) { - return 0; - } - // TODO: Why is this necessary? - memset(temp_states_even, 0, sizeof(unsigned int) * (1280)); - memset(temp_states_odd, 0, sizeof(unsigned int) * (1280)); - memcpy(temp_states_odd, odd_msbs[i].states, odd_msbs[i].tail * sizeof(unsigned int)); - memcpy(temp_states_even, even_msbs[i].states, even_msbs[i].tail * sizeof(unsigned int)); - int res = old_recover( - temp_states_odd, - 0, - odd_msbs[i].tail, - oks, - temp_states_even, - 0, - even_msbs[i].tail, - eks, - 3, - 0, - p, - 1); - if(res == -1) { - return 1; - } - //odd_msbs[i].tail = 0; - //even_msbs[i].tail = 0; - } - - return 0; -} - -bool recover(struct Crypto1Params* p, int ks2, ProgramState* program_state) { - bool found = false; - unsigned int* states_buffer = malloc(sizeof(unsigned int) * (2 << 9)); - struct Msb* odd_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb)); - struct Msb* even_msbs = (struct Msb*)malloc(MSB_LIMIT * sizeof(struct Msb)); - unsigned int* temp_states_odd = malloc(sizeof(unsigned int) * (1280)); - unsigned int* temp_states_even = malloc(sizeof(unsigned int) * (1280)); - int oks = 0, eks = 0; - int i = 0, msb = 0; - for(i = 31; i >= 0; i -= 2) { - oks = oks << 1 | BEBIT(ks2, i); - } - for(i = 30; i >= 0; i -= 2) { - eks = eks << 1 | BEBIT(ks2, i); - } - int bench_start = furi_hal_rtc_get_timestamp(); - program_state->eta_total = eta_total_time; - program_state->eta_timestamp = bench_start; - for(msb = 0; msb <= ((256 / MSB_LIMIT) - 1); msb++) { - program_state->search = msb; - program_state->eta_round = eta_round_time; - program_state->eta_total = eta_total_time - (eta_round_time * msb); - if(calculate_msb_tables( - oks, - eks, - msb, - p, - states_buffer, - odd_msbs, - even_msbs, - temp_states_odd, - temp_states_even, - program_state)) { - int bench_stop = furi_hal_rtc_get_timestamp(); - FURI_LOG_I(TAG, "Cracked in %i seconds", bench_stop - bench_start); - found = true; - break; - } - if(program_state->close_thread_please) { - break; - } - } - free(states_buffer); - free(odd_msbs); - free(even_msbs); - free(temp_states_odd); - free(temp_states_even); - return found; -} - -bool napi_mf_classic_dict_check_presence(MfClassicDictType dict_type) { - Storage* storage = furi_record_open(RECORD_STORAGE); - - bool dict_present = false; - if(dict_type == MfClassicDictTypeSystem) { - dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK; - } else if(dict_type == MfClassicDictTypeUser) { - dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK; - } - - furi_record_close(RECORD_STORAGE); - - return dict_present; -} - -MfClassicDict* napi_mf_classic_dict_alloc(MfClassicDictType dict_type) { - MfClassicDict* dict = malloc(sizeof(MfClassicDict)); - Storage* storage = furi_record_open(RECORD_STORAGE); - dict->stream = buffered_file_stream_alloc(storage); - furi_record_close(RECORD_STORAGE); - - bool dict_loaded = false; - do { - if(dict_type == MfClassicDictTypeSystem) { - if(!buffered_file_stream_open( - dict->stream, - MF_CLASSIC_DICT_FLIPPER_PATH, - FSAM_READ_WRITE, - FSOM_OPEN_EXISTING)) { - buffered_file_stream_close(dict->stream); - break; - } - } else if(dict_type == MfClassicDictTypeUser) { - if(!buffered_file_stream_open( - dict->stream, MF_CLASSIC_DICT_USER_PATH, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { - buffered_file_stream_close(dict->stream); - break; - } - } - - // Check for newline ending - if(!stream_eof(dict->stream)) { - if(!stream_seek(dict->stream, -1, StreamOffsetFromEnd)) break; - uint8_t last_char = 0; - if(stream_read(dict->stream, &last_char, 1) != 1) break; - if(last_char != '\n') { - FURI_LOG_D(TAG, "Adding new line ending"); - if(stream_write_char(dict->stream, '\n') != 1) break; - } - if(!stream_rewind(dict->stream)) break; - } - - // Read total amount of keys - FuriString* next_line; - next_line = furi_string_alloc(); - while(true) { - if(!stream_read_line(dict->stream, next_line)) { - FURI_LOG_T(TAG, "No keys left in dict"); - break; - } - FURI_LOG_T( - TAG, - "Read line: %s, len: %zu", - furi_string_get_cstr(next_line), - furi_string_size(next_line)); - if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - dict->total_keys++; - } - furi_string_free(next_line); - stream_rewind(dict->stream); - - dict_loaded = true; - FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys); - } while(false); - - if(!dict_loaded) { - buffered_file_stream_close(dict->stream); - free(dict); - dict = NULL; - } - - return dict; -} - -bool napi_mf_classic_dict_add_key_str(MfClassicDict* dict, FuriString* key) { - furi_assert(dict); - furi_assert(dict->stream); - FURI_LOG_I(TAG, "Saving key: %s", furi_string_get_cstr(key)); - - furi_string_cat_printf(key, "\n"); - - bool key_added = false; - do { - if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; - if(!stream_insert_string(dict->stream, key)) break; - dict->total_keys++; - key_added = true; - } while(false); - - furi_string_left(key, 12); - return key_added; -} - -void napi_mf_classic_dict_free(MfClassicDict* dict) { - furi_assert(dict); - furi_assert(dict->stream); - - buffered_file_stream_close(dict->stream); - stream_free(dict->stream); - free(dict); -} - -static void napi_mf_classic_dict_int_to_str(uint8_t* key_int, FuriString* key_str) { - furi_string_reset(key_str); - for(size_t i = 0; i < 6; i++) { - furi_string_cat_printf(key_str, "%02X", key_int[i]); - } -} - -static void napi_mf_classic_dict_str_to_int(FuriString* key_str, uint64_t* key_int) { - uint8_t key_byte_tmp; - - *key_int = 0ULL; - for(uint8_t i = 0; i < 12; i += 2) { - args_char_to_hex( - furi_string_get_char(key_str, i), furi_string_get_char(key_str, i + 1), &key_byte_tmp); - *key_int |= (uint64_t)key_byte_tmp << (8 * (5 - i / 2)); - } -} - -uint32_t napi_mf_classic_dict_get_total_keys(MfClassicDict* dict) { - furi_assert(dict); - - return dict->total_keys; -} - -bool napi_mf_classic_dict_rewind(MfClassicDict* dict) { - furi_assert(dict); - furi_assert(dict->stream); - - return stream_rewind(dict->stream); -} - -bool napi_mf_classic_dict_get_next_key_str(MfClassicDict* dict, FuriString* key) { - furi_assert(dict); - furi_assert(dict->stream); - - bool key_read = false; - furi_string_reset(key); - while(!key_read) { - if(!stream_read_line(dict->stream, key)) break; - if(furi_string_get_char(key, 0) == '#') continue; - if(furi_string_size(key) != NFC_MF_CLASSIC_KEY_LEN) continue; - furi_string_left(key, 12); - key_read = true; - } - - return key_read; -} - -bool napi_mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) { - furi_assert(dict); - furi_assert(dict->stream); - - FuriString* temp_key; - temp_key = furi_string_alloc(); - bool key_read = napi_mf_classic_dict_get_next_key_str(dict, temp_key); - if(key_read) { - napi_mf_classic_dict_str_to_int(temp_key, key); - } - furi_string_free(temp_key); - return key_read; -} - -bool napi_mf_classic_dict_is_key_present_str(MfClassicDict* dict, FuriString* key) { - furi_assert(dict); - furi_assert(dict->stream); - - FuriString* next_line; - next_line = furi_string_alloc(); - - bool key_found = false; - stream_rewind(dict->stream); - while(!key_found) { //-V654 - if(!stream_read_line(dict->stream, next_line)) break; - if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue; - furi_string_left(next_line, 12); - if(!furi_string_equal(key, next_line)) continue; - key_found = true; - } - - furi_string_free(next_line); - return key_found; -} - -bool napi_mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key) { - FuriString* temp_key; - - temp_key = furi_string_alloc(); - napi_mf_classic_dict_int_to_str(key, temp_key); - bool key_found = napi_mf_classic_dict_is_key_present_str(dict, temp_key); - furi_string_free(temp_key); - return key_found; -} - -bool napi_key_already_found_for_nonce( - MfClassicDict* dict, - uint32_t uid_xor_nt1, - uint32_t nr1_enc, - uint32_t p64b, - uint32_t ar1_enc) { - bool found = false; - uint64_t k = 0; - napi_mf_classic_dict_rewind(dict); - while(napi_mf_classic_dict_get_next_key(dict, &k)) { - struct Crypto1State temp = {0, 0}; - int i; - for(i = 0; i < 24; i++) { - (&temp)->odd |= (BIT(k, 2 * i + 1) << (i ^ 3)); - (&temp)->even |= (BIT(k, 2 * i) << (i ^ 3)); - } - crypt_word_noret(&temp, uid_xor_nt1, 0); - crypt_word_noret(&temp, nr1_enc, 1); - if(ar1_enc == (crypt_word(&temp) ^ p64b)) { - found = true; - break; - } - } - return found; -} - -bool napi_mf_classic_nonces_check_presence() { - Storage* storage = furi_record_open(RECORD_STORAGE); - - bool nonces_present = storage_common_stat(storage, MF_CLASSIC_NONCE_PATH, NULL) == FSE_OK; - - furi_record_close(RECORD_STORAGE); - - return nonces_present; -} - -MfClassicNonceArray* napi_mf_classic_nonce_array_alloc( - MfClassicDict* system_dict, - bool system_dict_exists, - MfClassicDict* user_dict, - ProgramState* program_state) { - MfClassicNonceArray* nonce_array = malloc(sizeof(MfClassicNonceArray)); - MfClassicNonce* remaining_nonce_array_init = malloc(sizeof(MfClassicNonce) * 1); - nonce_array->remaining_nonce_array = remaining_nonce_array_init; - Storage* storage = furi_record_open(RECORD_STORAGE); - nonce_array->stream = buffered_file_stream_alloc(storage); - furi_record_close(RECORD_STORAGE); - - bool array_loaded = false; - do { - // https://github.com/flipperdevices/flipperzero-firmware/blob/5134f44c09d39344a8747655c0d59864bb574b96/applications/services/storage/filesystem_api_defines.h#L8-L22 - if(!buffered_file_stream_open( - nonce_array->stream, MF_CLASSIC_NONCE_PATH, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { - buffered_file_stream_close(nonce_array->stream); - break; - } - - // Check for newline ending - if(!stream_eof(nonce_array->stream)) { - if(!stream_seek(nonce_array->stream, -1, StreamOffsetFromEnd)) break; - uint8_t last_char = 0; - if(stream_read(nonce_array->stream, &last_char, 1) != 1) break; - if(last_char != '\n') { - FURI_LOG_D(TAG, "Adding new line ending"); - if(stream_write_char(nonce_array->stream, '\n') != 1) break; - } - if(!stream_rewind(nonce_array->stream)) break; - } - - // Read total amount of nonces - FuriString* next_line; - next_line = furi_string_alloc(); - while(!(program_state->close_thread_please)) { - if(!stream_read_line(nonce_array->stream, next_line)) { - FURI_LOG_T(TAG, "No nonces left"); - break; - } - FURI_LOG_T( - TAG, - "Read line: %s, len: %zu", - furi_string_get_cstr(next_line), - furi_string_size(next_line)); - if(!furi_string_start_with_str(next_line, "Sec")) continue; - const char* next_line_cstr = furi_string_get_cstr(next_line); - MfClassicNonce res = {0}; - int i = 0; - char* endptr; - for(i = 0; i <= 17; i++) { - if(i != 0) { - next_line_cstr = strchr(next_line_cstr, ' '); - if(next_line_cstr) { - next_line_cstr++; - } else { - break; - } - } - unsigned long value = strtoul(next_line_cstr, &endptr, 16); - switch(i) { - case 5: - res.uid = value; - break; - case 7: - res.nt0 = value; - break; - case 9: - res.nr0_enc = value; - break; - case 11: - res.ar0_enc = value; - break; - case 13: - res.nt1 = value; - break; - case 15: - res.nr1_enc = value; - break; - case 17: - res.ar1_enc = value; - break; - default: - break; // Do nothing - } - next_line_cstr = endptr; - } - (program_state->total)++; - uint32_t p64b = prng_successor(res.nt1, 64); - if((system_dict_exists && - napi_key_already_found_for_nonce( - system_dict, res.uid ^ res.nt1, res.nr1_enc, p64b, res.ar1_enc)) || - (napi_key_already_found_for_nonce( - user_dict, res.uid ^ res.nt1, res.nr1_enc, p64b, res.ar1_enc))) { - (program_state->cracked)++; - (program_state->num_completed)++; - continue; - } - FURI_LOG_I(TAG, "No key found for %8lx %8lx", res.uid, res.ar1_enc); - // TODO: Refactor - nonce_array->remaining_nonce_array = realloc( //-V701 - nonce_array->remaining_nonce_array, - sizeof(MfClassicNonce) * ((nonce_array->remaining_nonces) + 1)); - nonce_array->remaining_nonces++; - nonce_array->remaining_nonce_array[(nonce_array->remaining_nonces) - 1] = res; - nonce_array->total_nonces++; - } - furi_string_free(next_line); - buffered_file_stream_close(nonce_array->stream); - - array_loaded = true; - FURI_LOG_I(TAG, "Loaded %lu nonces", nonce_array->total_nonces); - } while(false); - - if(!array_loaded) { - free(nonce_array); - nonce_array = NULL; - } - - return nonce_array; -} - -void napi_mf_classic_nonce_array_free(MfClassicNonceArray* nonce_array) { - furi_assert(nonce_array); - furi_assert(nonce_array->stream); - - buffered_file_stream_close(nonce_array->stream); - stream_free(nonce_array->stream); - free(nonce_array); -} - -static void finished_beep() { - // Beep to indicate completion - NotificationApp* notification = furi_record_open("notification"); - notification_message(notification, &sequence_audiovisual_alert); - notification_message(notification, &sequence_display_backlight_on); - furi_record_close("notification"); -} - -void mfkey32(ProgramState* program_state) { - uint64_t found_key; // recovered key - size_t keyarray_size = 0; - uint64_t* keyarray = malloc(sizeof(uint64_t) * 1); - uint32_t i = 0, j = 0; - // Check for nonces - if(!napi_mf_classic_nonces_check_presence()) { - program_state->err = MissingNonces; - program_state->mfkey_state = Error; - free(keyarray); - return; - } - // Read dictionaries (optional) - MfClassicDict* system_dict = {0}; - bool system_dict_exists = napi_mf_classic_dict_check_presence(MfClassicDictTypeSystem); - MfClassicDict* user_dict = {0}; - bool user_dict_exists = napi_mf_classic_dict_check_presence(MfClassicDictTypeUser); - uint32_t total_dict_keys = 0; - if(system_dict_exists) { - system_dict = napi_mf_classic_dict_alloc(MfClassicDictTypeSystem); - total_dict_keys += napi_mf_classic_dict_get_total_keys(system_dict); - } - user_dict = napi_mf_classic_dict_alloc(MfClassicDictTypeUser); - if(user_dict_exists) { - total_dict_keys += napi_mf_classic_dict_get_total_keys(user_dict); - } - user_dict_exists = true; - program_state->dict_count = total_dict_keys; - program_state->mfkey_state = DictionaryAttack; - // Read nonces - MfClassicNonceArray* nonce_arr; - nonce_arr = napi_mf_classic_nonce_array_alloc( - system_dict, system_dict_exists, user_dict, program_state); - if(system_dict_exists) { - napi_mf_classic_dict_free(system_dict); - } - if(nonce_arr->total_nonces == 0) { - // Nothing to crack - program_state->err = ZeroNonces; - program_state->mfkey_state = Error; - napi_mf_classic_nonce_array_free(nonce_arr); - napi_mf_classic_dict_free(user_dict); - free(keyarray); - return; - } - if(memmgr_get_free_heap() < MIN_RAM) { - // System has less than the guaranteed amount of RAM (140 KB) - adjust some parameters to run anyway at half speed - eta_round_time *= 2; - eta_total_time *= 2; - MSB_LIMIT /= 2; - } - program_state->mfkey_state = MfkeyAttack; - // TODO: Work backwards on this array and free memory - for(i = 0; i < nonce_arr->total_nonces; i++) { - MfClassicNonce next_nonce = nonce_arr->remaining_nonce_array[i]; - uint32_t p64 = prng_successor(next_nonce.nt0, 64); - uint32_t p64b = prng_successor(next_nonce.nt1, 64); - if(key_already_found_for_nonce( - keyarray, - keyarray_size, - next_nonce.uid ^ next_nonce.nt1, - next_nonce.nr1_enc, - p64b, - next_nonce.ar1_enc)) { - nonce_arr->remaining_nonces--; - (program_state->cracked)++; - (program_state->num_completed)++; - continue; - } - FURI_LOG_I(TAG, "Cracking %8lx %8lx", next_nonce.uid, next_nonce.ar1_enc); - struct Crypto1Params p = { - 0, - next_nonce.nr0_enc, - next_nonce.uid ^ next_nonce.nt0, - next_nonce.uid ^ next_nonce.nt1, - next_nonce.nr1_enc, - p64b, - next_nonce.ar1_enc}; - if(!recover(&p, next_nonce.ar0_enc ^ p64, program_state)) { - if(program_state->close_thread_please) { - break; - } - // No key found in recover() - (program_state->num_completed)++; - continue; - } - (program_state->cracked)++; - (program_state->num_completed)++; - found_key = p.key; - bool already_found = false; - for(j = 0; j < keyarray_size; j++) { - if(keyarray[j] == found_key) { - already_found = true; - break; - } - } - if(already_found == false) { - // New key - keyarray = realloc(keyarray, sizeof(uint64_t) * (keyarray_size + 1)); //-V701 - keyarray_size += 1; - keyarray[keyarray_size - 1] = found_key; - (program_state->unique_cracked)++; - } - } - // TODO: Update display to show all keys were found - // TODO: Prepend found key(s) to user dictionary file - //FURI_LOG_I(TAG, "Unique keys found:"); - for(i = 0; i < keyarray_size; i++) { - //FURI_LOG_I(TAG, "%012" PRIx64, keyarray[i]); - FuriString* temp_key = furi_string_alloc(); - furi_string_cat_printf(temp_key, "%012" PRIX64, keyarray[i]); - napi_mf_classic_dict_add_key_str(user_dict, temp_key); - furi_string_free(temp_key); - } - if(keyarray_size > 0) { - // TODO: Should we use DolphinDeedNfcMfcAdd? - dolphin_deed(DolphinDeedNfcMfcAdd); - } - napi_mf_classic_nonce_array_free(nonce_arr); - napi_mf_classic_dict_free(user_dict); - free(keyarray); - //FURI_LOG_I(TAG, "mfkey32 function completed normally"); // DEBUG - program_state->mfkey_state = Complete; - // No need to alert the user if they asked it to stop - if(!(program_state->close_thread_please)) { - finished_beep(); - } - return; -} - -// Screen is 128x64 px -static void render_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - ProgramState* program_state = ctx; - furi_mutex_acquire(program_state->mutex, FuriWaitForever); - char draw_str[44] = {}; - canvas_clear(canvas); - canvas_draw_frame(canvas, 0, 0, 128, 64); - canvas_draw_frame(canvas, 0, 15, 128, 64); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 5, 4, AlignLeft, AlignTop, "Mfkey32"); - canvas_draw_icon(canvas, 114, 4, &I_mfkey); - if(program_state->is_thread_running && program_state->mfkey_state == MfkeyAttack) { - float eta_round = (float)1 - ((float)program_state->eta_round / (float)eta_round_time); - float eta_total = (float)1 - ((float)program_state->eta_total / (float)eta_total_time); - float progress = (float)program_state->num_completed / (float)program_state->total; - if(eta_round < 0) { - // Round ETA miscalculated - eta_round = 1; - program_state->eta_round = 0; - } - if(eta_total < 0) { - // Total ETA miscalculated - eta_total = 1; - program_state->eta_total = 0; - } - canvas_set_font(canvas, FontSecondary); - snprintf( - draw_str, - sizeof(draw_str), - "Cracking: %d/%d - in prog.", - program_state->num_completed, - program_state->total); - elements_progress_bar_with_text(canvas, 5, 18, 118, progress, draw_str); - snprintf( - draw_str, - sizeof(draw_str), - "Round: %d/%d - ETA %02d Sec", - (program_state->search) + 1, // Zero indexed - 256 / MSB_LIMIT, - program_state->eta_round); - elements_progress_bar_with_text(canvas, 5, 31, 118, eta_round, draw_str); - snprintf(draw_str, sizeof(draw_str), "Total ETA %03d Sec", program_state->eta_total); - elements_progress_bar_with_text(canvas, 5, 44, 118, eta_total, draw_str); - } else if(program_state->is_thread_running && program_state->mfkey_state == DictionaryAttack) { - canvas_set_font(canvas, FontSecondary); - snprintf( - draw_str, sizeof(draw_str), "Dict solves: %d (in progress)", program_state->cracked); - canvas_draw_str_aligned(canvas, 10, 18, AlignLeft, AlignTop, draw_str); - snprintf(draw_str, sizeof(draw_str), "Keys in dict: %d", program_state->dict_count); - canvas_draw_str_aligned(canvas, 26, 28, AlignLeft, AlignTop, draw_str); - } else if(program_state->mfkey_state == Complete) { - // TODO: Scrollable list view to see cracked keys if user presses down - elements_progress_bar_with_text(canvas, 5, 18, 118, 1, draw_str); - canvas_set_font(canvas, FontSecondary); - snprintf(draw_str, sizeof(draw_str), "Complete"); - canvas_draw_str_aligned(canvas, 40, 31, AlignLeft, AlignTop, draw_str); - snprintf( - draw_str, - sizeof(draw_str), - "Keys added to user dict: %d", - program_state->unique_cracked); - canvas_draw_str_aligned(canvas, 10, 41, AlignLeft, AlignTop, draw_str); - } else if(program_state->mfkey_state == Ready) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 50, 30, AlignLeft, AlignTop, "Ready"); - elements_button_center(canvas, "Start"); - elements_button_right(canvas, "Help"); - } else if(program_state->mfkey_state == Help) { - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 7, 20, AlignLeft, AlignTop, "Collect nonces using"); - canvas_draw_str_aligned(canvas, 7, 30, AlignLeft, AlignTop, "Detect Reader."); - canvas_draw_str_aligned(canvas, 7, 40, AlignLeft, AlignTop, "Developers: noproto, AG"); - canvas_draw_str_aligned(canvas, 7, 50, AlignLeft, AlignTop, "Thanks: bettse"); - } else if(program_state->mfkey_state == Error) { - canvas_draw_str_aligned(canvas, 50, 25, AlignLeft, AlignTop, "Error"); - canvas_set_font(canvas, FontSecondary); - if(program_state->err == MissingNonces) { - canvas_draw_str_aligned(canvas, 25, 36, AlignLeft, AlignTop, "No nonces found"); - } else if(program_state->err == ZeroNonces) { - canvas_draw_str_aligned(canvas, 15, 36, AlignLeft, AlignTop, "Nonces already cracked"); - } else { - // Unhandled error - } - } else { - // Unhandled program state - } - furi_mutex_release(program_state->mutex); -} - -static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - PluginEvent event = {.type = EventTypeKey, .input = *input_event}; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -static void mfkey32_state_init(ProgramState* program_state) { - program_state->is_thread_running = false; - program_state->mfkey_state = Ready; - program_state->cracked = 0; - program_state->unique_cracked = 0; - program_state->num_completed = 0; - program_state->total = 0; - program_state->dict_count = 0; -} - -// Entrypoint for worker thread -static int32_t mfkey32_worker_thread(void* ctx) { - ProgramState* program_state = ctx; - program_state->is_thread_running = true; - program_state->mfkey_state = Initializing; - //FURI_LOG_I(TAG, "Hello from the mfkey32 worker thread"); // DEBUG - mfkey32(program_state); - program_state->is_thread_running = false; - return 0; -} - -void start_mfkey32_thread(ProgramState* program_state) { - if(!program_state->is_thread_running) { - furi_thread_start(program_state->mfkeythread); - } -} - -int32_t mfkey32_main() { - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); - - ProgramState* program_state = malloc(sizeof(ProgramState)); - - mfkey32_state_init(program_state); - - program_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!program_state->mutex) { - FURI_LOG_E(TAG, "cannot create mutex\r\n"); - free(program_state); - return 255; - } - - // Set system callbacks - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, program_state); - view_port_input_callback_set(view_port, input_callback, event_queue); - - // Open GUI and register view_port - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - program_state->mfkeythread = furi_thread_alloc(); - furi_thread_set_name(program_state->mfkeythread, "Mfkey32 Worker"); - furi_thread_set_stack_size(program_state->mfkeythread, 2048); - furi_thread_set_context(program_state->mfkeythread, program_state); - furi_thread_set_callback(program_state->mfkeythread, mfkey32_worker_thread); - - PluginEvent event; - for(bool main_loop = true; main_loop;) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - - furi_mutex_acquire(program_state->mutex, FuriWaitForever); - - if(event_status == FuriStatusOk) { - // press events - if(event.type == EventTypeKey) { - if(event.input.type == InputTypePress) { - switch(event.input.key) { - case InputKeyUp: - break; - case InputKeyDown: - break; - case InputKeyRight: - if(!program_state->is_thread_running && - program_state->mfkey_state == Ready) { - program_state->mfkey_state = Help; - view_port_update(view_port); - } - break; - case InputKeyLeft: - break; - case InputKeyOk: - if(!program_state->is_thread_running && - program_state->mfkey_state == Ready) { - start_mfkey32_thread(program_state); - view_port_update(view_port); - } - break; - case InputKeyBack: - if(!program_state->is_thread_running && - program_state->mfkey_state == Help) { - program_state->mfkey_state = Ready; - view_port_update(view_port); - } else { - program_state->close_thread_please = true; - if(program_state->is_thread_running && program_state->mfkeythread) { - // Wait until thread is finished - furi_thread_join(program_state->mfkeythread); - } - program_state->close_thread_please = false; - main_loop = false; - } - break; - default: - break; - } - } - } - } - - view_port_update(view_port); - furi_mutex_release(program_state->mutex); - } - - furi_thread_free(program_state->mfkeythread); - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close("gui"); - view_port_free(view_port); - furi_message_queue_free(event_queue); - furi_mutex_free(program_state->mutex); - free(program_state); - - return 0; -} diff --git a/applications/external/nfc_magic/application.fam b/applications/external/nfc_magic/application.fam deleted file mode 100644 index a89b45d00..000000000 --- a/applications/external/nfc_magic/application.fam +++ /dev/null @@ -1,21 +0,0 @@ -App( - appid="nfc_magic", - name="Nfc Magic", - apptype=FlipperAppType.EXTERNAL, - targets=["f7"], - entry_point="nfc_magic_app", - requires=[ - "storage", - "gui", - ], - stack_size=4 * 1024, - order=30, - fap_icon="../../../assets/icons/Archive/125_10px.png", - fap_category="NFC", - fap_private_libs=[ - Lib( - name="magic", - ), - ], - fap_icon_assets="assets", -) diff --git a/applications/external/nfc_magic/assets/DolphinCommon_56x48.png b/applications/external/nfc_magic/assets/DolphinCommon_56x48.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/applications/external/nfc_magic/assets/DolphinNice_96x59.png b/applications/external/nfc_magic/assets/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QR4Q+~K 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 LTALIbyM_M?u*zuP diff --git a/applications/external/nfc_magic/assets/NFC_manual_60x50.png b/applications/external/nfc_magic/assets/NFC_manual_60x50.png deleted file mode 100644 index 787c0bcfe01755f4dcadcdce004a1a0fcfb06f41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3804 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 IrsqTc1+$%)k^lez diff --git a/applications/external/nfc_magic/lib/magic/classic_gen1.c b/applications/external/nfc_magic/lib/magic/classic_gen1.c deleted file mode 100644 index 8d87d6316..000000000 --- a/applications/external/nfc_magic/lib/magic/classic_gen1.c +++ /dev/null @@ -1,145 +0,0 @@ -#include "classic_gen1.h" - -#include - -#define TAG "Magic" - -#define MAGIC_CMD_WUPA (0x40) -#define MAGIC_CMD_ACCESS (0x43) - -#define MAGIC_MIFARE_READ_CMD (0x30) -#define MAGIC_MIFARE_WRITE_CMD (0xA0) - -#define MAGIC_ACK (0x0A) - -#define MAGIC_BUFFER_SIZE (32) - -bool magic_gen1_wupa() { - bool magic_activated = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - // Start communication - tx_data[0] = MAGIC_CMD_WUPA; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 7, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - magic_activated = true; - } while(false); - - return magic_activated; -} - -bool magic_gen1_data_access_cmd() { - bool write_cmd_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - tx_data[0] = MAGIC_CMD_ACCESS; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | - FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - - write_cmd_success = true; - } while(false); - - return write_cmd_success; -} - -bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data) { - furi_assert(data); - - bool read_success = false; - - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - tx_data[0] = MAGIC_MIFARE_READ_CMD; - tx_data[1] = block_num; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 2 * 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON, - furi_hal_nfc_ll_ms2fc(20)); - - if(ret != FuriHalNfcReturnOk) break; - if(rx_len != 16 * 8) break; - memcpy(data->value, rx_data, sizeof(data->value)); - read_success = true; - } while(false); - - return read_success; -} - -bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data) { - furi_assert(data); - - bool write_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - tx_data[0] = MAGIC_MIFARE_WRITE_CMD; - tx_data[1] = block_num; - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 2 * 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - - memcpy(tx_data, data->value, sizeof(data->value)); - ret = furi_hal_nfc_ll_txrx_bits( - tx_data, - 16 * 8, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnIncompleteByte) break; - if(rx_len != 4) break; - if(rx_data[0] != MAGIC_ACK) break; - - write_success = true; - } while(false); - - return write_success; -} diff --git a/applications/external/nfc_magic/lib/magic/classic_gen1.h b/applications/external/nfc_magic/lib/magic/classic_gen1.h deleted file mode 100644 index 98de12302..000000000 --- a/applications/external/nfc_magic/lib/magic/classic_gen1.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -bool magic_gen1_wupa(); - -bool magic_gen1_read_block(uint8_t block_num, MfClassicBlock* data); - -bool magic_gen1_data_access_cmd(); - -bool magic_gen1_write_blk(uint8_t block_num, MfClassicBlock* data); diff --git a/applications/external/nfc_magic/lib/magic/common.c b/applications/external/nfc_magic/lib/magic/common.c deleted file mode 100644 index 0ea3cb218..000000000 --- a/applications/external/nfc_magic/lib/magic/common.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "common.h" - -#include - -#define REQA (0x26) -#define CL1_PREFIX (0x93) -#define SELECT (0x70) - -#define MAGIC_BUFFER_SIZE (32) - -bool magic_activate() { - FuriHalNfcReturn ret = 0; - - // Setup nfc poller - furi_hal_nfc_exit_sleep(); - furi_hal_nfc_ll_txrx_on(); - furi_hal_nfc_ll_poll(); - ret = furi_hal_nfc_ll_set_mode( - FuriHalNfcModePollNfca, FuriHalNfcBitrate106, FuriHalNfcBitrate106); - if(ret != FuriHalNfcReturnOk) return false; - - furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_NFCA_POLLER); - furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_NFCA_POLLER); - furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); - furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_NFCA); - - return true; -} - -void magic_deactivate() { - furi_hal_nfc_ll_txrx_off(); - furi_hal_nfc_sleep(); -} \ No newline at end of file diff --git a/applications/external/nfc_magic/lib/magic/common.h b/applications/external/nfc_magic/lib/magic/common.h deleted file mode 100644 index bef166c8f..000000000 --- a/applications/external/nfc_magic/lib/magic/common.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -typedef enum { - MagicTypeClassicGen1, - MagicTypeClassicDirectWrite, - MagicTypeClassicAPDU, - MagicTypeUltralightGen1, - MagicTypeUltralightDirectWrite, - MagicTypeUltralightC_Gen1, - MagicTypeUltralightC_DirectWrite, - MagicTypeGen4, -} MagicType; - -bool magic_activate(); - -void magic_deactivate(); \ No newline at end of file diff --git a/applications/external/nfc_magic/lib/magic/gen4.c b/applications/external/nfc_magic/lib/magic/gen4.c deleted file mode 100644 index 31be649a0..000000000 --- a/applications/external/nfc_magic/lib/magic/gen4.c +++ /dev/null @@ -1,199 +0,0 @@ -#include "gen4.h" - -#include -#include - -#define TAG "Magic" - -#define MAGIC_CMD_PREFIX (0xCF) - -#define MAGIC_CMD_GET_CFG (0xC6) -#define MAGIC_CMD_WRITE (0xCD) -#define MAGIC_CMD_READ (0xCE) -#define MAGIC_CMD_SET_CFG (0xF0) -#define MAGIC_CMD_FUSE_CFG (0xF1) -#define MAGIC_CMD_SET_PWD (0xFE) - -#define MAGIC_BUFFER_SIZE (40) - -const uint8_t MAGIC_DEFAULT_CONFIG[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09, 0x78, 0x00, 0x91, 0x02, 0xDA, 0xBC, 0x19, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x04, 0x00, 0x08, 0x00 -}; - -const uint8_t MAGIC_DEFAULT_BLOCK0[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -const uint8_t MAGIC_EMPTY_BLOCK[16] = { 0 }; - -const uint8_t MAGIC_DEFAULT_SECTOR_TRAILER[] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF -}; - -static bool magic_gen4_is_block_num_trailer(uint8_t n) { - n++; - if (n < 32 * 4) { - return (n % 4 == 0); - } - - return (n % 16 == 0); -} - -bool magic_gen4_get_cfg(uint32_t pwd, uint8_t* config) { - bool is_valid_config_len = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - // Start communication - tx_data[0] = MAGIC_CMD_PREFIX; - tx_data[1] = (uint8_t)(pwd >> 24); - tx_data[2] = (uint8_t)(pwd >> 16); - tx_data[3] = (uint8_t)(pwd >> 8); - tx_data[4] = (uint8_t)pwd; - tx_data[5] = MAGIC_CMD_GET_CFG; - ret = furi_hal_nfc_ll_txrx( - tx_data, - 6, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_TXRX_DEFAULT, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnOk) break; - if(rx_len != 30 && rx_len != 32) break; - memcpy(config, rx_data, rx_len); - is_valid_config_len = true; - } while(false); - - return is_valid_config_len; -} - -bool magic_gen4_set_cfg(uint32_t pwd, const uint8_t* config, uint8_t config_length, bool fuse) { - bool write_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - // Start communication - tx_data[0] = MAGIC_CMD_PREFIX; - tx_data[1] = (uint8_t)(pwd >> 24); - tx_data[2] = (uint8_t)(pwd >> 16); - tx_data[3] = (uint8_t)(pwd >> 8); - tx_data[4] = (uint8_t)pwd; - tx_data[5] = fuse ? MAGIC_CMD_FUSE_CFG : MAGIC_CMD_SET_CFG; - memcpy(tx_data + 6, config, config_length); - ret = furi_hal_nfc_ll_txrx( - tx_data, - 6 + config_length, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_TXRX_DEFAULT, - furi_hal_nfc_ll_ms2fc(20)); - if(ret != FuriHalNfcReturnOk) break; - if(rx_len != 2) break; - write_success = true; - } while(false); - - return write_success; -} - -bool magic_gen4_set_pwd(uint32_t old_pwd, uint32_t new_pwd) { - bool change_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - // Start communication - tx_data[0] = MAGIC_CMD_PREFIX; - tx_data[1] = (uint8_t)(old_pwd >> 24); - tx_data[2] = (uint8_t)(old_pwd >> 16); - tx_data[3] = (uint8_t)(old_pwd >> 8); - tx_data[4] = (uint8_t)old_pwd; - tx_data[5] = MAGIC_CMD_SET_PWD; - tx_data[6] = (uint8_t)(new_pwd >> 24); - tx_data[7] = (uint8_t)(new_pwd >> 16); - tx_data[8] = (uint8_t)(new_pwd >> 8); - tx_data[9] = (uint8_t)new_pwd; - ret = furi_hal_nfc_ll_txrx( - tx_data, - 10, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_TXRX_DEFAULT, - furi_hal_nfc_ll_ms2fc(20)); - FURI_LOG_I(TAG, "ret %d, len %d", ret, rx_len); - if(ret != FuriHalNfcReturnOk) break; - if(rx_len != 2) break; - change_success = true; - } while(false); - - return change_success; -} - -bool magic_gen4_write_blk(uint32_t pwd, uint8_t block_num, const uint8_t* data) { - bool write_success = false; - uint8_t tx_data[MAGIC_BUFFER_SIZE] = {}; - uint8_t rx_data[MAGIC_BUFFER_SIZE] = {}; - uint16_t rx_len = 0; - FuriHalNfcReturn ret = 0; - - do { - // Start communication - tx_data[0] = MAGIC_CMD_PREFIX; - tx_data[1] = (uint8_t)(pwd >> 24); - tx_data[2] = (uint8_t)(pwd >> 16); - tx_data[3] = (uint8_t)(pwd >> 8); - tx_data[4] = (uint8_t)pwd; - tx_data[5] = MAGIC_CMD_WRITE; - tx_data[6] = block_num; - memcpy(tx_data + 7, data, 16); - ret = furi_hal_nfc_ll_txrx( - tx_data, - 23, - rx_data, - sizeof(rx_data), - &rx_len, - FURI_HAL_NFC_TXRX_DEFAULT, - furi_hal_nfc_ll_ms2fc(200)); - if(ret != FuriHalNfcReturnOk) break; - if(rx_len != 2) break; - write_success = true; - } while(false); - - return write_success; -} - -bool magic_gen4_wipe(uint32_t pwd) { - if(!magic_gen4_set_cfg(pwd, MAGIC_DEFAULT_CONFIG, sizeof(MAGIC_DEFAULT_CONFIG), false)) { - FURI_LOG_E(TAG, "Set config failed"); - return false; - } - if(!magic_gen4_write_blk(pwd, 0, MAGIC_DEFAULT_BLOCK0)) { - FURI_LOG_E(TAG, "Block 0 write failed"); - return false; - } - for(size_t i = 1; i < 64; i++) { - const uint8_t* block = magic_gen4_is_block_num_trailer(i) ? MAGIC_DEFAULT_SECTOR_TRAILER : MAGIC_EMPTY_BLOCK; - if(!magic_gen4_write_blk(pwd, i, block)) { - FURI_LOG_E(TAG, "Block %d write failed", i); - return false; - } - } - for(size_t i = 65; i < 256; i++) { - if(!magic_gen4_write_blk(pwd, i, MAGIC_EMPTY_BLOCK)) { - FURI_LOG_E(TAG, "Block %d write failed", i); - return false; - } - } - - return true; -} \ No newline at end of file diff --git a/applications/external/nfc_magic/lib/magic/gen4.h b/applications/external/nfc_magic/lib/magic/gen4.h deleted file mode 100644 index c515af820..000000000 --- a/applications/external/nfc_magic/lib/magic/gen4.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include - -#define MAGIC_GEN4_DEFAULT_PWD 0x00000000 -#define MAGIC_GEN4_CONFIG_LEN 32 - -#define NFCID1_SINGLE_SIZE 4 -#define NFCID1_DOUBLE_SIZE 7 -#define NFCID1_TRIPLE_SIZE 10 - -typedef enum { - MagicGen4UIDLengthSingle = 0x00, - MagicGen4UIDLengthDouble = 0x01, - MagicGen4UIDLengthTriple = 0x02 -} MagicGen4UIDLength; - -typedef enum { - MagicGen4UltralightModeUL_EV1 = 0x00, - MagicGen4UltralightModeNTAG = 0x01, - MagicGen4UltralightModeUL_C = 0x02, - MagicGen4UltralightModeUL = 0x03 -} MagicGen4UltralightMode; - -typedef enum { - // for writing original (shadow) data - MagicGen4ShadowModePreWrite = 0x00, - // written data can be read once before restored to original - MagicGen4ShadowModeRestore = 0x01, - // written data is discarded - MagicGen4ShadowModeIgnore = 0x02, - // apparently for UL? - MagicGen4ShadowModeHighSpeedIgnore = 0x03 -} MagicGen4ShadowMode; - -bool magic_gen4_get_cfg(uint32_t pwd, uint8_t* config); - -bool magic_gen4_set_cfg(uint32_t pwd, const uint8_t* config, uint8_t config_length, bool fuse); - -bool magic_gen4_set_pwd(uint32_t old_pwd, uint32_t new_pwd); - -bool magic_gen4_read_blk(uint32_t pwd, uint8_t block_num, uint8_t* data); - -bool magic_gen4_write_blk(uint32_t pwd, uint8_t block_num, const uint8_t* data); - -bool magic_gen4_wipe(uint32_t pwd); - -void magic_gen4_deactivate(); diff --git a/applications/external/nfc_magic/lib/magic/types.c b/applications/external/nfc_magic/lib/magic/types.c deleted file mode 100644 index 77c6c0a4e..000000000 --- a/applications/external/nfc_magic/lib/magic/types.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "types.h" - -const char* nfc_magic_type(MagicType type) { - if(type == MagicTypeClassicGen1) { - return "Classic Gen 1A/B"; - } else if(type == MagicTypeClassicDirectWrite) { - return "Classic DirectWrite"; - } else if(type == MagicTypeClassicAPDU) { - return "Classic APDU"; - } else if(type == MagicTypeUltralightGen1) { - return "Ultralight Gen 1"; - } else if(type == MagicTypeUltralightDirectWrite) { - return "Ultralight DirectWrite"; - } else if(type == MagicTypeUltralightC_Gen1) { - return "Ultralight-C Gen 1"; - } else if(type == MagicTypeUltralightC_DirectWrite) { - return "Ultralight-C DirectWrite"; - } else if(type == MagicTypeGen4) { - return "Gen 4 GTU"; - } else { - return "Unknown"; - } -} diff --git a/applications/external/nfc_magic/lib/magic/types.h b/applications/external/nfc_magic/lib/magic/types.h deleted file mode 100644 index dbf554063..000000000 --- a/applications/external/nfc_magic/lib/magic/types.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "common.h" - -const char* nfc_magic_type(MagicType type); \ No newline at end of file diff --git a/applications/external/nfc_magic/nfc_magic.c b/applications/external/nfc_magic/nfc_magic.c deleted file mode 100644 index 68c9a65b5..000000000 --- a/applications/external/nfc_magic/nfc_magic.c +++ /dev/null @@ -1,184 +0,0 @@ -#include "nfc_magic_i.h" - -bool nfc_magic_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - NfcMagic* nfc_magic = context; - return scene_manager_handle_custom_event(nfc_magic->scene_manager, event); -} - -bool nfc_magic_back_event_callback(void* context) { - furi_assert(context); - NfcMagic* nfc_magic = context; - return scene_manager_handle_back_event(nfc_magic->scene_manager); -} - -void nfc_magic_tick_event_callback(void* context) { - furi_assert(context); - NfcMagic* nfc_magic = context; - scene_manager_handle_tick_event(nfc_magic->scene_manager); -} - -void nfc_magic_show_loading_popup(void* context, bool show) { - NfcMagic* nfc_magic = context; - TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); - - if(show) { - // Raise timer priority so that animations can play - vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewLoading); - } else { - // Restore default timer priority - vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); - } -} - -NfcMagic* nfc_magic_alloc() { - NfcMagic* nfc_magic = malloc(sizeof(NfcMagic)); - - nfc_magic->worker = nfc_magic_worker_alloc(); - nfc_magic->view_dispatcher = view_dispatcher_alloc(); - nfc_magic->scene_manager = scene_manager_alloc(&nfc_magic_scene_handlers, nfc_magic); - view_dispatcher_enable_queue(nfc_magic->view_dispatcher); - view_dispatcher_set_event_callback_context(nfc_magic->view_dispatcher, nfc_magic); - view_dispatcher_set_custom_event_callback( - nfc_magic->view_dispatcher, nfc_magic_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - nfc_magic->view_dispatcher, nfc_magic_back_event_callback); - view_dispatcher_set_tick_event_callback( - nfc_magic->view_dispatcher, nfc_magic_tick_event_callback, 100); - - // Nfc device - nfc_magic->dev = malloc(sizeof(NfcMagicDevice)); - nfc_magic->source_dev = nfc_device_alloc(); - furi_string_set(nfc_magic->source_dev->folder, NFC_APP_FOLDER); - - // Open GUI record - nfc_magic->gui = furi_record_open(RECORD_GUI); - view_dispatcher_attach_to_gui( - nfc_magic->view_dispatcher, nfc_magic->gui, ViewDispatcherTypeFullscreen); - - // Open Notification record - nfc_magic->notifications = furi_record_open(RECORD_NOTIFICATION); - - // Submenu - nfc_magic->submenu = submenu_alloc(); - view_dispatcher_add_view( - nfc_magic->view_dispatcher, NfcMagicViewMenu, submenu_get_view(nfc_magic->submenu)); - - // Popup - nfc_magic->popup = popup_alloc(); - view_dispatcher_add_view( - nfc_magic->view_dispatcher, NfcMagicViewPopup, popup_get_view(nfc_magic->popup)); - - // Loading - nfc_magic->loading = loading_alloc(); - view_dispatcher_add_view( - nfc_magic->view_dispatcher, NfcMagicViewLoading, loading_get_view(nfc_magic->loading)); - - // Text Input - nfc_magic->text_input = text_input_alloc(); - view_dispatcher_add_view( - nfc_magic->view_dispatcher, - NfcMagicViewTextInput, - text_input_get_view(nfc_magic->text_input)); - - // Byte Input - nfc_magic->byte_input = byte_input_alloc(); - view_dispatcher_add_view( - nfc_magic->view_dispatcher, - NfcMagicViewByteInput, - byte_input_get_view(nfc_magic->byte_input)); - - // Custom Widget - nfc_magic->widget = widget_alloc(); - view_dispatcher_add_view( - nfc_magic->view_dispatcher, NfcMagicViewWidget, widget_get_view(nfc_magic->widget)); - - return nfc_magic; -} - -void nfc_magic_free(NfcMagic* nfc_magic) { - furi_assert(nfc_magic); - - // Nfc device - free(nfc_magic->dev); - nfc_device_free(nfc_magic->source_dev); - - // Submenu - view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); - submenu_free(nfc_magic->submenu); - - // Popup - view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); - popup_free(nfc_magic->popup); - - // Loading - view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewLoading); - loading_free(nfc_magic->loading); - - // Text Input - view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewTextInput); - text_input_free(nfc_magic->text_input); - - // Byte Input - view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput); - byte_input_free(nfc_magic->byte_input); - - // Custom Widget - view_dispatcher_remove_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); - widget_free(nfc_magic->widget); - - // Worker - nfc_magic_worker_stop(nfc_magic->worker); - nfc_magic_worker_free(nfc_magic->worker); - - // View Dispatcher - view_dispatcher_free(nfc_magic->view_dispatcher); - - // Scene Manager - scene_manager_free(nfc_magic->scene_manager); - - // GUI - furi_record_close(RECORD_GUI); - nfc_magic->gui = NULL; - - // Notifications - furi_record_close(RECORD_NOTIFICATION); - nfc_magic->notifications = NULL; - - free(nfc_magic); -} - -static const NotificationSequence nfc_magic_sequence_blink_start_cyan = { - &message_blink_start_10, - &message_blink_set_color_cyan, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence nfc_magic_sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - -void nfc_magic_blink_start(NfcMagic* nfc_magic) { - notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_cyan); -} - -void nfc_magic_blink_stop(NfcMagic* nfc_magic) { - notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_stop); -} - -int32_t nfc_magic_app(void* p) { - UNUSED(p); - NfcMagic* nfc_magic = nfc_magic_alloc(); - - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneStart); - - view_dispatcher_run(nfc_magic->view_dispatcher); - - magic_deactivate(); - nfc_magic_free(nfc_magic); - - return 0; -} diff --git a/applications/external/nfc_magic/nfc_magic.h b/applications/external/nfc_magic/nfc_magic.h deleted file mode 100644 index f9cf395d8..000000000 --- a/applications/external/nfc_magic/nfc_magic.h +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -typedef struct NfcMagicDevice NfcMagicDevice; - -typedef struct NfcMagic NfcMagic; diff --git a/applications/external/nfc_magic/nfc_magic_i.h b/applications/external/nfc_magic/nfc_magic_i.h deleted file mode 100644 index 88bc5706f..000000000 --- a/applications/external/nfc_magic/nfc_magic_i.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include "nfc_magic.h" -#include "nfc_magic_worker.h" - -#include "lib/magic/common.h" -#include "lib/magic/types.h" -#include "lib/magic/classic_gen1.h" -#include "lib/magic/gen4.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include "scenes/nfc_magic_scene.h" - -#include -#include - -#include -#include "nfc_magic_icons.h" - -#define NFC_APP_FOLDER ANY_PATH("nfc") - -enum NfcMagicCustomEvent { - // Reserve first 100 events for button types and indexes, starting from 0 - NfcMagicCustomEventReserved = 100, - - NfcMagicCustomEventViewExit, - NfcMagicCustomEventWorkerExit, - NfcMagicCustomEventByteInputDone, - NfcMagicCustomEventTextInputDone, -}; - -struct NfcMagicDevice { - MagicType type; - uint32_t cuid; - uint8_t uid_len; - uint32_t password; -}; - -struct NfcMagic { - NfcMagicWorker* worker; - ViewDispatcher* view_dispatcher; - Gui* gui; - NotificationApp* notifications; - SceneManager* scene_manager; - struct NfcMagicDevice* dev; - NfcDevice* source_dev; - - uint32_t new_password; - - FuriString* text_box_store; - - // Common Views - Submenu* submenu; - Popup* popup; - Loading* loading; - TextInput* text_input; - ByteInput* byte_input; - Widget* widget; -}; - -typedef enum { - NfcMagicViewMenu, - NfcMagicViewPopup, - NfcMagicViewLoading, - NfcMagicViewTextInput, - NfcMagicViewByteInput, - NfcMagicViewWidget, -} NfcMagicView; - -NfcMagic* nfc_magic_alloc(); - -void nfc_magic_text_store_set(NfcMagic* nfc_magic, const char* text, ...); - -void nfc_magic_text_store_clear(NfcMagic* nfc_magic); - -void nfc_magic_blink_start(NfcMagic* nfc_magic); - -void nfc_magic_blink_stop(NfcMagic* nfc_magic); - -void nfc_magic_show_loading_popup(void* context, bool show); diff --git a/applications/external/nfc_magic/nfc_magic_worker.c b/applications/external/nfc_magic/nfc_magic_worker.c deleted file mode 100644 index eb715fe0d..000000000 --- a/applications/external/nfc_magic/nfc_magic_worker.c +++ /dev/null @@ -1,480 +0,0 @@ -#include "nfc_magic_worker_i.h" - -#include "nfc_magic_i.h" -#include "lib/magic/common.h" -#include "lib/magic/classic_gen1.h" -#include "lib/magic/gen4.h" - -#define TAG "NfcMagicWorker" - -static void - nfc_magic_worker_change_state(NfcMagicWorker* nfc_magic_worker, NfcMagicWorkerState state) { - furi_assert(nfc_magic_worker); - - nfc_magic_worker->state = state; -} - -NfcMagicWorker* nfc_magic_worker_alloc() { - NfcMagicWorker* nfc_magic_worker = malloc(sizeof(NfcMagicWorker)); - - // Worker thread attributes - nfc_magic_worker->thread = - furi_thread_alloc_ex("NfcMagicWorker", 8192, nfc_magic_worker_task, nfc_magic_worker); - - nfc_magic_worker->callback = NULL; - nfc_magic_worker->context = NULL; - - nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady); - - return nfc_magic_worker; -} - -void nfc_magic_worker_free(NfcMagicWorker* nfc_magic_worker) { - furi_assert(nfc_magic_worker); - - furi_thread_free(nfc_magic_worker->thread); - free(nfc_magic_worker); -} - -void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker) { - furi_assert(nfc_magic_worker); - - nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateStop); - furi_thread_join(nfc_magic_worker->thread); -} - -void nfc_magic_worker_start( - NfcMagicWorker* nfc_magic_worker, - NfcMagicWorkerState state, - NfcMagicDevice* magic_dev, - NfcDeviceData* dev_data, - uint32_t new_password, - NfcMagicWorkerCallback callback, - void* context) { - furi_assert(nfc_magic_worker); - furi_assert(magic_dev); - furi_assert(dev_data); - - nfc_magic_worker->callback = callback; - nfc_magic_worker->context = context; - nfc_magic_worker->magic_dev = magic_dev; - nfc_magic_worker->dev_data = dev_data; - nfc_magic_worker->new_password = new_password; - nfc_magic_worker_change_state(nfc_magic_worker, state); - furi_thread_start(nfc_magic_worker->thread); -} - -int32_t nfc_magic_worker_task(void* context) { - NfcMagicWorker* nfc_magic_worker = context; - - if(nfc_magic_worker->state == NfcMagicWorkerStateCheck) { - nfc_magic_worker_check(nfc_magic_worker); - } else if(nfc_magic_worker->state == NfcMagicWorkerStateWrite) { - nfc_magic_worker_write(nfc_magic_worker); - } else if(nfc_magic_worker->state == NfcMagicWorkerStateRekey) { - nfc_magic_worker_rekey(nfc_magic_worker); - } else if(nfc_magic_worker->state == NfcMagicWorkerStateWipe) { - nfc_magic_worker_wipe(nfc_magic_worker); - } - - nfc_magic_worker_change_state(nfc_magic_worker, NfcMagicWorkerStateReady); - - return 0; -} - -void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker) { - bool card_found_notified = false; - bool done = false; - FuriHalNfcDevData nfc_data = {}; - NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; - NfcDeviceData* dev_data = nfc_magic_worker->dev_data; - NfcProtocol dev_protocol = dev_data->protocol; - - while(nfc_magic_worker->state == NfcMagicWorkerStateWrite) { - do { - if(magic_dev->type == MagicTypeClassicGen1) { - if(furi_hal_nfc_detect(&nfc_data, 200)) { - magic_deactivate(); - magic_activate(); - if(!magic_gen1_wupa()) { - FURI_LOG_E(TAG, "No card response to WUPA (not a magic card)"); - nfc_magic_worker->callback( - NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); - done = true; - break; - } - magic_deactivate(); - } - magic_activate(); - if(magic_gen1_wupa()) { - magic_gen1_data_access_cmd(); - - MfClassicData* mfc_data = &dev_data->mf_classic_data; - for(size_t i = 0; i < 64; i++) { - FURI_LOG_D(TAG, "Writing block %d", i); - if(!magic_gen1_write_blk(i, &mfc_data->block[i])) { - FURI_LOG_E(TAG, "Failed to write %d block", i); - done = true; - nfc_magic_worker->callback( - NfcMagicWorkerEventFail, nfc_magic_worker->context); - break; - } - } - - done = true; - nfc_magic_worker->callback( - NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - break; - } - } else if(magic_dev->type == MagicTypeGen4) { - if(furi_hal_nfc_detect(&nfc_data, 200)) { - uint8_t gen4_config[28]; - uint32_t password = magic_dev->password; - - uint32_t cuid; - if(dev_protocol == NfcDeviceProtocolMifareClassic) { - gen4_config[0] = 0x00; - gen4_config[27] = 0x00; - } else if(dev_protocol == NfcDeviceProtocolMifareUl) { - MfUltralightData* mf_ul_data = &dev_data->mf_ul_data; - gen4_config[0] = 0x01; - switch(mf_ul_data->type) { - case MfUltralightTypeUL11: - case MfUltralightTypeUL21: - // UL-C? - // UL? - default: - gen4_config[27] = MagicGen4UltralightModeUL_EV1; - break; - case MfUltralightTypeNTAG203: - case MfUltralightTypeNTAG213: - case MfUltralightTypeNTAG215: - case MfUltralightTypeNTAG216: - case MfUltralightTypeNTAGI2C1K: - case MfUltralightTypeNTAGI2C2K: - case MfUltralightTypeNTAGI2CPlus1K: - case MfUltralightTypeNTAGI2CPlus2K: - gen4_config[27] = MagicGen4UltralightModeNTAG; - break; - } - } - - if(dev_data->nfc_data.uid_len == 4) { - gen4_config[1] = MagicGen4UIDLengthSingle; - } else if(dev_data->nfc_data.uid_len == 7) { - gen4_config[1] = MagicGen4UIDLengthDouble; - } else { - FURI_LOG_E(TAG, "Unexpected UID length %d", dev_data->nfc_data.uid_len); - nfc_magic_worker->callback( - NfcMagicWorkerEventFail, nfc_magic_worker->context); - done = true; - break; - } - - gen4_config[2] = (uint8_t)(password >> 24); - gen4_config[3] = (uint8_t)(password >> 16); - gen4_config[4] = (uint8_t)(password >> 8); - gen4_config[5] = (uint8_t)password; - - if(dev_protocol == NfcDeviceProtocolMifareUl) { - gen4_config[6] = MagicGen4ShadowModeHighSpeedIgnore; - } else { - gen4_config[6] = MagicGen4ShadowModeIgnore; - } - gen4_config[7] = 0x00; - memset(gen4_config + 8, 0, 16); - gen4_config[24] = dev_data->nfc_data.atqa[0]; - gen4_config[25] = dev_data->nfc_data.atqa[1]; - gen4_config[26] = dev_data->nfc_data.sak; - - furi_hal_nfc_sleep(); - furi_hal_nfc_activate_nfca(200, &cuid); - if(!magic_gen4_set_cfg(password, gen4_config, sizeof(gen4_config), false)) { - nfc_magic_worker->callback( - NfcMagicWorkerEventFail, nfc_magic_worker->context); - done = true; - break; - } - if(dev_protocol == NfcDeviceProtocolMifareClassic) { - MfClassicData* mfc_data = &dev_data->mf_classic_data; - size_t block_count = 64; - if(mfc_data->type == MfClassicType4k) block_count = 256; - for(size_t i = 0; i < block_count; i++) { - FURI_LOG_D(TAG, "Writing block %d", i); - if(!magic_gen4_write_blk(password, i, mfc_data->block[i].value)) { - FURI_LOG_E(TAG, "Failed to write %d block", i); - nfc_magic_worker->callback( - NfcMagicWorkerEventFail, nfc_magic_worker->context); - done = true; - break; - } - } - } else if(dev_protocol == NfcDeviceProtocolMifareUl) { - MfUltralightData* mf_ul_data = &dev_data->mf_ul_data; - for(size_t i = 0; (i * 4) < mf_ul_data->data_read; i++) { - size_t data_offset = i * 4; - FURI_LOG_D( - TAG, - "Writing page %zu (%zu/%u)", - i, - data_offset, - mf_ul_data->data_read); - uint8_t* block = mf_ul_data->data + data_offset; - if(!magic_gen4_write_blk(password, i, block)) { - FURI_LOG_E(TAG, "Failed to write %zu page", i); - nfc_magic_worker->callback( - NfcMagicWorkerEventFail, nfc_magic_worker->context); - done = true; - break; - } - } - - uint8_t buffer[16] = {0}; - - for(size_t i = 0; i < 8; i++) { - memcpy(buffer, &mf_ul_data->signature[i * 4], 4); //-V1086 - if(!magic_gen4_write_blk(password, 0xF2 + i, buffer)) { - FURI_LOG_E(TAG, "Failed to write signature block %d", i); - nfc_magic_worker->callback( - NfcMagicWorkerEventFail, nfc_magic_worker->context); - done = true; - break; - } - } - - buffer[0] = mf_ul_data->version.header; - buffer[1] = mf_ul_data->version.vendor_id; - buffer[2] = mf_ul_data->version.prod_type; - buffer[3] = mf_ul_data->version.prod_subtype; - if(!magic_gen4_write_blk(password, 0xFA, buffer)) { - FURI_LOG_E(TAG, "Failed to write version block 0"); - nfc_magic_worker->callback( - NfcMagicWorkerEventFail, nfc_magic_worker->context); - done = true; - break; - } - - buffer[0] = mf_ul_data->version.prod_ver_major; - buffer[1] = mf_ul_data->version.prod_ver_minor; - buffer[2] = mf_ul_data->version.storage_size; - buffer[3] = mf_ul_data->version.protocol_type; - if(!magic_gen4_write_blk(password, 0xFB, buffer)) { - FURI_LOG_E(TAG, "Failed to write version block 1"); - nfc_magic_worker->callback( - NfcMagicWorkerEventFail, nfc_magic_worker->context); - done = true; - break; - } - } - - nfc_magic_worker->callback( - NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - done = true; - break; - } - } - } while(false); - - if(done) break; - - if(card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); - card_found_notified = false; - } - - furi_delay_ms(300); - } - magic_deactivate(); -} - -void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker) { - FuriHalNfcDevData nfc_data = {}; - NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; - bool card_found_notified = false; - uint8_t gen4_config[MAGIC_GEN4_CONFIG_LEN]; - - while(nfc_magic_worker->state == NfcMagicWorkerStateCheck) { - magic_activate(); - if(magic_gen1_wupa()) { - magic_dev->type = MagicTypeClassicGen1; - if(!card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; - } - - if(furi_hal_nfc_detect(&nfc_data, 200)) { - magic_dev->cuid = nfc_data.cuid; - magic_dev->uid_len = nfc_data.uid_len; - } else { - // wrong BCC - magic_dev->uid_len = 4; - } - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - break; - } else { - magic_deactivate(); - magic_activate(); - if(furi_hal_nfc_detect(&nfc_data, 200)) { - magic_dev->cuid = nfc_data.cuid; - magic_dev->uid_len = nfc_data.uid_len; - if(magic_gen4_get_cfg(magic_dev->password, gen4_config)) { - magic_dev->type = MagicTypeGen4; - if(!card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; - } - - nfc_magic_worker->callback( - NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - } else { - nfc_magic_worker->callback( - NfcMagicWorkerEventWrongCard, nfc_magic_worker->context); - card_found_notified = true; - } - break; - } else { - if(card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); - card_found_notified = false; - } - } - } - - magic_deactivate(); - furi_delay_ms(300); - } - - magic_deactivate(); -} - -void nfc_magic_worker_rekey(NfcMagicWorker* nfc_magic_worker) { - NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; - bool card_found_notified = false; - - if(magic_dev->type != MagicTypeGen4) { - nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - return; - } - - while(nfc_magic_worker->state == NfcMagicWorkerStateRekey) { - magic_activate(); - uint32_t cuid; - furi_hal_nfc_activate_nfca(200, &cuid); - if(cuid != magic_dev->cuid) { - if(card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); - card_found_notified = false; - } - continue; - } - - nfc_magic_worker->callback(NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; - - if(magic_gen4_set_pwd(magic_dev->password, nfc_magic_worker->new_password)) { - magic_dev->password = nfc_magic_worker->new_password; - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - break; - } - - if(card_found_notified) { //-V547 - nfc_magic_worker->callback( - NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); - card_found_notified = false; - } - furi_delay_ms(300); - } - magic_deactivate(); -} - -void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker) { - NfcMagicDevice* magic_dev = nfc_magic_worker->magic_dev; - bool card_found_notified = false; - bool card_wiped = false; - - MfClassicBlock block; - memset(&block, 0, sizeof(MfClassicBlock)); - MfClassicBlock empty_block; - memset(&empty_block, 0, sizeof(MfClassicBlock)); - MfClassicBlock trailer_block; - memset(&trailer_block, 0xff, sizeof(MfClassicBlock)); - - block.value[0] = 0x01; - block.value[1] = 0x02; - block.value[2] = 0x03; - block.value[3] = 0x04; - block.value[4] = 0x04; - block.value[5] = 0x08; - block.value[6] = 0x04; - - trailer_block.value[7] = 0x07; - trailer_block.value[8] = 0x80; - trailer_block.value[9] = 0x69; - - while(nfc_magic_worker->state == NfcMagicWorkerStateWipe) { - do { - magic_deactivate(); - furi_delay_ms(300); - if(!magic_activate()) break; - if(magic_dev->type == MagicTypeClassicGen1) { - if(!magic_gen1_wupa()) break; - if(!card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; - } - - if(!magic_gen1_data_access_cmd()) break; - if(!magic_gen1_write_blk(0, &block)) break; - - for(size_t i = 1; i < 64; i++) { - FURI_LOG_D(TAG, "Wiping block %d", i); - bool success = false; - if((i | 0x03) == i) { - success = magic_gen1_write_blk(i, &trailer_block); - } else { - success = magic_gen1_write_blk(i, &empty_block); - } - - if(!success) { - FURI_LOG_E(TAG, "Failed to write %d block", i); - nfc_magic_worker->callback( - NfcMagicWorkerEventFail, nfc_magic_worker->context); - break; - } - } - - card_wiped = true; - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - } else if(magic_dev->type == MagicTypeGen4) { - uint32_t cuid; - if(!furi_hal_nfc_activate_nfca(200, &cuid)) break; - if(cuid != magic_dev->cuid) break; - if(!card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventCardDetected, nfc_magic_worker->context); - card_found_notified = true; - } - - if(!magic_gen4_wipe(magic_dev->password)) break; - - card_wiped = true; - nfc_magic_worker->callback(NfcMagicWorkerEventSuccess, nfc_magic_worker->context); - } - } while(false); - - if(card_wiped) break; - - if(card_found_notified) { - nfc_magic_worker->callback( - NfcMagicWorkerEventNoCardDetected, nfc_magic_worker->context); - card_found_notified = false; - } - } - magic_deactivate(); -} diff --git a/applications/external/nfc_magic/nfc_magic_worker.h b/applications/external/nfc_magic/nfc_magic_worker.h deleted file mode 100644 index 51ff4ee43..000000000 --- a/applications/external/nfc_magic/nfc_magic_worker.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include "nfc_magic.h" - -typedef struct NfcMagicWorker NfcMagicWorker; - -typedef enum { - NfcMagicWorkerStateReady, - - NfcMagicWorkerStateCheck, - NfcMagicWorkerStateWrite, - NfcMagicWorkerStateRekey, - NfcMagicWorkerStateWipe, - - NfcMagicWorkerStateStop, -} NfcMagicWorkerState; - -typedef enum { - NfcMagicWorkerEventSuccess, - NfcMagicWorkerEventFail, - NfcMagicWorkerEventCardDetected, - NfcMagicWorkerEventNoCardDetected, - NfcMagicWorkerEventWrongCard, -} NfcMagicWorkerEvent; - -typedef bool (*NfcMagicWorkerCallback)(NfcMagicWorkerEvent event, void* context); - -NfcMagicWorker* nfc_magic_worker_alloc(); - -void nfc_magic_worker_free(NfcMagicWorker* nfc_magic_worker); - -void nfc_magic_worker_stop(NfcMagicWorker* nfc_magic_worker); - -void nfc_magic_worker_start( - NfcMagicWorker* nfc_magic_worker, - NfcMagicWorkerState state, - NfcMagicDevice* magic_dev, - NfcDeviceData* dev_data, - uint32_t new_password, - NfcMagicWorkerCallback callback, - void* context); diff --git a/applications/external/nfc_magic/nfc_magic_worker_i.h b/applications/external/nfc_magic/nfc_magic_worker_i.h deleted file mode 100644 index a354f8047..000000000 --- a/applications/external/nfc_magic/nfc_magic_worker_i.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include "nfc_magic_worker.h" -#include "lib/magic/common.h" - -struct NfcMagicWorker { - FuriThread* thread; - - NfcMagicDevice* magic_dev; - NfcDeviceData* dev_data; - uint32_t new_password; - - NfcMagicWorkerCallback callback; - void* context; - - NfcMagicWorkerState state; -}; - -int32_t nfc_magic_worker_task(void* context); - -void nfc_magic_worker_check(NfcMagicWorker* nfc_magic_worker); - -void nfc_magic_worker_write(NfcMagicWorker* nfc_magic_worker); - -void nfc_magic_worker_rekey(NfcMagicWorker* nfc_magic_worker); - -void nfc_magic_worker_wipe(NfcMagicWorker* nfc_magic_worker); diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene.c b/applications/external/nfc_magic/scenes/nfc_magic_scene.c deleted file mode 100644 index 520ef2a9d..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "nfc_magic_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const nfc_magic_on_enter_handlers[])(void*) = { -#include "nfc_magic_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const nfc_magic_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "nfc_magic_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const nfc_magic_on_exit_handlers[])(void* context) = { -#include "nfc_magic_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers nfc_magic_scene_handlers = { - .on_enter_handlers = nfc_magic_on_enter_handlers, - .on_event_handlers = nfc_magic_on_event_handlers, - .on_exit_handlers = nfc_magic_on_exit_handlers, - .scene_num = NfcMagicSceneNum, -}; diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene.h b/applications/external/nfc_magic/scenes/nfc_magic_scene.h deleted file mode 100644 index f1e9f715d..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) NfcMagicScene##id, -typedef enum { -#include "nfc_magic_scene_config.h" - NfcMagicSceneNum, -} NfcMagicScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers nfc_magic_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "nfc_magic_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "nfc_magic_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "nfc_magic_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_actions.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_actions.c deleted file mode 100644 index 675262a9b..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_actions.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "../nfc_magic_i.h" -enum SubmenuIndex { - SubmenuIndexWrite, - SubmenuIndexWipe, -}; - -void nfc_magic_scene_actions_submenu_callback(void* context, uint32_t index) { - NfcMagic* nfc_magic = context; - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index); -} - -void nfc_magic_scene_actions_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - Submenu* submenu = nfc_magic->submenu; - submenu_add_item( - submenu, "Write", SubmenuIndexWrite, nfc_magic_scene_actions_submenu_callback, nfc_magic); - submenu_add_item( - submenu, "Wipe", SubmenuIndexWipe, nfc_magic_scene_actions_submenu_callback, nfc_magic); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneActions)); - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); -} - -bool nfc_magic_scene_actions_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect); - consumed = true; - } else if(event.event == SubmenuIndexWipe) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe); - consumed = true; - } - scene_manager_set_scene_state(nfc_magic->scene_manager, NfcMagicSceneActions, event.event); - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc_magic->scene_manager, NfcMagicSceneStart); - } - - return consumed; -} - -void nfc_magic_scene_actions_on_exit(void* context) { - NfcMagic* nfc_magic = context; - submenu_reset(nfc_magic->submenu); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_check.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_check.c deleted file mode 100644 index 90b43d7d3..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_check.c +++ /dev/null @@ -1,89 +0,0 @@ -#include "../nfc_magic_i.h" - -enum { - NfcMagicSceneCheckStateCardSearch, - NfcMagicSceneCheckStateCardFound, -}; - -bool nfc_magic_check_worker_callback(NfcMagicWorkerEvent event, void* context) { - furi_assert(context); - - NfcMagic* nfc_magic = context; - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event); - - return true; -} - -static void nfc_magic_scene_check_setup_view(NfcMagic* nfc_magic) { - Popup* popup = nfc_magic->popup; - popup_reset(popup); - uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneCheck); - - if(state == NfcMagicSceneCheckStateCardSearch) { - popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); - popup_set_text( - nfc_magic->popup, "Apply card to\nthe back", 128, 32, AlignRight, AlignCenter); - } else { - popup_set_icon(popup, 12, 23, &I_Loading_24); - popup_set_header(popup, "Checking\nDon't move...", 52, 32, AlignLeft, AlignCenter); - } - - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); -} - -void nfc_magic_scene_check_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch); - nfc_magic_scene_check_setup_view(nfc_magic); - - // Setup and start worker - nfc_magic_worker_start( - nfc_magic->worker, - NfcMagicWorkerStateCheck, - nfc_magic->dev, - &nfc_magic->source_dev->dev_data, - nfc_magic->new_password, - nfc_magic_check_worker_callback, - nfc_magic); - nfc_magic_blink_start(nfc_magic); -} - -bool nfc_magic_scene_check_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcMagicWorkerEventSuccess) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneMagicInfo); - consumed = true; - } else if(event.event == NfcMagicWorkerEventWrongCard) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic); - consumed = true; - } else if(event.event == NfcMagicWorkerEventCardDetected) { - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardFound); - nfc_magic_scene_check_setup_view(nfc_magic); - consumed = true; - } else if(event.event == NfcMagicWorkerEventNoCardDetected) { - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch); - nfc_magic_scene_check_setup_view(nfc_magic); - consumed = true; - } - } - return consumed; -} - -void nfc_magic_scene_check_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - nfc_magic_worker_stop(nfc_magic->worker); - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneCheck, NfcMagicSceneCheckStateCardSearch); - // Clear view - popup_reset(nfc_magic->popup); - - nfc_magic_blink_stop(nfc_magic); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_config.h b/applications/external/nfc_magic/scenes/nfc_magic_scene_config.h deleted file mode 100644 index 2f9860d96..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_config.h +++ /dev/null @@ -1,18 +0,0 @@ -ADD_SCENE(nfc_magic, start, Start) -ADD_SCENE(nfc_magic, key_input, KeyInput) -ADD_SCENE(nfc_magic, actions, Actions) -ADD_SCENE(nfc_magic, gen4_actions, Gen4Actions) -ADD_SCENE(nfc_magic, new_key_input, NewKeyInput) -ADD_SCENE(nfc_magic, file_select, FileSelect) -ADD_SCENE(nfc_magic, write_confirm, WriteConfirm) -ADD_SCENE(nfc_magic, wrong_card, WrongCard) -ADD_SCENE(nfc_magic, write, Write) -ADD_SCENE(nfc_magic, write_fail, WriteFail) -ADD_SCENE(nfc_magic, success, Success) -ADD_SCENE(nfc_magic, check, Check) -ADD_SCENE(nfc_magic, not_magic, NotMagic) -ADD_SCENE(nfc_magic, magic_info, MagicInfo) -ADD_SCENE(nfc_magic, rekey, Rekey) -ADD_SCENE(nfc_magic, rekey_fail, RekeyFail) -ADD_SCENE(nfc_magic, wipe, Wipe) -ADD_SCENE(nfc_magic, wipe_fail, WipeFail) diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c deleted file mode 100644 index 04b7024ff..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "../nfc_magic_i.h" - -static bool nfc_magic_scene_file_select_is_file_suitable(NfcMagic* nfc_magic) { - NfcDevice* nfc_dev = nfc_magic->source_dev; - if(nfc_dev->format == NfcDeviceSaveFormatMifareClassic) { - switch(nfc_magic->dev->type) { - case MagicTypeClassicGen1: - case MagicTypeClassicDirectWrite: - case MagicTypeClassicAPDU: - if((nfc_dev->dev_data.mf_classic_data.type != MfClassicType1k) || - (nfc_dev->dev_data.nfc_data.uid_len != nfc_magic->dev->uid_len)) { - return false; - } - return true; - - case MagicTypeGen4: - return true; - default: - return false; - } - } else if( - (nfc_dev->format == NfcDeviceSaveFormatMifareUl) && - (nfc_dev->dev_data.nfc_data.uid_len == 7)) { - switch(nfc_magic->dev->type) { - case MagicTypeUltralightGen1: - case MagicTypeUltralightDirectWrite: - case MagicTypeUltralightC_Gen1: - case MagicTypeUltralightC_DirectWrite: - case MagicTypeGen4: - switch(nfc_dev->dev_data.mf_ul_data.type) { - case MfUltralightTypeNTAGI2C1K: - case MfUltralightTypeNTAGI2C2K: - case MfUltralightTypeNTAGI2CPlus1K: - case MfUltralightTypeNTAGI2CPlus2K: - return false; - default: - return true; - } - default: - return false; - } - } - - return false; -} - -void nfc_magic_scene_file_select_on_enter(void* context) { - NfcMagic* nfc_magic = context; - // Process file_select return - nfc_device_set_loading_callback( - nfc_magic->source_dev, nfc_magic_show_loading_popup, nfc_magic); - - if(!furi_string_size(nfc_magic->source_dev->load_path)) { - furi_string_set_str(nfc_magic->source_dev->load_path, NFC_APP_FOLDER); - } - if(nfc_file_select(nfc_magic->source_dev)) { - if(nfc_magic_scene_file_select_is_file_suitable(nfc_magic)) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteConfirm); - } else { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrongCard); - } - } else { - scene_manager_previous_scene(nfc_magic->scene_manager); - } -} - -bool nfc_magic_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void nfc_magic_scene_file_select_on_exit(void* context) { - NfcMagic* nfc_magic = context; - nfc_device_set_loading_callback(nfc_magic->source_dev, NULL, nfc_magic); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_gen4_actions.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_gen4_actions.c deleted file mode 100644 index ceaa33e29..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_gen4_actions.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "../nfc_magic_i.h" -enum SubmenuIndex { - SubmenuIndexWrite, - SubmenuIndexChangePassword, - SubmenuIndexWipe, -}; - -void nfc_magic_scene_gen4_actions_submenu_callback(void* context, uint32_t index) { - NfcMagic* nfc_magic = context; - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index); -} - -void nfc_magic_scene_gen4_actions_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - Submenu* submenu = nfc_magic->submenu; - submenu_add_item( - submenu, - "Write", - SubmenuIndexWrite, - nfc_magic_scene_gen4_actions_submenu_callback, - nfc_magic); - submenu_add_item( - submenu, - "Change password", - SubmenuIndexChangePassword, - nfc_magic_scene_gen4_actions_submenu_callback, - nfc_magic); - submenu_add_item( - submenu, - "Wipe", - SubmenuIndexWipe, - nfc_magic_scene_gen4_actions_submenu_callback, - nfc_magic); - - submenu_set_selected_item( - submenu, - scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneGen4Actions)); - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); -} - -bool nfc_magic_scene_gen4_actions_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect); - consumed = true; - } else if(event.event == SubmenuIndexChangePassword) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNewKeyInput); - consumed = true; - } else if(event.event == SubmenuIndexWipe) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe); - consumed = true; - } - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneGen4Actions, event.event); - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc_magic->scene_manager, NfcMagicSceneStart); - } - - return consumed; -} - -void nfc_magic_scene_gen4_actions_on_exit(void* context) { - NfcMagic* nfc_magic = context; - submenu_reset(nfc_magic->submenu); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_key_input.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_key_input.c deleted file mode 100644 index 58b487a09..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_key_input.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../nfc_magic_i.h" - -void nfc_magic_scene_key_input_byte_input_callback(void* context) { - NfcMagic* nfc_magic = context; - - view_dispatcher_send_custom_event( - nfc_magic->view_dispatcher, NfcMagicCustomEventByteInputDone); -} - -void nfc_magic_scene_key_input_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - // Setup view - ByteInput* byte_input = nfc_magic->byte_input; - byte_input_set_header_text(byte_input, "Enter the password in hex"); - byte_input_set_result_callback( - byte_input, - nfc_magic_scene_key_input_byte_input_callback, - NULL, - nfc_magic, - (uint8_t*)&nfc_magic->dev->password, - 4); - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput); -} - -bool nfc_magic_scene_key_input_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcMagicCustomEventByteInputDone) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck); - consumed = true; - } - } - return consumed; -} - -void nfc_magic_scene_key_input_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - // Clear view - byte_input_set_result_callback(nfc_magic->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc_magic->byte_input, ""); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c deleted file mode 100644 index c147ac438..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "../nfc_magic_i.h" -#include "../lib/magic/types.h" - -void nfc_magic_scene_magic_info_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcMagic* nfc_magic = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); - } -} - -void nfc_magic_scene_magic_info_on_enter(void* context) { - NfcMagic* nfc_magic = context; - Widget* widget = nfc_magic->widget; - const char* card_type = nfc_magic_type(nfc_magic->dev->type); - - notification_message(nfc_magic->notifications, &sequence_success); - - widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); - widget_add_string_element( - widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Magic card detected"); - widget_add_string_element(widget, 3, 17, AlignLeft, AlignTop, FontSecondary, card_type); - widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_magic_info_widget_callback, nfc_magic); - widget_add_button_element( - widget, GuiButtonTypeRight, "More", nfc_magic_scene_magic_info_widget_callback, nfc_magic); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); -} - -bool nfc_magic_scene_magic_info_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(nfc_magic->scene_manager); - } else if(event.event == GuiButtonTypeRight) { - MagicType type = nfc_magic->dev->type; - if(type == MagicTypeGen4) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneGen4Actions); - consumed = true; - } else { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneActions); - consumed = true; - } - } - } - return consumed; -} - -void nfc_magic_scene_magic_info_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - widget_reset(nfc_magic->widget); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_new_key_input.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_new_key_input.c deleted file mode 100644 index b5247f6c5..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_new_key_input.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../nfc_magic_i.h" - -void nfc_magic_scene_new_key_input_byte_input_callback(void* context) { - NfcMagic* nfc_magic = context; - - view_dispatcher_send_custom_event( - nfc_magic->view_dispatcher, NfcMagicCustomEventByteInputDone); -} - -void nfc_magic_scene_new_key_input_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - // Setup view - ByteInput* byte_input = nfc_magic->byte_input; - byte_input_set_header_text(byte_input, "Enter the password in hex"); - byte_input_set_result_callback( - byte_input, - nfc_magic_scene_new_key_input_byte_input_callback, - NULL, - nfc_magic, - (uint8_t*)&nfc_magic->new_password, - 4); - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewByteInput); -} - -bool nfc_magic_scene_new_key_input_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcMagicCustomEventByteInputDone) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneRekey); - consumed = true; - } - } - return consumed; -} - -void nfc_magic_scene_new_key_input_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - // Clear view - byte_input_set_result_callback(nfc_magic->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(nfc_magic->byte_input, ""); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c deleted file mode 100644 index b4f579f44..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "../nfc_magic_i.h" - -void nfc_magic_scene_not_magic_widget_callback(GuiButtonType result, InputType type, void* context) { - NfcMagic* nfc_magic = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); - } -} - -void nfc_magic_scene_not_magic_on_enter(void* context) { - NfcMagic* nfc_magic = context; - Widget* widget = nfc_magic->widget; - - notification_message(nfc_magic->notifications, &sequence_error); - - widget_add_string_element( - widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); - widget_add_string_multiline_element( - widget, 4, 17, AlignLeft, AlignTop, FontSecondary, "Not magic or unsupported\ncard"); - widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_not_magic_widget_callback, nfc_magic); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); -} - -bool nfc_magic_scene_not_magic_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(nfc_magic->scene_manager); - } - } - return consumed; -} - -void nfc_magic_scene_not_magic_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - widget_reset(nfc_magic->widget); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey.c deleted file mode 100644 index 259dc78ea..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "../nfc_magic_i.h" - -enum { - NfcMagicSceneRekeyStateCardSearch, - NfcMagicSceneRekeyStateCardFound, -}; - -bool nfc_magic_rekey_worker_callback(NfcMagicWorkerEvent event, void* context) { - furi_assert(context); - - NfcMagic* nfc_magic = context; - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event); - - return true; -} - -static void nfc_magic_scene_rekey_setup_view(NfcMagic* nfc_magic) { - Popup* popup = nfc_magic->popup; - popup_reset(popup); - uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneRekey); - - if(state == NfcMagicSceneRekeyStateCardSearch) { - popup_set_text( - nfc_magic->popup, - "Apply the\nsame card\nto the back", - 128, - 32, - AlignRight, - AlignCenter); - popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); - } else { - popup_set_icon(popup, 12, 23, &I_Loading_24); - popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); - } - - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); -} - -void nfc_magic_scene_rekey_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch); - nfc_magic_scene_rekey_setup_view(nfc_magic); - - // Setup and start worker - nfc_magic_worker_start( - nfc_magic->worker, - NfcMagicWorkerStateRekey, - nfc_magic->dev, - &nfc_magic->source_dev->dev_data, - nfc_magic->new_password, - nfc_magic_rekey_worker_callback, - nfc_magic); - nfc_magic_blink_start(nfc_magic); -} - -bool nfc_magic_scene_rekey_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcMagicWorkerEventSuccess) { - nfc_magic->dev->password = nfc_magic->new_password; - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess); - consumed = true; - } else if(event.event == NfcMagicWorkerEventFail) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneRekeyFail); - consumed = true; - } else if(event.event == NfcMagicWorkerEventCardDetected) { - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardFound); - nfc_magic_scene_rekey_setup_view(nfc_magic); - consumed = true; - } else if(event.event == NfcMagicWorkerEventNoCardDetected) { - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch); - nfc_magic_scene_rekey_setup_view(nfc_magic); - consumed = true; - } - } - return consumed; -} - -void nfc_magic_scene_rekey_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - nfc_magic_worker_stop(nfc_magic->worker); - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneRekey, NfcMagicSceneRekeyStateCardSearch); - // Clear view - popup_reset(nfc_magic->popup); - - nfc_magic_blink_stop(nfc_magic); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey_fail.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey_fail.c deleted file mode 100644 index d30ee57bc..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_rekey_fail.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "../nfc_magic_i.h" - -void nfc_magic_scene_rekey_fail_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcMagic* nfc_magic = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); - } -} - -void nfc_magic_scene_rekey_fail_on_enter(void* context) { - NfcMagic* nfc_magic = context; - Widget* widget = nfc_magic->widget; - - notification_message(nfc_magic->notifications, &sequence_error); - - widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); - widget_add_string_element( - widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Can't change password!"); - - widget_add_button_element( - widget, GuiButtonTypeLeft, "Finish", nfc_magic_scene_rekey_fail_widget_callback, nfc_magic); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); -} - -bool nfc_magic_scene_rekey_fail_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc_magic->scene_manager, NfcMagicSceneStart); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc_magic->scene_manager, NfcMagicSceneStart); - } - return consumed; -} - -void nfc_magic_scene_rekey_fail_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - widget_reset(nfc_magic->widget); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_start.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_start.c deleted file mode 100644 index b5861629e..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_start.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "../nfc_magic_i.h" -enum SubmenuIndex { - SubmenuIndexCheck, - SubmenuIndexAuthenticateGen4, -}; - -void nfc_magic_scene_start_submenu_callback(void* context, uint32_t index) { - NfcMagic* nfc_magic = context; - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, index); -} - -void nfc_magic_scene_start_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - Submenu* submenu = nfc_magic->submenu; - submenu_add_item( - submenu, - "Check Magic Tag", - SubmenuIndexCheck, - nfc_magic_scene_start_submenu_callback, - nfc_magic); - submenu_add_item( - submenu, - "Authenticate Gen4", - SubmenuIndexAuthenticateGen4, - nfc_magic_scene_start_submenu_callback, - nfc_magic); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart)); - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewMenu); -} - -bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexCheck) { - nfc_magic->dev->password = MAGIC_GEN4_DEFAULT_PWD; - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexCheck); - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck); - consumed = true; - } else if(event.event == SubmenuIndexAuthenticateGen4) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneKeyInput); - } - } - - return consumed; -} - -void nfc_magic_scene_start_on_exit(void* context) { - NfcMagic* nfc_magic = context; - submenu_reset(nfc_magic->submenu); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_success.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_success.c deleted file mode 100644 index 37441e80e..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_success.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "../nfc_magic_i.h" - -void nfc_magic_scene_success_popup_callback(void* context) { - NfcMagic* nfc_magic = context; - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, NfcMagicCustomEventViewExit); -} - -void nfc_magic_scene_success_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - notification_message(nfc_magic->notifications, &sequence_success); - - Popup* popup = nfc_magic->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Success!", 10, 20, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, nfc_magic); - popup_set_callback(popup, nfc_magic_scene_success_popup_callback); - popup_enable_timeout(popup); - - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); -} - -bool nfc_magic_scene_success_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcMagicCustomEventViewExit) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc_magic->scene_manager, NfcMagicSceneStart); - } - } - return consumed; -} - -void nfc_magic_scene_success_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - // Clear view - popup_reset(nfc_magic->popup); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c deleted file mode 100644 index 29640f89c..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c +++ /dev/null @@ -1,97 +0,0 @@ -#include "../nfc_magic_i.h" - -enum { - NfcMagicSceneWipeStateCardSearch, - NfcMagicSceneWipeStateCardFound, -}; - -bool nfc_magic_wipe_worker_callback(NfcMagicWorkerEvent event, void* context) { - furi_assert(context); - - NfcMagic* nfc_magic = context; - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event); - - return true; -} - -static void nfc_magic_scene_wipe_setup_view(NfcMagic* nfc_magic) { - Popup* popup = nfc_magic->popup; - popup_reset(popup); - uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneWipe); - - if(state == NfcMagicSceneWipeStateCardSearch) { - popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); - popup_set_text( - nfc_magic->popup, - "Apply the\nsame card\nto the back", - 128, - 32, - AlignRight, - AlignCenter); - } else { - popup_set_icon(popup, 12, 23, &I_Loading_24); - popup_set_header(popup, "Wiping\nDon't move...", 52, 32, AlignLeft, AlignCenter); - } - - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); -} - -void nfc_magic_scene_wipe_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardSearch); - nfc_magic_scene_wipe_setup_view(nfc_magic); - - // Setup and start worker - nfc_magic_worker_start( - nfc_magic->worker, - NfcMagicWorkerStateWipe, - nfc_magic->dev, - &nfc_magic->source_dev->dev_data, - nfc_magic->new_password, - nfc_magic_wipe_worker_callback, - nfc_magic); - nfc_magic_blink_start(nfc_magic); -} - -bool nfc_magic_scene_wipe_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcMagicWorkerEventSuccess) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess); - consumed = true; - } else if(event.event == NfcMagicWorkerEventFail) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipeFail); - consumed = true; - } else if(event.event == NfcMagicWorkerEventWrongCard) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic); - consumed = true; - } else if(event.event == NfcMagicWorkerEventCardDetected) { - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardFound); - nfc_magic_scene_wipe_setup_view(nfc_magic); - consumed = true; - } else if(event.event == NfcMagicWorkerEventNoCardDetected) { - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardSearch); - nfc_magic_scene_wipe_setup_view(nfc_magic); - consumed = true; - } - } - return consumed; -} - -void nfc_magic_scene_wipe_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - nfc_magic_worker_stop(nfc_magic->worker); - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneWipe, NfcMagicSceneWipeStateCardSearch); - // Clear view - popup_reset(nfc_magic->popup); - - nfc_magic_blink_stop(nfc_magic); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c deleted file mode 100644 index 828b65e6c..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "../nfc_magic_i.h" - -void nfc_magic_scene_wipe_fail_widget_callback(GuiButtonType result, InputType type, void* context) { - NfcMagic* nfc_magic = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); - } -} - -void nfc_magic_scene_wipe_fail_on_enter(void* context) { - NfcMagic* nfc_magic = context; - Widget* widget = nfc_magic->widget; - - notification_message(nfc_magic->notifications, &sequence_error); - - widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); - widget_add_string_element(widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "Wipe failed"); - widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wipe_fail_widget_callback, nfc_magic); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); -} - -bool nfc_magic_scene_wipe_fail_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(nfc_magic->scene_manager); - } - } - return consumed; -} - -void nfc_magic_scene_wipe_fail_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - widget_reset(nfc_magic->widget); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_write.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write.c deleted file mode 100644 index 45c54557f..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_write.c +++ /dev/null @@ -1,97 +0,0 @@ -#include "../nfc_magic_i.h" - -enum { - NfcMagicSceneWriteStateCardSearch, - NfcMagicSceneWriteStateCardFound, -}; - -bool nfc_magic_write_worker_callback(NfcMagicWorkerEvent event, void* context) { - furi_assert(context); - - NfcMagic* nfc_magic = context; - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, event); - - return true; -} - -static void nfc_magic_scene_write_setup_view(NfcMagic* nfc_magic) { - Popup* popup = nfc_magic->popup; - popup_reset(popup); - uint32_t state = scene_manager_get_scene_state(nfc_magic->scene_manager, NfcMagicSceneWrite); - - if(state == NfcMagicSceneWriteStateCardSearch) { - popup_set_text( - nfc_magic->popup, - "Apply the\nsame card\nto the back", - 128, - 32, - AlignRight, - AlignCenter); - popup_set_icon(nfc_magic->popup, 0, 8, &I_NFC_manual_60x50); - } else { - popup_set_icon(popup, 12, 23, &I_Loading_24); - popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter); - } - - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewPopup); -} - -void nfc_magic_scene_write_on_enter(void* context) { - NfcMagic* nfc_magic = context; - - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardSearch); - nfc_magic_scene_write_setup_view(nfc_magic); - - // Setup and start worker - nfc_magic_worker_start( - nfc_magic->worker, - NfcMagicWorkerStateWrite, - nfc_magic->dev, - &nfc_magic->source_dev->dev_data, - nfc_magic->new_password, - nfc_magic_write_worker_callback, - nfc_magic); - nfc_magic_blink_start(nfc_magic); -} - -bool nfc_magic_scene_write_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcMagicWorkerEventSuccess) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneSuccess); - consumed = true; - } else if(event.event == NfcMagicWorkerEventFail) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWriteFail); - consumed = true; - } else if(event.event == NfcMagicWorkerEventWrongCard) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneNotMagic); - consumed = true; - } else if(event.event == NfcMagicWorkerEventCardDetected) { - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardFound); - nfc_magic_scene_write_setup_view(nfc_magic); - consumed = true; - } else if(event.event == NfcMagicWorkerEventNoCardDetected) { - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardSearch); - nfc_magic_scene_write_setup_view(nfc_magic); - consumed = true; - } - } - return consumed; -} - -void nfc_magic_scene_write_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - nfc_magic_worker_stop(nfc_magic->worker); - scene_manager_set_scene_state( - nfc_magic->scene_manager, NfcMagicSceneWrite, NfcMagicSceneWriteStateCardSearch); - // Clear view - popup_reset(nfc_magic->popup); - - nfc_magic_blink_stop(nfc_magic); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_write_confirm.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write_confirm.c deleted file mode 100644 index d31c1c194..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_write_confirm.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "../nfc_magic_i.h" - -void nfc_magic_scene_write_confirm_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcMagic* nfc_magic = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); - } -} - -void nfc_magic_scene_write_confirm_on_enter(void* context) { - NfcMagic* nfc_magic = context; - Widget* widget = nfc_magic->widget; - - widget_add_string_element(widget, 3, 0, AlignLeft, AlignTop, FontPrimary, "Risky operation"); - widget_add_text_box_element( - widget, - 0, - 13, - 128, - 54, - AlignLeft, - AlignTop, - "Writing to this card will change manufacturer block. On some cards it may not be rewritten", - false); - widget_add_button_element( - widget, - GuiButtonTypeCenter, - "Continue", - nfc_magic_scene_write_confirm_widget_callback, - nfc_magic); - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Back", - nfc_magic_scene_write_confirm_widget_callback, - nfc_magic); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); -} - -bool nfc_magic_scene_write_confirm_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(nfc_magic->scene_manager); - } else if(event.event == GuiButtonTypeCenter) { - scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWrite); - consumed = true; - } - } - return consumed; -} - -void nfc_magic_scene_write_confirm_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - widget_reset(nfc_magic->widget); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_write_fail.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_write_fail.c deleted file mode 100644 index 8a465bf61..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_write_fail.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../nfc_magic_i.h" - -void nfc_magic_scene_write_fail_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcMagic* nfc_magic = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); - } -} - -void nfc_magic_scene_write_fail_on_enter(void* context) { - NfcMagic* nfc_magic = context; - Widget* widget = nfc_magic->widget; - - notification_message(nfc_magic->notifications, &sequence_error); - - widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48); - widget_add_string_element( - widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!"); - widget_add_string_multiline_element( - widget, - 7, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Not all sectors\nwere written\ncorrectly."); - - widget_add_button_element( - widget, GuiButtonTypeLeft, "Finish", nfc_magic_scene_write_fail_widget_callback, nfc_magic); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); -} - -bool nfc_magic_scene_write_fail_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc_magic->scene_manager, NfcMagicSceneStart); - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - nfc_magic->scene_manager, NfcMagicSceneStart); - } - return consumed; -} - -void nfc_magic_scene_write_fail_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - widget_reset(nfc_magic->widget); -} diff --git a/applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c b/applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c deleted file mode 100644 index 857d50c1f..000000000 --- a/applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "../nfc_magic_i.h" - -void nfc_magic_scene_wrong_card_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcMagic* nfc_magic = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc_magic->view_dispatcher, result); - } -} - -void nfc_magic_scene_wrong_card_on_enter(void* context) { - NfcMagic* nfc_magic = context; - Widget* widget = nfc_magic->widget; - - notification_message(nfc_magic->notifications, &sequence_error); - - widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48); - widget_add_string_element( - widget, 1, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card"); - widget_add_string_multiline_element( - widget, - 1, - 17, - AlignLeft, - AlignTop, - FontSecondary, - "Writing this file is\nnot supported for\nthis magic card."); - widget_add_button_element( - widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic); - - // Setup and start worker - view_dispatcher_switch_to_view(nfc_magic->view_dispatcher, NfcMagicViewWidget); -} - -bool nfc_magic_scene_wrong_card_on_event(void* context, SceneManagerEvent event) { - NfcMagic* nfc_magic = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(nfc_magic->scene_manager); - } - } - return consumed; -} - -void nfc_magic_scene_wrong_card_on_exit(void* context) { - NfcMagic* nfc_magic = context; - - widget_reset(nfc_magic->widget); -} diff --git a/applications/external/nfc_rfid_detector/application.fam b/applications/external/nfc_rfid_detector/application.fam deleted file mode 100644 index 70c91bc84..000000000 --- a/applications/external/nfc_rfid_detector/application.fam +++ /dev/null @@ -1,13 +0,0 @@ -App( - appid="nfc_rfid_detector", - name="NFC/RFID detector", - apptype=FlipperAppType.EXTERNAL, - targets=["f7"], - entry_point="nfc_rfid_detector_app", - requires=["gui"], - stack_size=4 * 1024, - order=50, - fap_icon="nfc_rfid_detector_10px.png", - fap_category="Tools", - fap_icon_assets="images", -) diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h deleted file mode 100644 index bbffe2938..000000000 --- a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -typedef enum { - //NfcRfidDetectorCustomEvent - NfcRfidDetectorCustomEventStartId = 100, - -} NfcRfidDetectorCustomEvent; diff --git a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h b/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h deleted file mode 100644 index 5d44b09b7..000000000 --- a/applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -#define NFC_RFID_DETECTOR_VERSION_APP "0.1" -#define NFC_RFID_DETECTOR_DEVELOPED "SkorP" -#define NFC_RFID_DETECTOR_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" - -typedef enum { - NfcRfidDetectorViewVariableItemList, - NfcRfidDetectorViewSubmenu, - NfcRfidDetectorViewFieldPresence, - NfcRfidDetectorViewWidget, -} NfcRfidDetectorView; diff --git a/applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png b/applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png deleted file mode 100644 index b19c0f30c9f3928d3129acc9da92d5a9e962d084..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3670 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 diff --git a/applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png b/applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png deleted file mode 100644 index ff4af9ff05989e5ff04d3888971b7f7801c59ed4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3698 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 QfL1Vgy85}Sb4q9e0PSTwjsO4v diff --git a/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png b/applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png deleted file mode 100644 index 35c205049bc5a6c7f0bb13a0cb3057963a023c6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^xG+l41V{G`^6RB9^&ce?Q{EhH1o(i zcG0sF=jLrIU`gfJ9e<(w?c#$+`{GybuV0XK^{NaL55t;h1_!r?84rQRGkCiCxvXf4IeWS|hDc0ZJK-P~g93;1_80%_ zf0mxq_>?eVL9oQDG>54hmUMGg2<9ZE<@p-^(r|y*a$w%vQ*}A2s_`AWqh21A3+I&= XdhU3CV!O&Kpm7YIu6{1-oD!M<1Jf%z diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c deleted file mode 100644 index cba8b6085..000000000 --- a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c +++ /dev/null @@ -1,108 +0,0 @@ -#include "nfc_rfid_detector_app_i.h" - -#include -#include - -static bool nfc_rfid_detector_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - NfcRfidDetectorApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool nfc_rfid_detector_app_back_event_callback(void* context) { - furi_assert(context); - NfcRfidDetectorApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void nfc_rfid_detector_app_tick_event_callback(void* context) { - furi_assert(context); - NfcRfidDetectorApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -NfcRfidDetectorApp* nfc_rfid_detector_app_alloc() { - NfcRfidDetectorApp* app = malloc(sizeof(NfcRfidDetectorApp)); - - // GUI - app->gui = furi_record_open(RECORD_GUI); - - // View Dispatcher - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&nfc_rfid_detector_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, nfc_rfid_detector_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, nfc_rfid_detector_app_back_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, nfc_rfid_detector_app_tick_event_callback, 100); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - // Open Notification record - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - // SubMenu - app->submenu = submenu_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, NfcRfidDetectorViewSubmenu, submenu_get_view(app->submenu)); - - // Widget - app->widget = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, NfcRfidDetectorViewWidget, widget_get_view(app->widget)); - - // Field Presence - app->nfc_rfid_detector_field_presence = nfc_rfid_detector_view_field_presence_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - NfcRfidDetectorViewFieldPresence, - nfc_rfid_detector_view_field_presence_get_view(app->nfc_rfid_detector_field_presence)); - - scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneStart); - - return app; -} - -void nfc_rfid_detector_app_free(NfcRfidDetectorApp* app) { - furi_assert(app); - - // Submenu - view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); - submenu_free(app->submenu); - - // Widget - view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewWidget); - widget_free(app->widget); - - // Field Presence - view_dispatcher_remove_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); - nfc_rfid_detector_view_field_presence_free(app->nfc_rfid_detector_field_presence); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Notifications - furi_record_close(RECORD_NOTIFICATION); - app->notifications = NULL; - - // Close records - furi_record_close(RECORD_GUI); - - free(app); -} - -int32_t nfc_rfid_detector_app(void* p) { - UNUSED(p); - NfcRfidDetectorApp* nfc_rfid_detector_app = nfc_rfid_detector_app_alloc(); - - view_dispatcher_run(nfc_rfid_detector_app->view_dispatcher); - - nfc_rfid_detector_app_free(nfc_rfid_detector_app); - - return 0; -} diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c deleted file mode 100644 index c59d40d50..000000000 --- a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "nfc_rfid_detector_app_i.h" - -#include - -#define TAG "NfcRfidDetector" - -void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app) { - furi_assert(app); - - // start the field presence rfid detection - furi_hal_rfid_field_detect_start(); - - // start the field presence nfc detection - furi_hal_nfc_exit_sleep(); - furi_hal_nfc_field_detect_start(); -} - -void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app) { - furi_assert(app); - - // stop the field presence rfid detection - furi_hal_rfid_field_detect_stop(); - - // stop the field presence nfc detection - furi_hal_nfc_start_sleep(); -} - -bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app) { - furi_assert(app); - - // check if the field presence is nfc - return furi_hal_nfc_field_is_present(); -} - -bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency) { - furi_assert(app); - - // check if the field presence is rfid - return furi_hal_rfid_field_is_present(frequency); -} \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h b/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h deleted file mode 100644 index 72cb126d4..000000000 --- a/applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "helpers/nfc_rfid_detector_types.h" -#include "helpers/nfc_rfid_detector_event.h" - -#include "scenes/nfc_rfid_detector_scene.h" -#include -#include -#include -#include -#include -#include -#include "views/nfc_rfid_detector_view_field_presence.h" - -typedef struct NfcRfidDetectorApp NfcRfidDetectorApp; - -struct NfcRfidDetectorApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - Submenu* submenu; - Widget* widget; - NfcRfidDetectorFieldPresence* nfc_rfid_detector_field_presence; -}; - -void nfc_rfid_detector_app_field_presence_start(NfcRfidDetectorApp* app); -void nfc_rfid_detector_app_field_presence_stop(NfcRfidDetectorApp* app); -bool nfc_rfid_detector_app_field_presence_is_nfc(NfcRfidDetectorApp* app); -bool nfc_rfid_detector_app_field_presence_is_rfid(NfcRfidDetectorApp* app, uint32_t* frequency); \ No newline at end of file diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c deleted file mode 100644 index d75eb2884..000000000 --- a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "../nfc_rfid_detector_app_i.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const nfc_rfid_detector_scene_on_enter_handlers[])(void*) = { -#include "nfc_rfid_detector_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const nfc_rfid_detector_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = - { -#include "nfc_rfid_detector_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const nfc_rfid_detector_scene_on_exit_handlers[])(void* context) = { -#include "nfc_rfid_detector_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers nfc_rfid_detector_scene_handlers = { - .on_enter_handlers = nfc_rfid_detector_scene_on_enter_handlers, - .on_event_handlers = nfc_rfid_detector_scene_on_event_handlers, - .on_exit_handlers = nfc_rfid_detector_scene_on_exit_handlers, - .scene_num = NfcRfidDetectorSceneNum, -}; diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h deleted file mode 100644 index 74d324b4d..000000000 --- a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) NfcRfidDetectorScene##id, -typedef enum { -#include "nfc_rfid_detector_scene_config.h" - NfcRfidDetectorSceneNum, -} NfcRfidDetectorScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers nfc_rfid_detector_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "nfc_rfid_detector_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "nfc_rfid_detector_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "nfc_rfid_detector_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c deleted file mode 100644 index ddcb8aac0..000000000 --- a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "../nfc_rfid_detector_app_i.h" - -void nfc_rfid_detector_scene_about_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - NfcRfidDetectorApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -void nfc_rfid_detector_scene_about_on_enter(void* context) { - NfcRfidDetectorApp* app = context; - - FuriString* temp_str; - temp_str = furi_string_alloc(); - furi_string_printf(temp_str, "\e#%s\n", "Information"); - - furi_string_cat_printf(temp_str, "Version: %s\n", NFC_RFID_DETECTOR_VERSION_APP); - furi_string_cat_printf(temp_str, "Developed by: %s\n", NFC_RFID_DETECTOR_DEVELOPED); - furi_string_cat_printf(temp_str, "Github: %s\n\n", NFC_RFID_DETECTOR_GITHUB); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); - furi_string_cat_printf( - temp_str, - "This application allows\nyou to determine what\ntype of electromagnetic\nfield the reader is using.\nFor LF RFID you can also\nsee the carrier frequency\n\n"); - - widget_add_text_box_element( - app->widget, - 0, - 0, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! \e!\n", - false); - widget_add_text_box_element( - app->widget, - 0, - 2, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! NFC/RFID detector \e!\n", - false); - widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); - furi_string_free(temp_str); - - view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewWidget); -} - -bool nfc_rfid_detector_scene_about_on_event(void* context, SceneManagerEvent event) { - NfcRfidDetectorApp* app = context; - bool consumed = false; - UNUSED(app); - UNUSED(event); - - return consumed; -} - -void nfc_rfid_detector_scene_about_on_exit(void* context) { - NfcRfidDetectorApp* app = context; - - // Clear views - widget_reset(app->widget); -} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h deleted file mode 100644 index ab49ad5c2..000000000 --- a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h +++ /dev/null @@ -1,3 +0,0 @@ -ADD_SCENE(nfc_rfid_detector, start, Start) -ADD_SCENE(nfc_rfid_detector, about, About) -ADD_SCENE(nfc_rfid_detector, field_presence, FieldPresence) diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c deleted file mode 100644 index ec53b5a0a..000000000 --- a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "../nfc_rfid_detector_app_i.h" -#include "../views/nfc_rfid_detector_view_field_presence.h" - -void nfc_rfid_detector_scene_field_presence_callback( - NfcRfidDetectorCustomEvent event, - void* context) { - furi_assert(context); - NfcRfidDetectorApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -static const NotificationSequence notification_app_display_on = { - - &message_display_backlight_on, - NULL, -}; - -static void nfc_rfid_detector_scene_field_presence_update(void* context) { - furi_assert(context); - NfcRfidDetectorApp* app = context; - - uint32_t frequency = 0; - bool nfc_field = nfc_rfid_detector_app_field_presence_is_nfc(app); - bool rfid_field = nfc_rfid_detector_app_field_presence_is_rfid(app, &frequency); - - if(nfc_field || rfid_field) - notification_message(app->notifications, ¬ification_app_display_on); - - nfc_rfid_detector_view_field_presence_update( - app->nfc_rfid_detector_field_presence, nfc_field, rfid_field, frequency); -} - -void nfc_rfid_detector_scene_field_presence_on_enter(void* context) { - furi_assert(context); - NfcRfidDetectorApp* app = context; - - // Start detection of field presence - nfc_rfid_detector_app_field_presence_start(app); - - view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewFieldPresence); -} - -bool nfc_rfid_detector_scene_field_presence_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - NfcRfidDetectorApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeTick) { - nfc_rfid_detector_scene_field_presence_update(app); - } - - return consumed; -} - -void nfc_rfid_detector_scene_field_presence_on_exit(void* context) { - furi_assert(context); - NfcRfidDetectorApp* app = context; - // Stop detection of field presence - nfc_rfid_detector_app_field_presence_stop(app); -} diff --git a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c b/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c deleted file mode 100644 index 7b71bd973..000000000 --- a/applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../nfc_rfid_detector_app_i.h" - -typedef enum { - SubmenuIndexNfcRfidDetectorFieldPresence, - SubmenuIndexNfcRfidDetectorAbout, -} SubmenuIndex; - -void nfc_rfid_detector_scene_start_submenu_callback(void* context, uint32_t index) { - NfcRfidDetectorApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void nfc_rfid_detector_scene_start_on_enter(void* context) { - UNUSED(context); - NfcRfidDetectorApp* app = context; - Submenu* submenu = app->submenu; - - submenu_add_item( - submenu, - "Detect field type", - SubmenuIndexNfcRfidDetectorFieldPresence, - nfc_rfid_detector_scene_start_submenu_callback, - app); - submenu_add_item( - submenu, - "About", - SubmenuIndexNfcRfidDetectorAbout, - nfc_rfid_detector_scene_start_submenu_callback, - app); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, NfcRfidDetectorSceneStart)); - - view_dispatcher_switch_to_view(app->view_dispatcher, NfcRfidDetectorViewSubmenu); -} - -bool nfc_rfid_detector_scene_start_on_event(void* context, SceneManagerEvent event) { - NfcRfidDetectorApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexNfcRfidDetectorAbout) { - scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneAbout); - consumed = true; - } else if(event.event == SubmenuIndexNfcRfidDetectorFieldPresence) { - scene_manager_next_scene(app->scene_manager, NfcRfidDetectorSceneFieldPresence); - consumed = true; - } - scene_manager_set_scene_state(app->scene_manager, NfcRfidDetectorSceneStart, event.event); - } - - return consumed; -} - -void nfc_rfid_detector_scene_start_on_exit(void* context) { - NfcRfidDetectorApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c deleted file mode 100644 index e65eb8362..000000000 --- a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c +++ /dev/null @@ -1,164 +0,0 @@ -#include "nfc_rfid_detector_view_field_presence.h" -#include "../nfc_rfid_detector_app_i.h" -#include - -#include -#include - -#define FIELD_FOUND_WEIGHT 5 - -typedef enum { - NfcRfidDetectorTypeFieldPresenceNfc, - NfcRfidDetectorTypeFieldPresenceRfid, -} NfcRfidDetectorTypeFieldPresence; - -static const Icon* NfcRfidDetectorFieldPresenceIcons[] = { - [NfcRfidDetectorTypeFieldPresenceNfc] = &I_NFC_detect_45x30, - [NfcRfidDetectorTypeFieldPresenceRfid] = &I_Rfid_detect_45x30, -}; - -struct NfcRfidDetectorFieldPresence { - View* view; -}; - -typedef struct { - uint8_t nfc_field; - uint8_t rfid_field; - uint32_t rfid_frequency; -} NfcRfidDetectorFieldPresenceModel; - -void nfc_rfid_detector_view_field_presence_update( - NfcRfidDetectorFieldPresence* instance, - bool nfc_field, - bool rfid_field, - uint32_t rfid_frequency) { - furi_assert(instance); - with_view_model( - instance->view, - NfcRfidDetectorFieldPresenceModel * model, - { - if(nfc_field) { - model->nfc_field = FIELD_FOUND_WEIGHT; - } else if(model->nfc_field) { - model->nfc_field--; - } - if(rfid_field) { - model->rfid_field = FIELD_FOUND_WEIGHT; - model->rfid_frequency = rfid_frequency; - } else if(model->rfid_field) { - model->rfid_field--; - } - }, - true); -} - -void nfc_rfid_detector_view_field_presence_draw( - Canvas* canvas, - NfcRfidDetectorFieldPresenceModel* model) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(!model->nfc_field && !model->rfid_field) { - canvas_draw_icon(canvas, 0, 16, &I_Modern_reader_18x34); - canvas_draw_icon(canvas, 22, 12, &I_Move_flipper_26x39); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 56, 36, "Touch the reader"); - } else { - if(model->nfc_field) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 21, 10, "NFC"); - canvas_draw_icon( - canvas, - 9, - 17, - NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceNfc]); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 9, 62, "13,56 MHz"); - } - - if(model->rfid_field) { - char str[16]; - snprintf(str, sizeof(str), "%.02f KHz", (double)model->rfid_frequency / 1000); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 76, 10, "LF RFID"); - canvas_draw_icon( - canvas, - 71, - 17, - NfcRfidDetectorFieldPresenceIcons[NfcRfidDetectorTypeFieldPresenceRfid]); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 69, 62, str); - } - } -} - -bool nfc_rfid_detector_view_field_presence_input(InputEvent* event, void* context) { - furi_assert(context); - NfcRfidDetectorFieldPresence* instance = context; - UNUSED(instance); - - if(event->key == InputKeyBack) { - return false; - } - - return true; -} - -void nfc_rfid_detector_view_field_presence_enter(void* context) { - furi_assert(context); - NfcRfidDetectorFieldPresence* instance = context; - with_view_model( - instance->view, - NfcRfidDetectorFieldPresenceModel * model, - { - model->nfc_field = 0; - model->rfid_field = 0; - model->rfid_frequency = 0; - }, - true); -} - -void nfc_rfid_detector_view_field_presence_exit(void* context) { - furi_assert(context); - NfcRfidDetectorFieldPresence* instance = context; - UNUSED(instance); -} - -NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc() { - NfcRfidDetectorFieldPresence* instance = malloc(sizeof(NfcRfidDetectorFieldPresence)); - - // View allocation and configuration - instance->view = view_alloc(); - - view_allocate_model( - instance->view, ViewModelTypeLocking, sizeof(NfcRfidDetectorFieldPresenceModel)); - view_set_context(instance->view, instance); - view_set_draw_callback( - instance->view, (ViewDrawCallback)nfc_rfid_detector_view_field_presence_draw); - view_set_input_callback(instance->view, nfc_rfid_detector_view_field_presence_input); - view_set_enter_callback(instance->view, nfc_rfid_detector_view_field_presence_enter); - view_set_exit_callback(instance->view, nfc_rfid_detector_view_field_presence_exit); - - with_view_model( - instance->view, - NfcRfidDetectorFieldPresenceModel * model, - { - model->nfc_field = 0; - model->rfid_field = 0; - model->rfid_frequency = 0; - }, - true); - return instance; -} - -void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance) { - furi_assert(instance); - return instance->view; -} diff --git a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h b/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h deleted file mode 100644 index 0ddb4e2cd..000000000 --- a/applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "../helpers/nfc_rfid_detector_types.h" -#include "../helpers/nfc_rfid_detector_event.h" - -typedef struct NfcRfidDetectorFieldPresence NfcRfidDetectorFieldPresence; - -void nfc_rfid_detector_view_field_presence_update( - NfcRfidDetectorFieldPresence* instance, - bool nfc_field, - bool rfid_field, - uint32_t rfid_frequency); - -NfcRfidDetectorFieldPresence* nfc_rfid_detector_view_field_presence_alloc(); - -void nfc_rfid_detector_view_field_presence_free(NfcRfidDetectorFieldPresence* instance); - -View* nfc_rfid_detector_view_field_presence_get_view(NfcRfidDetectorFieldPresence* instance); diff --git a/applications/external/picopass/125_10px.png b/applications/external/picopass/125_10px.png deleted file mode 100644 index ce01284a2c1f3eb413f581b84f1fb3f3a2a7223b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xkYHHq`AGmsv7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xBF)%c=FfjZA3N^f7U???UV0e|lz+g3lfkC`r&aOZkpafHrx4R1i<>&pI=m5)bWZjP>yH&963)5S4_<9hOs!iI -#include - -#define ICLASS_ELITE_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_elite_dict.txt") -#define ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt") -#define ICLASS_STANDARD_DICT_FLIPPER_NAME APP_DATA_PATH("assets/iclass_standard_dict.txt") - -#define TAG "IclassEliteDict" - -#define ICLASS_ELITE_KEY_LINE_LEN (17) -#define ICLASS_ELITE_KEY_LEN (8) - -struct IclassEliteDict { - Stream* stream; - uint32_t total_keys; -}; - -bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type) { - Storage* storage = furi_record_open(RECORD_STORAGE); - - bool dict_present = false; - if(dict_type == IclassEliteDictTypeFlipper) { - dict_present = - (storage_common_stat(storage, ICLASS_ELITE_DICT_FLIPPER_NAME, NULL) == FSE_OK); - } else if(dict_type == IclassEliteDictTypeUser) { - dict_present = (storage_common_stat(storage, ICLASS_ELITE_DICT_USER_NAME, NULL) == FSE_OK); - } else if(dict_type == IclassStandardDictTypeFlipper) { - dict_present = - (storage_common_stat(storage, ICLASS_STANDARD_DICT_FLIPPER_NAME, NULL) == FSE_OK); - } - - furi_record_close(RECORD_STORAGE); - - return dict_present; -} - -IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type) { - IclassEliteDict* dict = malloc(sizeof(IclassEliteDict)); - Storage* storage = furi_record_open(RECORD_STORAGE); - dict->stream = buffered_file_stream_alloc(storage); - FuriString* next_line = furi_string_alloc(); - - bool dict_loaded = false; - do { - if(dict_type == IclassEliteDictTypeFlipper) { - if(!buffered_file_stream_open( - dict->stream, ICLASS_ELITE_DICT_FLIPPER_NAME, FSAM_READ, FSOM_OPEN_EXISTING)) { - buffered_file_stream_close(dict->stream); - break; - } - } else if(dict_type == IclassEliteDictTypeUser) { - if(!buffered_file_stream_open( - dict->stream, ICLASS_ELITE_DICT_USER_NAME, FSAM_READ_WRITE, FSOM_OPEN_ALWAYS)) { - buffered_file_stream_close(dict->stream); - break; - } - } else if(dict_type == IclassStandardDictTypeFlipper) { - if(!buffered_file_stream_open( - dict->stream, - ICLASS_STANDARD_DICT_FLIPPER_NAME, - FSAM_READ, - FSOM_OPEN_EXISTING)) { - buffered_file_stream_close(dict->stream); - break; - } - } - - // Read total amount of keys - while(true) { //-V547 - if(!stream_read_line(dict->stream, next_line)) break; - if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; - dict->total_keys++; - } - furi_string_reset(next_line); - stream_rewind(dict->stream); - - dict_loaded = true; - FURI_LOG_I(TAG, "Loaded dictionary with %lu keys", dict->total_keys); - } while(false); - - if(!dict_loaded) { //-V547 - buffered_file_stream_close(dict->stream); - free(dict); - dict = NULL; - } - - furi_record_close(RECORD_STORAGE); - furi_string_free(next_line); - - return dict; -} - -void iclass_elite_dict_free(IclassEliteDict* dict) { - furi_assert(dict); - furi_assert(dict->stream); - - buffered_file_stream_close(dict->stream); - stream_free(dict->stream); - free(dict); -} - -uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict) { - furi_assert(dict); - - return dict->total_keys; -} - -bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key) { - furi_assert(dict); - furi_assert(dict->stream); - - uint8_t key_byte_tmp = 0; - FuriString* next_line = furi_string_alloc(); - - bool key_read = false; - *key = 0ULL; - while(!key_read) { - if(!stream_read_line(dict->stream, next_line)) break; - if(furi_string_get_char(next_line, 0) == '#') continue; - if(furi_string_size(next_line) != ICLASS_ELITE_KEY_LINE_LEN) continue; - for(uint8_t i = 0; i < ICLASS_ELITE_KEY_LEN * 2; i += 2) { - args_char_to_hex( - furi_string_get_char(next_line, i), - furi_string_get_char(next_line, i + 1), - &key_byte_tmp); - key[i / 2] = key_byte_tmp; - } - key_read = true; - } - - furi_string_free(next_line); - return key_read; -} - -bool iclass_elite_dict_rewind(IclassEliteDict* dict) { - furi_assert(dict); - furi_assert(dict->stream); - - return stream_rewind(dict->stream); -} - -bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key) { - furi_assert(dict); - furi_assert(dict->stream); - - FuriString* key_str = furi_string_alloc(); - for(size_t i = 0; i < 6; i++) { - furi_string_cat_printf(key_str, "%02X", key[i]); - } - furi_string_cat_printf(key_str, "\n"); - - bool key_added = false; - do { - if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break; - if(!stream_insert_string(dict->stream, key_str)) break; - key_added = true; - } while(false); - - furi_string_free(key_str); - return key_added; -} diff --git a/applications/external/picopass/helpers/iclass_elite_dict.h b/applications/external/picopass/helpers/iclass_elite_dict.h deleted file mode 100644 index 150cd1b76..000000000 --- a/applications/external/picopass/helpers/iclass_elite_dict.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -typedef enum { - IclassEliteDictTypeUser, - IclassEliteDictTypeFlipper, - IclassStandardDictTypeFlipper, -} IclassEliteDictType; - -typedef struct IclassEliteDict IclassEliteDict; - -bool iclass_elite_dict_check_presence(IclassEliteDictType dict_type); - -IclassEliteDict* iclass_elite_dict_alloc(IclassEliteDictType dict_type); - -void iclass_elite_dict_free(IclassEliteDict* dict); - -uint32_t iclass_elite_dict_get_total_keys(IclassEliteDict* dict); - -bool iclass_elite_dict_get_next_key(IclassEliteDict* dict, uint8_t* key); - -bool iclass_elite_dict_rewind(IclassEliteDict* dict); - -bool iclass_elite_dict_add_key(IclassEliteDict* dict, uint8_t* key); diff --git a/applications/external/picopass/icons/DolphinMafia_115x62.png b/applications/external/picopass/icons/DolphinMafia_115x62.png deleted file mode 100644 index 66fdb40ff2651916faed4a2ae1d564cafdbf7bcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj diff --git a/applications/external/picopass/icons/DolphinNice_96x59.png b/applications/external/picopass/icons/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Q<>&pI=m5)b(dHL6nbwD9yPZ!4!j_b)k${QZ;XFmLn zjqNsD+YPq1J8W%_*aXBie!OR3*tC!PwU_7Q9H4U564!{5l*E!$tK_0oAjM#0U}UIk zV5)0q5@Kj%Wo%$&Y@uynU}a#izM}XGiiX_$l+3hBs0L%8o)7~QD^p9LQiz6svOM}g O4Gf;HelF{r5}E+GUQp8j diff --git a/applications/external/picopass/icons/RFIDDolphinReceive_97x61.png b/applications/external/picopass/icons/RFIDDolphinReceive_97x61.png deleted file mode 100644 index e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ diff --git a/applications/external/picopass/icons/RFIDDolphinSend_97x61.png b/applications/external/picopass/icons/RFIDDolphinSend_97x61.png deleted file mode 100644 index 380a970d9004cba5520560fd9aa24aa42924e2a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ diff --git a/applications/external/picopass/lib/loclass/optimized_cipher.c b/applications/external/picopass/lib/loclass/optimized_cipher.c deleted file mode 100644 index 94df07bae..000000000 --- a/applications/external/picopass/lib/loclass/optimized_cipher.c +++ /dev/null @@ -1,299 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -/* - This file contains an optimized version of the MAC-calculation algorithm. Some measurements on - a std laptop showed it runs in about 1/3 of the time: - - Std: 0.428962 - Opt: 0.151609 - - Additionally, it is self-reliant, not requiring e.g. bitstreams from the cipherutils, thus can - be easily dropped into a code base. - - The optimizations have been performed in the following steps: - * Parameters passed by reference instead of by value. - * Iteration instead of recursion, un-nesting recursive loops into for-loops. - * Handling of bytes instead of individual bits, for less shuffling and masking - * Less creation of "objects", structs, and instead reuse of alloc:ed memory - * Inlining some functions via #define:s - - As a consequence, this implementation is less generic. Also, I haven't bothered documenting this. - For a thorough documentation, check out the MAC-calculation within cipher.c instead. - - -- MHS 2015 -**/ - -/** - - The runtime of opt_doTagMAC_2() with the MHS optimized version was 403 microseconds on Proxmark3. - This was still to slow for some newer readers which didn't want to wait that long. - - Further optimizations to speedup the MAC calculations: - * Optimized opt_Tt logic - * Look up table for opt_select - * Removing many unnecessary bit maskings (& 0x1) - * updating state in place instead of alternating use of a second state structure - * remove the necessity to reverse bits of input and output bytes - - opt_doTagMAC_2() now completes in 270 microseconds. - - -- piwi 2019 -**/ - -/** - add the possibility to do iCLASS on device only - -- iceman 2020 -**/ - -#include "optimized_cipher.h" -#include "optimized_elite.h" -#include "optimized_ikeys.h" -#include "optimized_cipherutils.h" - -static const uint8_t loclass_opt_select_LUT[256] = { - 00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04, 01, 02, 03, 00, 02, 03, 00, 01, - 05, 06, 06, 05, 06, 07, 05, 04, 06, 05, 04, 07, 04, 05, 06, 07, 06, 05, 05, 06, 04, 05, 07, 06, - 07, 04, 05, 06, 04, 05, 06, 07, 07, 04, 04, 07, 04, 05, 07, 06, 06, 05, 04, 07, 04, 05, 06, 07, - 02, 01, 01, 02, 00, 01, 03, 02, 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, - 00, 03, 02, 01, 02, 03, 00, 01, 00, 03, 03, 00, 02, 03, 01, 00, 05, 06, 07, 04, 06, 07, 04, 05, - 05, 06, 06, 05, 06, 07, 05, 04, 02, 01, 00, 03, 00, 01, 02, 03, 06, 05, 05, 06, 04, 05, 07, 06, - 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, 02, 01, 00, 03, 00, 01, 02, 03, - 02, 01, 01, 02, 00, 01, 03, 02, 03, 00, 01, 02, 00, 01, 02, 03, 03, 00, 00, 03, 00, 01, 03, 02, - 04, 07, 06, 05, 06, 07, 04, 05, 00, 03, 03, 00, 02, 03, 01, 00, 01, 02, 03, 00, 02, 03, 00, 01, - 05, 06, 06, 05, 06, 07, 05, 04, 04, 07, 06, 05, 06, 07, 04, 05, 04, 07, 07, 04, 06, 07, 05, 04, - 01, 02, 03, 00, 02, 03, 00, 01, 01, 02, 02, 01, 02, 03, 01, 00}; - -/********************** the table above has been generated with this code: ******** -#include "util.h" -static void init_opt_select_LUT(void) { - for (int r = 0; r < 256; r++) { - uint8_t r_ls2 = r << 2; - uint8_t r_and_ls2 = r & r_ls2; - uint8_t r_or_ls2 = r | r_ls2; - uint8_t z0 = (r_and_ls2 >> 5) ^ ((r & ~r_ls2) >> 4) ^ ( r_or_ls2 >> 3); - uint8_t z1 = (r_or_ls2 >> 6) ^ ( r_or_ls2 >> 1) ^ (r >> 5) ^ r; - uint8_t z2 = ((r & ~r_ls2) >> 4) ^ (r_and_ls2 >> 3) ^ r; - loclass_opt_select_LUT[r] = (z0 & 4) | (z1 & 2) | (z2 & 1); - } - print_result("", loclass_opt_select_LUT, 256); -} -***********************************************************************************/ - -#define loclass_opt__select(x, y, r) \ - (4 & ((((r) & ((r) << 2)) >> 5) ^ (((r) & ~((r) << 2)) >> 4) ^ (((r) | (r) << 2) >> 3))) | \ - (2 & ((((r) | (r) << 2) >> 6) ^ (((r) | (r) << 2) >> 1) ^ ((r) >> 5) ^ (r) ^ (((x) ^ (y)) << 1))) | \ - (1 & ((((r) & ~((r) << 2)) >> 4) ^ (((r) & ((r) << 2)) >> 3) ^ (r) ^ (x))) - -static void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) { - uint16_t Tt = s->t & 0xc533; - Tt = Tt ^ (Tt >> 1); - Tt = Tt ^ (Tt >> 4); - Tt = Tt ^ (Tt >> 10); - Tt = Tt ^ (Tt >> 8); - - s->t = (s->t >> 1); - s->t |= (Tt ^ (s->r >> 7) ^ (s->r >> 3)) << 15; - - uint8_t opt_B = s->b; - opt_B ^= s->b >> 6; - opt_B ^= s->b >> 5; - opt_B ^= s->b >> 4; - - s->b = s->b >> 1; - s->b |= (opt_B ^ s->r) << 7; - - uint8_t opt_select = loclass_opt_select_LUT[s->r] & 0x04; - opt_select |= (loclass_opt_select_LUT[s->r] ^ ((Tt ^ y) << 1)) & 0x02; - opt_select |= (loclass_opt_select_LUT[s->r] ^ Tt) & 0x01; - - uint8_t r = s->r; - s->r = (k[opt_select] ^ s->b) + s->l; - s->l = s->r + r; -} - -static void loclass_opt_suc( - const uint8_t* k, - LoclassState_t* s, - const uint8_t* in, - uint8_t length, - bool add32Zeroes) { - for(int i = 0; i < length; i++) { - uint8_t head = in[i]; - for(int j = 0; j < 8; j++) { - loclass_opt_successor(k, s, head); - head >>= 1; - } - } - //For tag MAC, an additional 32 zeroes - if(add32Zeroes) { - for(int i = 0; i < 16; i++) { - loclass_opt_successor(k, s, 0); - loclass_opt_successor(k, s, 0); - } - } -} - -static void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) { - for(uint8_t times = 0; times < 4; times++) { - uint8_t bout = 0; - bout |= (s->r & 0x4) >> 2; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) >> 1; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4); - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 1; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 2; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 3; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 4; - loclass_opt_successor(k, s, 0); - bout |= (s->r & 0x4) << 5; - loclass_opt_successor(k, s, 0); - buffer[times] = bout; - } -} - -static void loclass_opt_MAC(uint8_t* k, uint8_t* input, uint8_t* out) { - LoclassState_t _init = { - ((k[0] ^ 0x4c) + 0xEC) & 0xFF, // l - ((k[0] ^ 0x4c) + 0x21) & 0xFF, // r - 0x4c, // b - 0xE012 // t - }; - - loclass_opt_suc(k, &_init, input, 12, false); - loclass_opt_output(k, &_init, out); -} - -static void loclass_opt_MAC_N(uint8_t* k, uint8_t* input, uint8_t in_size, uint8_t* out) { - LoclassState_t _init = { - ((k[0] ^ 0x4c) + 0xEC) & 0xFF, // l - ((k[0] ^ 0x4c) + 0x21) & 0xFF, // r - 0x4c, // b - 0xE012 // t - }; - - loclass_opt_suc(k, &_init, input, in_size, false); - loclass_opt_output(k, &_init, out); -} - -void loclass_opt_doReaderMAC(uint8_t* cc_nr_p, uint8_t* div_key_p, uint8_t mac[4]) { - uint8_t dest[] = {0, 0, 0, 0, 0, 0, 0, 0}; - loclass_opt_MAC(div_key_p, cc_nr_p, dest); - memcpy(mac, dest, 4); -} - -void loclass_opt_doReaderMAC_2( - LoclassState_t _init, - uint8_t* nr, - uint8_t mac[4], - const uint8_t* div_key_p) { - loclass_opt_suc(div_key_p, &_init, nr, 4, false); - loclass_opt_output(div_key_p, &_init, mac); -} - -void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]) { - uint8_t dest[] = {0, 0, 0, 0, 0, 0, 0, 0}; - loclass_opt_MAC_N(div_key_p, in_p, in_size, dest); - memcpy(mac, dest, 4); -} - -void loclass_opt_doTagMAC(uint8_t* cc_p, const uint8_t* div_key_p, uint8_t mac[4]) { - LoclassState_t _init = { - ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF, // l - ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF, // r - 0x4c, // b - 0xE012 // t - }; - loclass_opt_suc(div_key_p, &_init, cc_p, 12, true); - loclass_opt_output(div_key_p, &_init, mac); -} - -/** - * The tag MAC can be divided (both can, but no point in dividing the reader mac) into - * two functions, since the first 8 bytes are known, we can pre-calculate the state - * reached after feeding CC to the cipher. - * @param cc_p - * @param div_key_p - * @return the cipher state - */ -LoclassState_t loclass_opt_doTagMAC_1(uint8_t* cc_p, const uint8_t* div_key_p) { - LoclassState_t _init = { - ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF, // l - ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF, // r - 0x4c, // b - 0xE012 // t - }; - loclass_opt_suc(div_key_p, &_init, cc_p, 8, false); - return _init; -} - -/** - * The second part of the tag MAC calculation, since the CC is already calculated into the state, - * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag - * MAC response. - * @param _init - precalculated cipher state - * @param nr - the reader challenge - * @param mac - where to store the MAC - * @param div_key_p - the key to use - */ -void loclass_opt_doTagMAC_2( - LoclassState_t _init, - uint8_t* nr, - uint8_t mac[4], - const uint8_t* div_key_p) { - loclass_opt_suc(div_key_p, &_init, nr, 4, true); - loclass_opt_output(div_key_p, &_init, mac); -} - -void loclass_iclass_calc_div_key(uint8_t* csn, uint8_t* key, uint8_t* div_key, bool elite) { - if(elite) { - uint8_t keytable[128] = {0}; - uint8_t key_index[8] = {0}; - uint8_t key_sel[8] = {0}; - uint8_t key_sel_p[8] = {0}; - loclass_hash2(key, keytable); - loclass_hash1(csn, key_index); - for(uint8_t i = 0; i < 8; i++) key_sel[i] = keytable[key_index[i]]; - - //Permute from iclass format to standard format - loclass_permutekey_rev(key_sel, key_sel_p); - loclass_diversifyKey(csn, key_sel_p, div_key); - } else { - loclass_diversifyKey(csn, key, div_key); - } -} diff --git a/applications/external/picopass/lib/loclass/optimized_cipher.h b/applications/external/picopass/lib/loclass/optimized_cipher.h deleted file mode 100644 index 2158f0acf..000000000 --- a/applications/external/picopass/lib/loclass/optimized_cipher.h +++ /dev/null @@ -1,98 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// More recently from https://github.com/RfidResearchGroup/proxmark3 -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#ifndef OPTIMIZED_CIPHER_H -#define OPTIMIZED_CIPHER_H -#include -#include -#include -#include - -/** -* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 -* consisting of the following four components: -* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ; -* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ; -* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 . -* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 . -**/ -typedef struct { - uint8_t l; - uint8_t r; - uint8_t b; - uint16_t t; -} LoclassState_t; - -/** The reader MAC is MAC(key, CC * NR ) - **/ -void loclass_opt_doReaderMAC(uint8_t* cc_nr_p, uint8_t* div_key_p, uint8_t mac[4]); - -void loclass_opt_doReaderMAC_2( - LoclassState_t _init, - uint8_t* nr, - uint8_t mac[4], - const uint8_t* div_key_p); - -/** - * The tag MAC is MAC(key, CC * NR * 32x0)) - */ -void loclass_opt_doTagMAC(uint8_t* cc_p, const uint8_t* div_key_p, uint8_t mac[4]); - -/** - * The tag MAC can be divided (both can, but no point in dividing the reader mac) into - * two functions, since the first 8 bytes are known, we can pre-calculate the state - * reached after feeding CC to the cipher. - * @param cc_p - * @param div_key_p - * @return the cipher state - */ -LoclassState_t loclass_opt_doTagMAC_1(uint8_t* cc_p, const uint8_t* div_key_p); -/** - * The second part of the tag MAC calculation, since the CC is already calculated into the state, - * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag - * MAC response. - * @param _init - precalculated cipher state - * @param nr - the reader challenge - * @param mac - where to store the MAC - * @param div_key_p - the key to use - */ -void loclass_opt_doTagMAC_2( - LoclassState_t _init, - uint8_t* nr, - uint8_t mac[4], - const uint8_t* div_key_p); - -void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]); -void loclass_iclass_calc_div_key(uint8_t* csn, uint8_t* key, uint8_t* div_key, bool elite); -#endif // OPTIMIZED_CIPHER_H diff --git a/applications/external/picopass/lib/loclass/optimized_cipherutils.c b/applications/external/picopass/lib/loclass/optimized_cipherutils.c deleted file mode 100644 index e6a87c4a7..000000000 --- a/applications/external/picopass/lib/loclass/optimized_cipherutils.c +++ /dev/null @@ -1,136 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#include "optimized_cipherutils.h" -#include - -/** - * - * @brief Return and remove the first bit (x0) in the stream : - * @param stream - * @return - */ -bool loclass_headBit(LoclassBitstreamIn_t* stream) { - int bytepos = stream->position >> 3; // divide by 8 - int bitpos = (stream->position++) & 7; // mask out 00000111 - return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; -} -/** - * @brief Return and remove the last bit (xn) in the stream: - * @param stream - * @return - */ -bool loclass_tailBit(LoclassBitstreamIn_t* stream) { - int bitpos = stream->numbits - 1 - (stream->position++); - - int bytepos = bitpos >> 3; - bitpos &= 7; - return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; -} -/** - * @brief Pushes bit onto the stream - * @param stream - * @param bit - */ -void loclass_pushBit(LoclassBitstreamOut_t* stream, bool bit) { - int bytepos = stream->position >> 3; // divide by 8 - int bitpos = stream->position & 7; - *(stream->buffer + bytepos) |= (bit) << (7 - bitpos); - stream->position++; - stream->numbits++; -} - -/** - * @brief Pushes the lower six bits onto the stream - * as b0 b1 b2 b3 b4 b5 b6 - * @param stream - * @param bits - */ -void loclass_push6bits(LoclassBitstreamOut_t* stream, uint8_t bits) { - loclass_pushBit(stream, bits & 0x20); - loclass_pushBit(stream, bits & 0x10); - loclass_pushBit(stream, bits & 0x08); - loclass_pushBit(stream, bits & 0x04); - loclass_pushBit(stream, bits & 0x02); - loclass_pushBit(stream, bits & 0x01); -} - -/** - * @brief loclass_bitsLeft - * @param stream - * @return number of bits left in stream - */ -int loclass_bitsLeft(LoclassBitstreamIn_t* stream) { - return stream->numbits - stream->position; -} -/** - * @brief numBits - * @param stream - * @return Number of bits stored in stream - */ -void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t* dest) { - while(len--) { - dest[len] = (uint8_t)n; - n >>= 8; - } -} - -uint64_t loclass_x_bytes_to_num(uint8_t* src, size_t len) { - uint64_t num = 0; - while(len--) { - num = (num << 8) | (*src); - src++; - } - return num; -} - -uint8_t loclass_reversebytes(uint8_t b) { - b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; - b = (b & 0xCC) >> 2 | (b & 0x33) << 2; - b = (b & 0xAA) >> 1 | (b & 0x55) << 1; - return b; -} - -void loclass_reverse_arraybytes(uint8_t* arr, size_t len) { - uint8_t i; - for(i = 0; i < len; i++) { - arr[i] = loclass_reversebytes(arr[i]); - } -} - -void loclass_reverse_arraycopy(uint8_t* arr, uint8_t* dest, size_t len) { - uint8_t i; - for(i = 0; i < len; i++) { - dest[i] = loclass_reversebytes(arr[i]); - } -} diff --git a/applications/external/picopass/lib/loclass/optimized_cipherutils.h b/applications/external/picopass/lib/loclass/optimized_cipherutils.h deleted file mode 100644 index 05b682079..000000000 --- a/applications/external/picopass/lib/loclass/optimized_cipherutils.h +++ /dev/null @@ -1,64 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// More recently from https://github.com/RfidResearchGroup/proxmark3 -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#ifndef CIPHERUTILS_H -#define CIPHERUTILS_H -#include -#include -#include - -typedef struct { - uint8_t* buffer; - uint8_t numbits; - uint8_t position; -} LoclassBitstreamIn_t; - -typedef struct { - uint8_t* buffer; - uint8_t numbits; - uint8_t position; -} LoclassBitstreamOut_t; - -bool loclass_headBit(LoclassBitstreamIn_t* stream); -bool loclass_tailBit(LoclassBitstreamIn_t* stream); -void loclass_pushBit(LoclassBitstreamOut_t* stream, bool bit); -int loclass_bitsLeft(LoclassBitstreamIn_t* stream); - -void loclass_push6bits(LoclassBitstreamOut_t* stream, uint8_t bits); -void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t* dest); -uint64_t loclass_x_bytes_to_num(uint8_t* src, size_t len); -uint8_t loclass_reversebytes(uint8_t b); -void loclass_reverse_arraybytes(uint8_t* arr, size_t len); -void loclass_reverse_arraycopy(uint8_t* arr, uint8_t* dest, size_t len); -#endif // CIPHERUTILS_H diff --git a/applications/external/picopass/lib/loclass/optimized_elite.c b/applications/external/picopass/lib/loclass/optimized_elite.c deleted file mode 100644 index 34e987060..000000000 --- a/applications/external/picopass/lib/loclass/optimized_elite.c +++ /dev/null @@ -1,232 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#include "optimized_elite.h" - -#include -#include -#include -#include -#include "optimized_ikeys.h" - -/** - * @brief Permutes a key from standard NIST format to Iclass specific format - * from http://www.proxmark.org/forum/viewtopic.php?pid=11220#p11220 - * - * If you loclass_permute [6c 8d 44 f9 2a 2d 01 bf] you get [8a 0d b9 88 bb a7 90 ea] as shown below. - * - * 1 0 1 1 1 1 1 1 bf - * 0 0 0 0 0 0 0 1 01 - * 0 0 1 0 1 1 0 1 2d - * 0 0 1 0 1 0 1 0 2a - * 1 1 1 1 1 0 0 1 f9 - * 0 1 0 0 0 1 0 0 44 - * 1 0 0 0 1 1 0 1 8d - * 0 1 1 0 1 1 0 0 6c - * - * 8 0 b 8 b a 9 e - * a d 9 8 b 7 0 a - * - * @param key - * @param dest - */ -void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]) { - int i; - for(i = 0; i < 8; i++) { - dest[i] = (((key[7] & (0x80 >> i)) >> (7 - i)) << 7) | - (((key[6] & (0x80 >> i)) >> (7 - i)) << 6) | - (((key[5] & (0x80 >> i)) >> (7 - i)) << 5) | - (((key[4] & (0x80 >> i)) >> (7 - i)) << 4) | - (((key[3] & (0x80 >> i)) >> (7 - i)) << 3) | - (((key[2] & (0x80 >> i)) >> (7 - i)) << 2) | - (((key[1] & (0x80 >> i)) >> (7 - i)) << 1) | - (((key[0] & (0x80 >> i)) >> (7 - i)) << 0); - } -} -/** - * Permutes a key from iclass specific format to NIST format - * @brief loclass_permutekey_rev - * @param key - * @param dest - */ -void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]) { - int i; - for(i = 0; i < 8; i++) { - dest[7 - i] = (((key[0] & (0x80 >> i)) >> (7 - i)) << 7) | - (((key[1] & (0x80 >> i)) >> (7 - i)) << 6) | - (((key[2] & (0x80 >> i)) >> (7 - i)) << 5) | - (((key[3] & (0x80 >> i)) >> (7 - i)) << 4) | - (((key[4] & (0x80 >> i)) >> (7 - i)) << 3) | - (((key[5] & (0x80 >> i)) >> (7 - i)) << 2) | - (((key[6] & (0x80 >> i)) >> (7 - i)) << 1) | - (((key[7] & (0x80 >> i)) >> (7 - i)) << 0); - } -} - -/** - * Helper function for loclass_hash1 - * @brief loclass_rr - * @param val - * @return - */ -static uint8_t loclass_rr(uint8_t val) { - return val >> 1 | ((val & 1) << 7); -} - -/** - * Helper function for loclass_hash1 - * @brief rl - * @param val - * @return - */ -static uint8_t loclass_rl(uint8_t val) { - return val << 1 | ((val & 0x80) >> 7); -} - -/** - * Helper function for loclass_hash1 - * @brief loclass_swap - * @param val - * @return - */ -static uint8_t loclass_swap(uint8_t val) { - return ((val >> 4) & 0xFF) | ((val & 0xFF) << 4); -} - -/** - * Hash1 takes CSN as input, and determines what bytes in the keytable will be used - * when constructing the K_sel. - * @param csn the CSN used - * @param k output - */ -void loclass_hash1(const uint8_t csn[], uint8_t k[]) { - k[0] = csn[0] ^ csn[1] ^ csn[2] ^ csn[3] ^ csn[4] ^ csn[5] ^ csn[6] ^ csn[7]; - k[1] = csn[0] + csn[1] + csn[2] + csn[3] + csn[4] + csn[5] + csn[6] + csn[7]; - k[2] = loclass_rr(loclass_swap(csn[2] + k[1])); - k[3] = loclass_rl(loclass_swap(csn[3] + k[0])); - k[4] = ~loclass_rr(csn[4] + k[2]) + 1; - k[5] = ~loclass_rl(csn[5] + k[3]) + 1; - k[6] = loclass_rr(csn[6] + (k[4] ^ 0x3c)); - k[7] = loclass_rl(csn[7] + (k[5] ^ 0xc3)); - - k[7] &= 0x7F; - k[6] &= 0x7F; - k[5] &= 0x7F; - k[4] &= 0x7F; - k[3] &= 0x7F; - k[2] &= 0x7F; - k[1] &= 0x7F; - k[0] &= 0x7F; -} -/** -Definition 14. Define the rotate key function loclass_rk : (F 82 ) 8 × N → (F 82 ) 8 as -loclass_rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] -loclass_rk(x [0] . . . x [7] , n + 1) = loclass_rk(loclass_rl(x [0] ) . . . loclass_rl(x [7] ), n) -**/ -static void loclass_rk(uint8_t* key, uint8_t n, uint8_t* outp_key) { - memcpy(outp_key, key, 8); - uint8_t j; - while(n-- > 0) { - for(j = 0; j < 8; j++) outp_key[j] = loclass_rl(outp_key[j]); - } - return; -} - -static mbedtls_des_context loclass_ctx_enc; -static mbedtls_des_context loclass_ctx_dec; - -static void loclass_desdecrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8_t* output) { - uint8_t key_std_format[8] = {0}; - loclass_permutekey_rev(iclass_key, key_std_format); - mbedtls_des_setkey_dec(&loclass_ctx_dec, key_std_format); - mbedtls_des_crypt_ecb(&loclass_ctx_dec, input, output); -} - -static void loclass_desencrypt_iclass(uint8_t* iclass_key, uint8_t* input, uint8_t* output) { - uint8_t key_std_format[8] = {0}; - loclass_permutekey_rev(iclass_key, key_std_format); - mbedtls_des_setkey_enc(&loclass_ctx_enc, key_std_format); - mbedtls_des_crypt_ecb(&loclass_ctx_enc, input, output); -} - -/** - * @brief Insert uint8_t[8] custom master key to calculate hash2 and return key_select. - * @param key unpermuted custom key - * @param loclass_hash1 loclass_hash1 - * @param key_sel output key_sel=h[loclass_hash1[i]] - */ -void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable) { - /** - *Expected: - * High Security Key Table - - 00 F1 35 59 A1 0D 5A 26 7F 18 60 0B 96 8A C0 25 C1 - 10 BF A1 3B B0 FF 85 28 75 F2 1F C6 8F 0E 74 8F 21 - 20 14 7A 55 16 C8 A9 7D B3 13 0C 5D C9 31 8D A9 B2 - 30 A3 56 83 0F 55 7E DE 45 71 21 D2 6D C1 57 1C 9C - 40 78 2F 64 51 42 7B 64 30 FA 26 51 76 D3 E0 FB B6 - 50 31 9F BF 2F 7E 4F 94 B4 BD 4F 75 91 E3 1B EB 42 - 60 3F 88 6F B8 6C 2C 93 0D 69 2C D5 20 3C C1 61 95 - 70 43 08 A0 2F FE B3 26 D7 98 0B 34 7B 47 70 A0 AB - - **** The 64-bit HS Custom Key Value = 5B7C62C491C11B39 ******/ - uint8_t key64_negated[8] = {0}; - uint8_t z[8][8] = {{0}, {0}}; - uint8_t temp_output[8] = {0}; - - //calculate complement of key - int i; - for(i = 0; i < 8; i++) key64_negated[i] = ~key64[i]; - - // Once again, key is on iclass-format - loclass_desencrypt_iclass(key64, key64_negated, z[0]); - - uint8_t y[8][8] = {{0}, {0}}; - - // y[0]=DES_dec(z[0],~key) - // Once again, key is on iclass-format - loclass_desdecrypt_iclass(z[0], key64_negated, y[0]); - - for(i = 1; i < 8; i++) { - loclass_rk(key64, i, temp_output); - loclass_desdecrypt_iclass(temp_output, z[i - 1], z[i]); - loclass_desencrypt_iclass(temp_output, y[i - 1], y[i]); - } - - if(outp_keytable != NULL) { - for(i = 0; i < 8; i++) { - memcpy(outp_keytable + i * 16, y[i], 8); - memcpy(outp_keytable + 8 + i * 16, z[i], 8); - } - } -} diff --git a/applications/external/picopass/lib/loclass/optimized_elite.h b/applications/external/picopass/lib/loclass/optimized_elite.h deleted file mode 100644 index 5343ebb07..000000000 --- a/applications/external/picopass/lib/loclass/optimized_elite.h +++ /dev/null @@ -1,58 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// More recently from https://github.com/RfidResearchGroup/proxmark3 -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#ifndef ELITE_CRACK_H -#define ELITE_CRACK_H - -#include -#include - -void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]); -/** - * Permutes a key from iclass specific format to NIST format - * @brief loclass_permutekey_rev - * @param key - * @param dest - */ -void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]); -/** - * Hash1 takes CSN as input, and determines what bytes in the keytable will be used - * when constructing the K_sel. - * @param csn the CSN used - * @param k output - */ -void loclass_hash1(const uint8_t* csn, uint8_t* k); -void loclass_hash2(uint8_t* key64, uint8_t* outp_keytable); - -#endif diff --git a/applications/external/picopass/lib/loclass/optimized_ikeys.c b/applications/external/picopass/lib/loclass/optimized_ikeys.c deleted file mode 100644 index 1e6f12c56..000000000 --- a/applications/external/picopass/lib/loclass/optimized_ikeys.c +++ /dev/null @@ -1,320 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- - -/** -From "Dismantling iclass": - This section describes in detail the built-in key diversification algorithm of iClass. - Besides the obvious purpose of deriving a card key from a master key, this - algorithm intends to circumvent weaknesses in the cipher by preventing the - usage of certain ‘weak’ keys. In order to compute a diversified key, the iClass - reader first encrypts the card identity id with the master key K, using single - DES. The resulting ciphertext is then input to a function called loclass_hash0 which - outputs the diversified key k. - - k = loclass_hash0(DES enc (id, K)) - - Here the DES encryption of id with master key K outputs a cryptogram c - of 64 bits. These 64 bits are divided as c = x, y, z [0] , . . . , z [7] ∈ F 82 × F 82 × (F 62 ) 8 - which is used as input to the loclass_hash0 function. This function introduces some - obfuscation by performing a number of permutations, complement and modulo - operations, see Figure 2.5. Besides that, it checks for and removes patterns like - similar key bytes, which could produce a strong bias in the cipher. Finally, the - output of loclass_hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 . - -**/ -#include "optimized_ikeys.h" - -#include -#include -#include -#include -#include "optimized_cipherutils.h" - -static const uint8_t loclass_pi[35] = {0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, - 0x33, 0x35, 0x39, 0x36, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, - 0x4E, 0x53, 0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, - 0x66, 0x69, 0x6A, 0x6C, 0x71, 0x72, 0x74, 0x78}; - -/** - * @brief The key diversification algorithm uses 6-bit bytes. - * This implementation uses 64 bit uint to pack seven of them into one - * variable. When they are there, they are placed as follows: - * XXXX XXXX N0 .... N7, occupying the last 48 bits. - * - * This function picks out one from such a collection - * @param all - * @param n bitnumber - * @return - */ -static uint8_t loclass_getSixBitByte(uint64_t c, int n) { - return (c >> (42 - 6 * n)) & 0x3F; -} - -/** - * @brief Puts back a six-bit 'byte' into a uint64_t. - * @param c buffer - * @param z the value to place there - * @param n bitnumber. - */ -static void loclass_pushbackSixBitByte(uint64_t* c, uint8_t z, int n) { - //0x XXXX YYYY ZZZZ ZZZZ ZZZZ - // ^z0 ^z7 - //z0: 1111 1100 0000 0000 - - uint64_t masked = z & 0x3F; - uint64_t eraser = 0x3F; - masked <<= 42 - 6 * n; - eraser <<= 42 - 6 * n; - - //masked <<= 6*n; - //eraser <<= 6*n; - - eraser = ~eraser; - (*c) &= eraser; - (*c) |= masked; -} -/** - * @brief Swaps the z-values. - * If the input value has format XYZ0Z1...Z7, the output will have the format - * XYZ7Z6...Z0 instead - * @param c - * @return - */ -static uint64_t loclass_swapZvalues(uint64_t c) { - uint64_t newz = 0; - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 0), 7); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 1), 6); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 2), 5); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 3), 4); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 4), 3); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 5), 2); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 6), 1); - loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 7), 0); - newz |= (c & 0xFFFF000000000000); - return newz; -} - -/** -* @return 4 six-bit bytes chunked into a uint64_t,as 00..00a0a1a2a3 -*/ -static uint64_t loclass_ck(int i, int j, uint64_t z) { - if(i == 1 && j == -1) { - // loclass_ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] - return z; - } else if(j == -1) { - // loclass_ck(i, −1, z [0] . . . z [3] ) = loclass_ck(i − 1, i − 2, z [0] . . . z [3] ) - return loclass_ck(i - 1, i - 2, z); - } - - if(loclass_getSixBitByte(z, i) == loclass_getSixBitByte(z, j)) { - //loclass_ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ) - uint64_t newz = 0; - int c; - for(c = 0; c < 4; c++) { - uint8_t val = loclass_getSixBitByte(z, c); - if(c == i) - loclass_pushbackSixBitByte(&newz, j, c); - else - loclass_pushbackSixBitByte(&newz, val, c); - } - return loclass_ck(i, j - 1, newz); - } else { - return loclass_ck(i, j - 1, z); - } -} -/** - - Definition 8. - Let the function check : (F 62 ) 8 → (F 62 ) 8 be defined as - check(z [0] . . . z [7] ) = loclass_ck(3, 2, z [0] . . . z [3] ) · loclass_ck(3, 2, z [4] . . . z [7] ) - - where loclass_ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as - - loclass_ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] - loclass_ck(i, −1, z [0] . . . z [3] ) = loclass_ck(i − 1, i − 2, z [0] . . . z [3] ) - loclass_ck(i, j, z [0] . . . z [3] ) = - loclass_ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ; - loclass_ck(i, j − 1, z [0] . . . z [3] ), otherwise - - otherwise. -**/ - -static uint64_t loclass_check(uint64_t z) { - //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] - - // loclass_ck(3, 2, z [0] . . . z [3] ) - uint64_t ck1 = loclass_ck(3, 2, z); - - // loclass_ck(3, 2, z [4] . . . z [7] ) - uint64_t ck2 = loclass_ck(3, 2, z << 24); - - //The loclass_ck function will place the values - // in the middle of z. - ck1 &= 0x00000000FFFFFF000000; - ck2 &= 0x00000000FFFFFF000000; - - return ck1 | ck2 >> 24; -} - -static void loclass_permute( - LoclassBitstreamIn_t* p_in, - uint64_t z, - int l, - int r, - LoclassBitstreamOut_t* out) { - if(loclass_bitsLeft(p_in) == 0) return; - - bool pn = loclass_tailBit(p_in); - if(pn) { // pn = 1 - uint8_t zl = loclass_getSixBitByte(z, l); - - loclass_push6bits(out, zl + 1); - loclass_permute(p_in, z, l + 1, r, out); - } else { // otherwise - uint8_t zr = loclass_getSixBitByte(z, r); - - loclass_push6bits(out, zr); - loclass_permute(p_in, z, l, r + 1, out); - } -} - -/** - * @brief - *Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as - * loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where - * z'[i] = (z[i] mod (63-i)) + i i = 0...3 - * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 - * ẑ = check(z'); - * @param c - * @param k this is where the diversified key is put (should be 8 bytes) - * @return - */ -void loclass_hash0(uint64_t c, uint8_t k[8]) { - c = loclass_swapZvalues(c); - - //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] - // x = 8 bits - // y = 8 bits - // z0-z7 6 bits each : 48 bits - uint8_t x = (c & 0xFF00000000000000) >> 56; - uint8_t y = (c & 0x00FF000000000000) >> 48; - uint64_t zP = 0; - - for(int n = 0; n < 4; n++) { - uint8_t zn = loclass_getSixBitByte(c, n); - uint8_t zn4 = loclass_getSixBitByte(c, n + 4); - uint8_t _zn = (zn % (63 - n)) + n; - uint8_t _zn4 = (zn4 % (64 - n)) + n; - loclass_pushbackSixBitByte(&zP, _zn, n); - loclass_pushbackSixBitByte(&zP, _zn4, n + 4); - } - - uint64_t zCaret = loclass_check(zP); - uint8_t p = loclass_pi[x % 35]; - - if(x & 1) //Check if x7 is 1 - p = ~p; - - LoclassBitstreamIn_t p_in = {&p, 8, 0}; - uint8_t outbuffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; - LoclassBitstreamOut_t out = {outbuffer, 0, 0}; - loclass_permute(&p_in, zCaret, 0, 4, &out); //returns 48 bits? or 6 8-bytes - - //Out is now a buffer containing six-bit bytes, should be 48 bits - // if all went well - //Shift z-values down onto the lower segment - - uint64_t zTilde = loclass_x_bytes_to_num(outbuffer, sizeof(outbuffer)); - - zTilde >>= 16; - - for(int i = 0; i < 8; i++) { - // the key on index i is first a bit from y - // then six bits from z, - // then a bit from p - - // Init with zeroes - k[i] = 0; - // First, place yi leftmost in k - //k[i] |= (y << i) & 0x80 ; - - // First, place y(7-i) leftmost in k - k[i] |= (y << (7 - i)) & 0x80; - - uint8_t zTilde_i = loclass_getSixBitByte(zTilde, i); - // zTildeI is now on the form 00XXXXXX - // with one leftshift, it'll be - // 0XXXXXX0 - // So after leftshift, we can OR it into k - // However, when doing complement, we need to - // again MASK 0XXXXXX0 (0x7E) - zTilde_i <<= 1; - - //Finally, add bit from p or p-mod - //Shift bit i into rightmost location (mask only after complement) - uint8_t p_i = p >> i & 0x1; - - if(k[i]) { // yi = 1 - k[i] |= ~zTilde_i & 0x7E; - k[i] |= p_i & 1; - k[i] += 1; - - } else { // otherwise - k[i] |= zTilde_i & 0x7E; - k[i] |= (~p_i) & 1; - } - } -} -/** - * @brief Performs Elite-class key diversification - * @param csn - * @param key - * @param div_key - */ -void loclass_diversifyKey(uint8_t* csn, const uint8_t* key, uint8_t* div_key) { - mbedtls_des_context loclass_ctx_enc; - - // Prepare the DES key - mbedtls_des_setkey_enc(&loclass_ctx_enc, key); - - uint8_t crypted_csn[8] = {0}; - - // Calculate DES(CSN, KEY) - mbedtls_des_crypt_ecb(&loclass_ctx_enc, csn, crypted_csn); - - //Calculate HASH0(DES)) - uint64_t c_csn = loclass_x_bytes_to_num(crypted_csn, sizeof(crypted_csn)); - - loclass_hash0(c_csn, div_key); -} diff --git a/applications/external/picopass/lib/loclass/optimized_ikeys.h b/applications/external/picopass/lib/loclass/optimized_ikeys.h deleted file mode 100644 index f2711d31e..000000000 --- a/applications/external/picopass/lib/loclass/optimized_ikeys.h +++ /dev/null @@ -1,66 +0,0 @@ -//----------------------------------------------------------------------------- -// Borrowed initially from https://github.com/holiman/loclass -// More recently from https://github.com/RfidResearchGroup/proxmark3 -// Copyright (C) 2014 Martin Holst Swende -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// WARNING -// -// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. -// -// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL -// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, -// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. -// -// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. -//----------------------------------------------------------------------------- -// It is a reconstruction of the cipher engine used in iClass, and RFID techology. -// -// The implementation is based on the work performed by -// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and -// Milosch Meriac in the paper "Dismantling IClass". -//----------------------------------------------------------------------------- -#ifndef IKEYS_H -#define IKEYS_H - -#include - -/** - * @brief - *Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as - * loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where - * z'[i] = (z[i] mod (63-i)) + i i = 0...3 - * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 - * ẑ = check(z'); - * @param c - * @param k this is where the diversified key is put (should be 8 bytes) - * @return - */ -void loclass_hash0(uint64_t c, uint8_t k[8]); -/** - * @brief Performs Elite-class key diversification - * @param csn - * @param key - * @param div_key - */ - -void loclass_diversifyKey(uint8_t* csn, const uint8_t* key, uint8_t* div_key); -/** - * @brief Permutes a key from standard NIST format to Iclass specific format - * @param key - * @param dest - */ - -#endif // IKEYS_H diff --git a/applications/external/picopass/picopass.c b/applications/external/picopass/picopass.c deleted file mode 100644 index 6737d8077..000000000 --- a/applications/external/picopass/picopass.c +++ /dev/null @@ -1,212 +0,0 @@ -#include "picopass_i.h" - -#define TAG "PicoPass" - -bool picopass_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - Picopass* picopass = context; - return scene_manager_handle_custom_event(picopass->scene_manager, event); -} - -bool picopass_back_event_callback(void* context) { - furi_assert(context); - Picopass* picopass = context; - return scene_manager_handle_back_event(picopass->scene_manager); -} - -void picopass_tick_event_callback(void* context) { - furi_assert(context); - Picopass* picopass = context; - scene_manager_handle_tick_event(picopass->scene_manager); -} - -Picopass* picopass_alloc() { - Picopass* picopass = malloc(sizeof(Picopass)); - - picopass->worker = picopass_worker_alloc(); - picopass->view_dispatcher = view_dispatcher_alloc(); - picopass->scene_manager = scene_manager_alloc(&picopass_scene_handlers, picopass); - view_dispatcher_enable_queue(picopass->view_dispatcher); - view_dispatcher_set_event_callback_context(picopass->view_dispatcher, picopass); - view_dispatcher_set_custom_event_callback( - picopass->view_dispatcher, picopass_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - picopass->view_dispatcher, picopass_back_event_callback); - view_dispatcher_set_tick_event_callback( - picopass->view_dispatcher, picopass_tick_event_callback, 100); - - // Picopass device - picopass->dev = picopass_device_alloc(); - - // Open GUI record - picopass->gui = furi_record_open(RECORD_GUI); - view_dispatcher_attach_to_gui( - picopass->view_dispatcher, picopass->gui, ViewDispatcherTypeFullscreen); - - // Open Notification record - picopass->notifications = furi_record_open(RECORD_NOTIFICATION); - - // Submenu - picopass->submenu = submenu_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, PicopassViewMenu, submenu_get_view(picopass->submenu)); - - // Popup - picopass->popup = popup_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, PicopassViewPopup, popup_get_view(picopass->popup)); - - // Loading - picopass->loading = loading_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, PicopassViewLoading, loading_get_view(picopass->loading)); - - // Text Input - picopass->text_input = text_input_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, - PicopassViewTextInput, - text_input_get_view(picopass->text_input)); - - // Custom Widget - picopass->widget = widget_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget)); - - picopass->dict_attack = dict_attack_alloc(); - view_dispatcher_add_view( - picopass->view_dispatcher, - PicopassViewDictAttack, - dict_attack_get_view(picopass->dict_attack)); - - return picopass; -} - -void picopass_free(Picopass* picopass) { - furi_assert(picopass); - - // Picopass device - picopass_device_free(picopass->dev); - picopass->dev = NULL; - - // Submenu - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewMenu); - submenu_free(picopass->submenu); - - // Popup - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewPopup); - popup_free(picopass->popup); - - // Loading - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewLoading); - loading_free(picopass->loading); - - // TextInput - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewTextInput); - text_input_free(picopass->text_input); - - // Custom Widget - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget); - widget_free(picopass->widget); - - view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack); - dict_attack_free(picopass->dict_attack); - - // Worker - picopass_worker_stop(picopass->worker); - picopass_worker_free(picopass->worker); - - // View Dispatcher - view_dispatcher_free(picopass->view_dispatcher); - - // Scene Manager - scene_manager_free(picopass->scene_manager); - - // GUI - furi_record_close(RECORD_GUI); - picopass->gui = NULL; - - // Notifications - furi_record_close(RECORD_NOTIFICATION); - picopass->notifications = NULL; - - free(picopass); -} - -void picopass_text_store_set(Picopass* picopass, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(picopass->text_store, sizeof(picopass->text_store), text, args); - - va_end(args); -} - -void picopass_text_store_clear(Picopass* picopass) { - memset(picopass->text_store, 0, sizeof(picopass->text_store)); -} - -static const NotificationSequence picopass_sequence_blink_start_cyan = { - &message_blink_start_10, - &message_blink_set_color_cyan, - &message_do_not_reset, - NULL, -}; - -static const NotificationSequence picopass_sequence_blink_stop = { - &message_blink_stop, - NULL, -}; - -void picopass_blink_start(Picopass* picopass) { - notification_message(picopass->notifications, &picopass_sequence_blink_start_cyan); -} - -void picopass_blink_stop(Picopass* picopass) { - notification_message(picopass->notifications, &picopass_sequence_blink_stop); -} - -void picopass_show_loading_popup(void* context, bool show) { - Picopass* picopass = context; - TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); - - if(show) { - // Raise timer priority so that animations can play - vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewLoading); - } else { - // Restore default timer priority - vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); - } -} - -static void picopass_migrate_from_old_folder() { - Storage* storage = furi_record_open(RECORD_STORAGE); - storage_common_migrate(storage, "/ext/picopass", STORAGE_APP_DATA_PATH_PREFIX); - furi_record_close(RECORD_STORAGE); -} - -bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size) { - bool result = size > 0; - while(size > 0) { - result &= (*data == pattern); - data++; - size--; - } - return result; -} - -int32_t picopass_app(void* p) { - UNUSED(p); - picopass_migrate_from_old_folder(); - - Picopass* picopass = picopass_alloc(); - - scene_manager_next_scene(picopass->scene_manager, PicopassSceneStart); - - view_dispatcher_run(picopass->view_dispatcher); - - picopass_free(picopass); - - return 0; -} diff --git a/applications/external/picopass/picopass.h b/applications/external/picopass/picopass.h deleted file mode 100644 index a1a87d7f8..000000000 --- a/applications/external/picopass/picopass.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -typedef struct Picopass Picopass; diff --git a/applications/external/picopass/picopass_device.c b/applications/external/picopass/picopass_device.c deleted file mode 100644 index de43b0bb7..000000000 --- a/applications/external/picopass/picopass_device.c +++ /dev/null @@ -1,380 +0,0 @@ -#include "picopass_device.h" - -#include -#include -#include - -#define TAG "PicopassDevice" - -static const char* picopass_file_header = "Flipper Picopass device"; -static const uint32_t picopass_file_version = 1; - -const uint8_t picopass_iclass_decryptionkey[] = - {0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36}; - -PicopassDevice* picopass_device_alloc() { - PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice)); - picopass_dev->dev_data.pacs.legacy = false; - picopass_dev->dev_data.pacs.se_enabled = false; - picopass_dev->dev_data.pacs.elite_kdf = false; - picopass_dev->dev_data.pacs.pin_length = 0; - picopass_dev->storage = furi_record_open(RECORD_STORAGE); - picopass_dev->dialogs = furi_record_open(RECORD_DIALOGS); - picopass_dev->load_path = furi_string_alloc(); - return picopass_dev; -} - -void picopass_device_set_name(PicopassDevice* dev, const char* name) { - furi_assert(dev); - - strlcpy(dev->dev_name, name, PICOPASS_DEV_NAME_MAX_LEN); -} - -static bool picopass_device_save_file( - PicopassDevice* dev, - const char* dev_name, - const char* folder, - const char* extension, - bool use_load_path) { - furi_assert(dev); - - bool saved = false; - FlipperFormat* file = flipper_format_file_alloc(dev->storage); - PicopassPacs* pacs = &dev->dev_data.pacs; - PicopassBlock* AA1 = dev->dev_data.AA1; - FuriString* temp_str; - temp_str = furi_string_alloc(); - - do { - if(use_load_path && !furi_string_empty(dev->load_path)) { - // Get directory name - path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str); - // Make path to file to save - furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension); - } else { - // First remove picopass device file if it was saved - furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); - } - // Open file - if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; - - if(dev->format == PicopassDeviceSaveFormatHF) { - uint32_t fc = pacs->record.FacilityCode; - uint32_t cn = pacs->record.CardNumber; - // Write header - if(!flipper_format_write_header_cstr(file, picopass_file_header, picopass_file_version)) - break; - if(pacs->record.valid) { - if(!flipper_format_write_uint32(file, "Facility Code", &fc, 1)) break; - if(!flipper_format_write_uint32(file, "Card Number", &cn, 1)) break; - if(!flipper_format_write_hex( - file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN)) - break; - if(pacs->pin_length > 0) { - if(!flipper_format_write_hex(file, "PIN\t\t", pacs->pin0, PICOPASS_BLOCK_LEN)) - break; - if(!flipper_format_write_hex( - file, "PIN(cont.)\t", pacs->pin1, PICOPASS_BLOCK_LEN)) - break; - } - } - // TODO: Add elite - if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break; - bool block_saved = true; - - size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : - PICOPASS_MAX_APP_LIMIT; - for(size_t i = 0; i < app_limit; i++) { - furi_string_printf(temp_str, "Block %d", i); - if(!flipper_format_write_hex( - file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { - block_saved = false; - break; - } - } - if(!block_saved) break; - } else if(dev->format == PicopassDeviceSaveFormatLF) { - const char* lf_header = "Flipper RFID key"; - // Write header - if(!flipper_format_write_header_cstr(file, lf_header, 1)) break; - if(!flipper_format_write_comment_cstr( - file, - "This was generated from the Picopass plugin and may not match current lfrfid")) - break; - // When lfrfid supports more formats, update this - if(!flipper_format_write_string_cstr(file, "Key type", "H10301")) break; - uint8_t H10301[3] = {0}; - H10301[0] = pacs->record.FacilityCode; - H10301[1] = pacs->record.CardNumber >> 8; - H10301[2] = pacs->record.CardNumber & 0x00FF; - if(!flipper_format_write_hex(file, "Data", H10301, 3)) break; - } - saved = true; - } while(0); - - if(!saved) { - dialog_message_show_storage_error(dev->dialogs, "Can not save\nfile"); - } - furi_string_free(temp_str); - flipper_format_free(file); - return saved; -} - -bool picopass_device_save(PicopassDevice* dev, const char* dev_name) { - if(dev->format == PicopassDeviceSaveFormatHF) { - return picopass_device_save_file( - dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, PICOPASS_APP_EXTENSION, true); - } else if(dev->format == PicopassDeviceSaveFormatLF) { - return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true); - } - - return false; -} - -static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, bool show_dialog) { - bool parsed = false; - FlipperFormat* file = flipper_format_file_alloc(dev->storage); - PicopassBlock* AA1 = dev->dev_data.AA1; - PicopassPacs* pacs = &dev->dev_data.pacs; - FuriString* temp_str; - temp_str = furi_string_alloc(); - bool deprecated_version = false; - - if(dev->loading_cb) { - dev->loading_cb(dev->loading_cb_ctx, true); - } - - do { - if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break; - - // Read and verify file header - uint32_t version = 0; - if(!flipper_format_read_header(file, temp_str, &version)) break; - if(furi_string_cmp_str(temp_str, picopass_file_header) || - (version != picopass_file_version)) { - deprecated_version = true; - break; - } - - // Parse header blocks - bool block_read = true; - for(size_t i = 0; i < 6; i++) { - furi_string_printf(temp_str, "Block %d", i); - if(!flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { - block_read = false; - break; - } - } - - size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0]; - // Fix for unpersonalized cards that have app_limit set to 0xFF - if(app_limit > PICOPASS_MAX_APP_LIMIT) app_limit = PICOPASS_MAX_APP_LIMIT; - for(size_t i = 6; i < app_limit; i++) { - furi_string_printf(temp_str, "Block %d", i); - if(!flipper_format_read_hex( - file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) { - block_read = false; - break; - } - } - if(!block_read) break; - - if(picopass_device_parse_credential(AA1, pacs) != ERR_NONE) break; - if(picopass_device_parse_wiegand(pacs->credential, &pacs->record) != ERR_NONE) break; - - parsed = true; - } while(false); - - if(dev->loading_cb) { - dev->loading_cb(dev->loading_cb_ctx, false); - } - - if((!parsed) && (show_dialog)) { - if(deprecated_version) { - dialog_message_show_storage_error(dev->dialogs, "File format deprecated"); - } else { - dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile"); - } - } - - furi_string_free(temp_str); - flipper_format_free(file); - - return parsed; -} - -void picopass_device_clear(PicopassDevice* dev) { - furi_assert(dev); - - picopass_device_data_clear(&dev->dev_data); - memset(&dev->dev_data, 0, sizeof(dev->dev_data)); - dev->format = PicopassDeviceSaveFormatHF; - furi_string_reset(dev->load_path); -} - -void picopass_device_free(PicopassDevice* picopass_dev) { - furi_assert(picopass_dev); - picopass_device_clear(picopass_dev); - furi_record_close(RECORD_STORAGE); - furi_record_close(RECORD_DIALOGS); - furi_string_free(picopass_dev->load_path); - free(picopass_dev); -} - -bool picopass_file_select(PicopassDevice* dev) { - furi_assert(dev); - - FuriString* picopass_app_folder; - picopass_app_folder = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, PICOPASS_APP_EXTENSION, &I_Nfc_10px); - browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; - - bool res = dialog_file_browser_show( - dev->dialogs, dev->load_path, picopass_app_folder, &browser_options); - - furi_string_free(picopass_app_folder); - if(res) { - FuriString* filename; - filename = furi_string_alloc(); - path_extract_filename(dev->load_path, filename, true); - strncpy(dev->dev_name, furi_string_get_cstr(filename), PICOPASS_DEV_NAME_MAX_LEN); - res = picopass_device_load_data(dev, dev->load_path, true); - if(res) { - picopass_device_set_name(dev, dev->dev_name); - } - furi_string_free(filename); - } - - return res; -} - -void picopass_device_data_clear(PicopassDeviceData* dev_data) { - for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { - memset(dev_data->AA1[i].data, 0, sizeof(dev_data->AA1[i].data)); - } - dev_data->pacs.legacy = false; - dev_data->pacs.se_enabled = false; - dev_data->pacs.elite_kdf = false; - dev_data->pacs.pin_length = 0; -} - -bool picopass_device_delete(PicopassDevice* dev, bool use_load_path) { - furi_assert(dev); - - bool deleted = false; - FuriString* file_path; - file_path = furi_string_alloc(); - - do { - // Delete original file - if(use_load_path && !furi_string_empty(dev->load_path)) { - furi_string_set(file_path, dev->load_path); - } else { - furi_string_printf( - file_path, APP_DATA_PATH("%s%s"), dev->dev_name, PICOPASS_APP_EXTENSION); - } - if(!storage_simply_remove(dev->storage, furi_string_get_cstr(file_path))) break; - deleted = true; - } while(0); - - if(!deleted) { - dialog_message_show_storage_error(dev->dialogs, "Can not remove file"); - } - - furi_string_free(file_path); - return deleted; -} - -void picopass_device_set_loading_callback( - PicopassDevice* dev, - PicopassLoadingCallback callback, - void* context) { - furi_assert(dev); - - dev->loading_cb = callback; - dev->loading_cb_ctx = context; -} - -ReturnCode picopass_device_decrypt(uint8_t* enc_data, uint8_t* dec_data) { - uint8_t key[32] = {0}; - memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey)); - mbedtls_des3_context ctx; - mbedtls_des3_init(&ctx); - mbedtls_des3_set2key_dec(&ctx, key); - mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); - mbedtls_des3_free(&ctx); - return ERR_NONE; -} - -ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs) { - ReturnCode err; - - pacs->biometrics = AA1[6].data[4]; - pacs->pin_length = AA1[6].data[6] & 0x0F; - pacs->encryption = AA1[6].data[7]; - - if(pacs->encryption == PicopassDeviceEncryption3DES) { - FURI_LOG_D(TAG, "3DES Encrypted"); - err = picopass_device_decrypt(AA1[7].data, pacs->credential); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - return err; - } - - err = picopass_device_decrypt(AA1[8].data, pacs->pin0); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - return err; - } - - err = picopass_device_decrypt(AA1[9].data, pacs->pin1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "decrypt error %d", err); - return err; - } - } else if(pacs->encryption == PicopassDeviceEncryptionNone) { - FURI_LOG_D(TAG, "No Encryption"); - memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN); - memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN); - } else if(pacs->encryption == PicopassDeviceEncryptionDES) { - FURI_LOG_D(TAG, "DES Encrypted"); - } else { - FURI_LOG_D(TAG, "Unknown encryption"); - } - - pacs->sio = (AA1[10].data[0] == 0x30); // rough check - - return ERR_NONE; -} - -ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) { - uint32_t* halves = (uint32_t*)data; - if(halves[0] == 0) { - uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); - record->bitLength = 31 - leading0s; - } else { - uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); - record->bitLength = 63 - leading0s; - } - FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); - - if(record->bitLength == 26) { - uint8_t* v4 = data + 4; - uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); - - record->CardNumber = (bot >> 1) & 0xFFFF; - record->FacilityCode = (bot >> 17) & 0xFF; - FURI_LOG_D(TAG, "FC: %u CN: %u", record->FacilityCode, record->CardNumber); - record->valid = true; - } else { - record->CardNumber = 0; - record->FacilityCode = 0; - record->valid = false; - } - return ERR_NONE; -} diff --git a/applications/external/picopass/picopass_device.h b/applications/external/picopass/picopass_device.h deleted file mode 100644 index b45df346c..000000000 --- a/applications/external/picopass/picopass_device.h +++ /dev/null @@ -1,117 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "rfal_picopass.h" -#include -#include -#include "helpers/iclass_elite_dict.h" - -#define PICOPASS_DEV_NAME_MAX_LEN 22 -#define PICOPASS_READER_DATA_MAX_SIZE 64 -#define PICOPASS_BLOCK_LEN 8 -#define PICOPASS_MAX_APP_LIMIT 32 - -#define PICOPASS_CSN_BLOCK_INDEX 0 -#define PICOPASS_CONFIG_BLOCK_INDEX 1 -#define PICOPASS_EPURSE_BLOCK_INDEX 2 -#define PICOPASS_KD_BLOCK_INDEX 3 -#define PICOPASS_KC_BLOCK_INDEX 4 -#define PICOPASS_AIA_BLOCK_INDEX 5 -#define PICOPASS_PACS_CFG_BLOCK_INDEX 6 - -#define PICOPASS_APP_EXTENSION ".picopass" -#define PICOPASS_APP_SHADOW_EXTENSION ".pas" - -#define PICOPASS_DICT_KEY_BATCH_SIZE 10 - -typedef void (*PicopassLoadingCallback)(void* context, bool state); - -typedef struct { - IclassEliteDict* dict; - IclassEliteDictType type; - uint8_t current_sector; -} IclassEliteDictAttackData; - -typedef enum { - PicopassDeviceEncryptionUnknown = 0, - PicopassDeviceEncryptionNone = 0x14, - PicopassDeviceEncryptionDES = 0x15, - PicopassDeviceEncryption3DES = 0x17, -} PicopassEncryption; - -typedef enum { - PicopassDeviceSaveFormatHF, - PicopassDeviceSaveFormatLF, -} PicopassDeviceSaveFormat; - -typedef struct { - bool valid; - uint8_t bitLength; - uint8_t FacilityCode; - uint16_t CardNumber; -} PicopassWiegandRecord; - -typedef struct { - bool legacy; - bool se_enabled; - bool sio; - bool biometrics; - uint8_t key[8]; - bool elite_kdf; - uint8_t pin_length; - PicopassEncryption encryption; - uint8_t credential[8]; - uint8_t pin0[8]; - uint8_t pin1[8]; - PicopassWiegandRecord record; -} PicopassPacs; - -typedef struct { - uint8_t data[PICOPASS_BLOCK_LEN]; -} PicopassBlock; - -typedef struct { - PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT]; - PicopassPacs pacs; - IclassEliteDictAttackData iclass_elite_dict_attack_data; -} PicopassDeviceData; - -typedef struct { - Storage* storage; - DialogsApp* dialogs; - PicopassDeviceData dev_data; - char dev_name[PICOPASS_DEV_NAME_MAX_LEN + 1]; - FuriString* load_path; - PicopassDeviceSaveFormat format; - PicopassLoadingCallback loading_cb; - void* loading_cb_ctx; -} PicopassDevice; - -PicopassDevice* picopass_device_alloc(); - -void picopass_device_free(PicopassDevice* picopass_dev); - -void picopass_device_set_name(PicopassDevice* dev, const char* name); - -bool picopass_device_save(PicopassDevice* dev, const char* dev_name); - -bool picopass_file_select(PicopassDevice* dev); - -void picopass_device_data_clear(PicopassDeviceData* dev_data); - -void picopass_device_clear(PicopassDevice* dev); - -bool picopass_device_delete(PicopassDevice* dev, bool use_load_path); - -void picopass_device_set_loading_callback( - PicopassDevice* dev, - PicopassLoadingCallback callback, - void* context); - -ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs); -ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record); diff --git a/applications/external/picopass/picopass_i.h b/applications/external/picopass/picopass_i.h deleted file mode 100644 index 9147cfa0c..000000000 --- a/applications/external/picopass/picopass_i.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -#include "picopass.h" -#include "picopass_worker.h" -#include "picopass_device.h" - -#include "rfal_picopass.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "scenes/picopass_scene.h" -#include "views/dict_attack.h" - -#include -#include -#include - -#define PICOPASS_TEXT_STORE_SIZE 128 - -enum PicopassCustomEvent { - // Reserve first 100 events for button types and indexes, starting from 0 - PicopassCustomEventReserved = 100, - - PicopassCustomEventViewExit, - PicopassCustomEventWorkerExit, - PicopassCustomEventByteInputDone, - PicopassCustomEventTextInputDone, - PicopassCustomEventDictAttackSkip, -}; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -struct Picopass { - PicopassWorker* worker; - ViewDispatcher* view_dispatcher; - Gui* gui; - NotificationApp* notifications; - SceneManager* scene_manager; - PicopassDevice* dev; - - char text_store[PICOPASS_TEXT_STORE_SIZE + 1]; - FuriString* text_box_store; - - // Common Views - Submenu* submenu; - Popup* popup; - Loading* loading; - TextInput* text_input; - Widget* widget; - DictAttack* dict_attack; -}; - -typedef enum { - PicopassViewMenu, - PicopassViewPopup, - PicopassViewLoading, - PicopassViewTextInput, - PicopassViewWidget, - PicopassViewDictAttack, -} PicopassView; - -Picopass* picopass_alloc(); - -void picopass_text_store_set(Picopass* picopass, const char* text, ...); - -void picopass_text_store_clear(Picopass* picopass); - -void picopass_blink_start(Picopass* picopass); - -void picopass_blink_stop(Picopass* picopass); - -void picopass_show_loading_popup(void* context, bool show); - -/** Check if memory is set to pattern - * - * @warning zero size will return false - * - * @param[in] data Pointer to the byte array - * @param[in] pattern The pattern - * @param[in] size The byte array size - * - * @return True if memory is set to pattern, false otherwise - */ -bool picopass_is_memset(const uint8_t* data, const uint8_t pattern, size_t size); diff --git a/applications/external/picopass/picopass_keys.c b/applications/external/picopass/picopass_keys.c deleted file mode 100644 index 43dfc6312..000000000 --- a/applications/external/picopass/picopass_keys.c +++ /dev/null @@ -1,8 +0,0 @@ -#include "picopass_keys.h" - -const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78}; -const uint8_t picopass_factory_credit_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00}; -const uint8_t picopass_factory_debit_key[] = {0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87}; -const uint8_t picopass_xice_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88}; -const uint8_t picopass_xicl_key[] = {0x20, 0x20, 0x66, 0x66, 0x66, 0x66, 0x88, 0x88}; -const uint8_t picopass_xics_key[] = {0x66, 0x66, 0x20, 0x20, 0x66, 0x66, 0x88, 0x88}; diff --git a/applications/external/picopass/picopass_keys.h b/applications/external/picopass/picopass_keys.h deleted file mode 100644 index 2b5dba661..000000000 --- a/applications/external/picopass/picopass_keys.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "picopass_device.h" - -extern const uint8_t picopass_iclass_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_factory_credit_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_factory_debit_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_xice_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_xicl_key[PICOPASS_BLOCK_LEN]; -extern const uint8_t picopass_xics_key[PICOPASS_BLOCK_LEN]; diff --git a/applications/external/picopass/picopass_worker.c b/applications/external/picopass/picopass_worker.c deleted file mode 100644 index 6301704ca..000000000 --- a/applications/external/picopass/picopass_worker.c +++ /dev/null @@ -1,752 +0,0 @@ -#include "picopass_worker_i.h" - -#include - -#define TAG "PicopassWorker" - -static void picopass_worker_enable_field() { - furi_hal_nfc_ll_txrx_on(); - furi_hal_nfc_exit_sleep(); - furi_hal_nfc_ll_poll(); -} - -static ReturnCode picopass_worker_disable_field(ReturnCode rc) { - furi_hal_nfc_ll_txrx_off(); - furi_hal_nfc_start_sleep(); - return rc; -} - -/***************************** Picopass Worker API *******************************/ - -PicopassWorker* picopass_worker_alloc() { - PicopassWorker* picopass_worker = malloc(sizeof(PicopassWorker)); - - // Worker thread attributes - picopass_worker->thread = - furi_thread_alloc_ex("PicopassWorker", 8 * 1024, picopass_worker_task, picopass_worker); - - picopass_worker->callback = NULL; - picopass_worker->context = NULL; - picopass_worker->storage = furi_record_open(RECORD_STORAGE); - - picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); - - return picopass_worker; -} - -void picopass_worker_free(PicopassWorker* picopass_worker) { - furi_assert(picopass_worker); - - furi_thread_free(picopass_worker->thread); - - furi_record_close(RECORD_STORAGE); - - free(picopass_worker); -} - -PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker) { - return picopass_worker->state; -} - -void picopass_worker_start( - PicopassWorker* picopass_worker, - PicopassWorkerState state, - PicopassDeviceData* dev_data, - PicopassWorkerCallback callback, - void* context) { - furi_assert(picopass_worker); - furi_assert(dev_data); - - picopass_worker->callback = callback; - picopass_worker->context = context; - picopass_worker->dev_data = dev_data; - picopass_worker_change_state(picopass_worker, state); - furi_thread_start(picopass_worker->thread); -} - -void picopass_worker_stop(PicopassWorker* picopass_worker) { - furi_assert(picopass_worker); - furi_assert(picopass_worker->thread); - - if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) { - picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop); - furi_thread_join(picopass_worker->thread); - } -} - -void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) { - picopass_worker->state = state; -} - -/***************************** Picopass Worker Thread *******************************/ - -ReturnCode picopass_detect_card(int timeout) { - UNUSED(timeout); - - ReturnCode err; - - err = rfalPicoPassPollerInitialize(); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d", err); - return err; - } - - err = rfalFieldOnAndStartGT(); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d", err); - return err; - } - - err = rfalPicoPassPollerCheckPresence(); - if(err != ERR_RF_COLLISION) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d", err); - return err; - } - - return ERR_NONE; -} - -ReturnCode picopass_read_preauth(PicopassBlock* AA1) { - rfalPicoPassIdentifyRes idRes; - rfalPicoPassSelectRes selRes; - - ReturnCode err; - - err = rfalPicoPassPollerIdentify(&idRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); - return err; - } - - err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err); - return err; - } - - memcpy(AA1[PICOPASS_CSN_BLOCK_INDEX].data, selRes.CSN, sizeof(selRes.CSN)); - FURI_LOG_D( - TAG, - "csn %02x%02x%02x%02x%02x%02x%02x%02x", - AA1[PICOPASS_CSN_BLOCK_INDEX].data[0], - AA1[PICOPASS_CSN_BLOCK_INDEX].data[1], - AA1[PICOPASS_CSN_BLOCK_INDEX].data[2], - AA1[PICOPASS_CSN_BLOCK_INDEX].data[3], - AA1[PICOPASS_CSN_BLOCK_INDEX].data[4], - AA1[PICOPASS_CSN_BLOCK_INDEX].data[5], - AA1[PICOPASS_CSN_BLOCK_INDEX].data[6], - AA1[PICOPASS_CSN_BLOCK_INDEX].data[7]); - - rfalPicoPassReadBlockRes cfg = {0}; - rfalPicoPassPollerReadBlock(PICOPASS_CONFIG_BLOCK_INDEX, &cfg); - memcpy(AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, cfg.data, sizeof(cfg.data)); - FURI_LOG_D( - TAG, - "config %02x%02x%02x%02x%02x%02x%02x%02x", - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0], - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[1], - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[2], - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[3], - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[4], - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[5], - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[6], - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7]); - - rfalPicoPassReadBlockRes aia; - rfalPicoPassPollerReadBlock(PICOPASS_AIA_BLOCK_INDEX, &aia); - memcpy(AA1[PICOPASS_AIA_BLOCK_INDEX].data, aia.data, sizeof(aia.data)); - FURI_LOG_D( - TAG, - "aia %02x%02x%02x%02x%02x%02x%02x%02x", - AA1[PICOPASS_AIA_BLOCK_INDEX].data[0], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[1], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[2], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[3], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[4], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[5], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[6], - AA1[PICOPASS_AIA_BLOCK_INDEX].data[7]); - - return ERR_NONE; -} - -static ReturnCode - picopass_auth_dict(PicopassWorker* picopass_worker, IclassEliteDictType dict_type) { - rfalPicoPassReadCheckRes rcRes; - rfalPicoPassCheckRes chkRes; - bool elite = (dict_type != IclassStandardDictTypeFlipper); - - PicopassDeviceData* dev_data = picopass_worker->dev_data; - PicopassBlock* AA1 = dev_data->AA1; - PicopassPacs* pacs = &dev_data->pacs; - - uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; - uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data; - - ReturnCode err = ERR_PARAM; - - uint8_t mac[4] = {0}; - uint8_t ccnr[12] = {0}; - - size_t index = 0; - uint8_t key[PICOPASS_BLOCK_LEN] = {0}; - - if(!iclass_elite_dict_check_presence(dict_type)) { - FURI_LOG_E(TAG, "Dictionary not found"); - return ERR_PARAM; - } - - IclassEliteDict* dict = iclass_elite_dict_alloc(dict_type); - if(!dict) { - FURI_LOG_E(TAG, "Dictionary not allocated"); - return ERR_PARAM; - } - - FURI_LOG_D(TAG, "Loaded %lu keys", iclass_elite_dict_get_total_keys(dict)); - while(iclass_elite_dict_get_next_key(dict, key)) { - FURI_LOG_D( - TAG, - "Try to %s auth with key %zu %02x%02x%02x%02x%02x%02x%02x%02x", - elite ? "elite" : "standard", - index++, - key[0], - key[1], - key[2], - key[3], - key[4], - key[5], - key[6], - key[7]); - - err = rfalPicoPassPollerReadCheck(&rcRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); - break; - } - memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - - loclass_iclass_calc_div_key(csn, key, div_key, elite); - loclass_opt_doReaderMAC(ccnr, div_key, mac); - - err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err == ERR_NONE) { - memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); - break; - } - - if(picopass_worker->state != PicopassWorkerStateDetect) break; - } - - iclass_elite_dict_free(dict); - - return err; -} - -ReturnCode picopass_auth(PicopassWorker* picopass_worker) { - ReturnCode err; - - FURI_LOG_I(TAG, "Starting system dictionary attack [Standard KDF]"); - err = picopass_auth_dict(picopass_worker, IclassStandardDictTypeFlipper); - if(err == ERR_NONE) { - return ERR_NONE; - } - - FURI_LOG_I(TAG, "Starting user dictionary attack [Elite KDF]"); - err = picopass_auth_dict(picopass_worker, IclassEliteDictTypeUser); - if(err == ERR_NONE) { - return ERR_NONE; - } - - FURI_LOG_I(TAG, "Starting system dictionary attack [Elite KDF]"); - err = picopass_auth_dict(picopass_worker, IclassEliteDictTypeFlipper); - if(err == ERR_NONE) { - return ERR_NONE; - } - - return err; -} - -ReturnCode picopass_read_card(PicopassBlock* AA1) { - ReturnCode err; - - size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] < PICOPASS_MAX_APP_LIMIT ? - AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] : - PICOPASS_MAX_APP_LIMIT; - - for(size_t i = 2; i < app_limit; i++) { - if(i == PICOPASS_KD_BLOCK_INDEX) { - // Skip over Kd block which is populated earlier (READ of Kd returns all FF's) - continue; - } - - rfalPicoPassReadBlockRes block; - err = rfalPicoPassPollerReadBlock(i, &block); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err); - return err; - } - - FURI_LOG_D( - TAG, - "rfalPicoPassPollerReadBlock %d %02x%02x%02x%02x%02x%02x%02x%02x", - i, - block.data[0], - block.data[1], - block.data[2], - block.data[3], - block.data[4], - block.data[5], - block.data[6], - block.data[7]); - - memcpy(AA1[i].data, block.data, sizeof(block.data)); - } - - return ERR_NONE; -} - -ReturnCode picopass_write_card(PicopassBlock* AA1) { - rfalPicoPassIdentifyRes idRes; - rfalPicoPassSelectRes selRes; - rfalPicoPassReadCheckRes rcRes; - rfalPicoPassCheckRes chkRes; - - ReturnCode err; - - uint8_t div_key[8] = {0}; - uint8_t mac[4] = {0}; - uint8_t ccnr[12] = {0}; - - err = rfalPicoPassPollerIdentify(&idRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); - return err; - } - - err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err); - return err; - } - - err = rfalPicoPassPollerReadCheck(&rcRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); - return err; - } - memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - - loclass_iclass_calc_div_key(selRes.CSN, (uint8_t*)picopass_iclass_key, div_key, false); - loclass_opt_doReaderMAC(ccnr, div_key, mac); - - err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); - return err; - } - - for(size_t i = 6; i < 10; i++) { - FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", i); - uint8_t data[9] = {0}; - data[0] = i; - memcpy(data + 1, AA1[i].data, RFAL_PICOPASS_MAX_BLOCK_LEN); - loclass_doMAC_N(data, sizeof(data), div_key, mac); - FURI_LOG_D( - TAG, - "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x", - i, - data[1], - data[2], - data[3], - data[4], - data[5], - data[6], - data[7], - data[8], - mac[0], - mac[1], - mac[2], - mac[3]); - - err = rfalPicoPassPollerWriteBlock(i, AA1[i].data, mac); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err); - return err; - } - } - - return ERR_NONE; -} - -ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* newBlock) { - rfalPicoPassIdentifyRes idRes; - rfalPicoPassSelectRes selRes; - rfalPicoPassReadCheckRes rcRes; - rfalPicoPassCheckRes chkRes; - - ReturnCode err; - - uint8_t mac[4] = {0}; - uint8_t ccnr[12] = {0}; - - err = rfalPicoPassPollerIdentify(&idRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d", err); - return err; - } - - err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d", err); - return err; - } - - err = rfalPicoPassPollerReadCheck(&rcRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); - return err; - } - memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - - if(memcmp(selRes.CSN, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN) != 0) { - FURI_LOG_E(TAG, "Wrong CSN for write"); - return ERR_REQUEST; - } - - loclass_opt_doReaderMAC(ccnr, AA1[PICOPASS_KD_BLOCK_INDEX].data, mac); - err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); - return err; - } - - FURI_LOG_D(TAG, "rfalPicoPassPollerWriteBlock %d", blockNo); - uint8_t data[9] = { - blockNo, - newBlock[0], - newBlock[1], - newBlock[2], - newBlock[3], - newBlock[4], - newBlock[5], - newBlock[6], - newBlock[7]}; - loclass_doMAC_N(data, sizeof(data), AA1[PICOPASS_KD_BLOCK_INDEX].data, mac); - FURI_LOG_D( - TAG, - "loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x", - blockNo, - data[1], - data[2], - data[3], - data[4], - data[5], - data[6], - data[7], - data[8], - mac[0], - mac[1], - mac[2], - mac[3]); - - err = rfalPicoPassPollerWriteBlock(data[0], data + 1, mac); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerWriteBlock error %d", err); - return err; - } - - return ERR_NONE; -} - -void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { - furi_assert(picopass_worker); - furi_assert(picopass_worker->callback); - - picopass_device_data_clear(picopass_worker->dev_data); - PicopassDeviceData* dev_data = picopass_worker->dev_data; - PicopassBlock* AA1 = dev_data->AA1; - PicopassPacs* pacs = &dev_data->pacs; - - for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { - memset(AA1[i].data, 0, sizeof(AA1[i].data)); - } - memset(pacs, 0, sizeof(PicopassPacs)); - - IclassEliteDictAttackData* dict_attack_data = - &picopass_worker->dev_data->iclass_elite_dict_attack_data; - bool elite = (dict_attack_data->type != IclassStandardDictTypeFlipper); - - rfalPicoPassReadCheckRes rcRes; - rfalPicoPassCheckRes chkRes; - - ReturnCode err; - uint8_t mac[4] = {0}; - uint8_t ccnr[12] = {0}; - - size_t index = 0; - uint8_t key[PICOPASS_BLOCK_LEN] = {0}; - - // Load dictionary - IclassEliteDict* dict = dict_attack_data->dict; - if(!dict) { - FURI_LOG_E(TAG, "Dictionary not found"); - picopass_worker->callback(PicopassWorkerEventNoDictFound, picopass_worker->context); - return; - } - - do { - if(picopass_detect_card(1000) == ERR_NONE) { - picopass_worker->callback(PicopassWorkerEventCardDetected, picopass_worker->context); - - // Process first found device - err = picopass_read_preauth(AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_read_preauth error %d", err); - picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); - return; - } - - // Thank you proxmark! - pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8); - pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); - if(pacs->se_enabled) { - FURI_LOG_D(TAG, "SE enabled"); - picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); - return; - } - - break; - } else { - picopass_worker->callback(PicopassWorkerEventNoCardDetected, picopass_worker->context); - } - if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break; - - furi_delay_ms(100); - } while(true); - - FURI_LOG_D( - TAG, "Start Dictionary attack, Key Count %lu", iclass_elite_dict_get_total_keys(dict)); - while(iclass_elite_dict_get_next_key(dict, key)) { - FURI_LOG_T(TAG, "Key %zu", index); - if(++index % PICOPASS_DICT_KEY_BATCH_SIZE == 0) { - picopass_worker->callback( - PicopassWorkerEventNewDictKeyBatch, picopass_worker->context); - } - - err = rfalPicoPassPollerReadCheck(&rcRes); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); - break; - } - memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 - - uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; - uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data; - - loclass_iclass_calc_div_key(csn, key, div_key, elite); - loclass_opt_doReaderMAC(ccnr, div_key, mac); - - err = rfalPicoPassPollerCheck(mac, &chkRes); - if(err == ERR_NONE) { - FURI_LOG_I(TAG, "Found key"); - memcpy(pacs->key, key, PICOPASS_BLOCK_LEN); - pacs->elite_kdf = elite; - err = picopass_read_card(AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_read_card error %d", err); - picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); - break; - } - - err = picopass_device_parse_credential(AA1, pacs); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); - picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); - break; - } - - err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); - picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); - break; - } - picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); - break; - } - - if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break; - } - FURI_LOG_D(TAG, "Dictionary complete"); - if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { - picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context); - } else { - picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context); - } -} - -int32_t picopass_worker_task(void* context) { - PicopassWorker* picopass_worker = context; - - picopass_worker_enable_field(); - if(picopass_worker->state == PicopassWorkerStateDetect) { - picopass_worker_detect(picopass_worker); - } else if(picopass_worker->state == PicopassWorkerStateWrite) { - picopass_worker_write(picopass_worker); - } else if(picopass_worker->state == PicopassWorkerStateWriteKey) { - picopass_worker_write_key(picopass_worker); - } else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) { - picopass_worker_elite_dict_attack(picopass_worker); - } else if(picopass_worker->state == PicopassWorkerStateStop) { - FURI_LOG_D(TAG, "Worker state stop"); - // no-op - } else { - FURI_LOG_W(TAG, "Unknown state %d", picopass_worker->state); - } - picopass_worker_disable_field(ERR_NONE); - picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady); - - return 0; -} - -void picopass_worker_detect(PicopassWorker* picopass_worker) { - picopass_device_data_clear(picopass_worker->dev_data); - PicopassDeviceData* dev_data = picopass_worker->dev_data; - - PicopassBlock* AA1 = dev_data->AA1; - PicopassPacs* pacs = &dev_data->pacs; - ReturnCode err; - - // reset device data - for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) { - memset(AA1[i].data, 0, sizeof(AA1[i].data)); - } - memset(pacs, 0, sizeof(PicopassPacs)); - - PicopassWorkerEvent nextState = PicopassWorkerEventSuccess; - - while(picopass_worker->state == PicopassWorkerStateDetect) { - if(picopass_detect_card(1000) == ERR_NONE) { - // Process first found device - err = picopass_read_preauth(AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_read_preauth error %d", err); - nextState = PicopassWorkerEventFail; - } - - // Thank you proxmark! - pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8); - pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); - if(pacs->se_enabled) { - FURI_LOG_D(TAG, "SE enabled"); - nextState = PicopassWorkerEventFail; - } - - if(nextState == PicopassWorkerEventSuccess) { - err = picopass_auth(picopass_worker); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_try_auth error %d", err); - nextState = PicopassWorkerEventFail; - } - } - - if(nextState == PicopassWorkerEventSuccess) { - err = picopass_read_card(AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_read_card error %d", err); - nextState = PicopassWorkerEventFail; - } - } - - if(nextState == PicopassWorkerEventSuccess) { - err = picopass_device_parse_credential(AA1, pacs); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err); - nextState = PicopassWorkerEventFail; - } - } - - if(nextState == PicopassWorkerEventSuccess) { - err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); - nextState = PicopassWorkerEventFail; - } - } - - // Notify caller and exit - if(picopass_worker->callback) { - picopass_worker->callback(nextState, picopass_worker->context); - } - break; - } - furi_delay_ms(100); - } -} - -void picopass_worker_write(PicopassWorker* picopass_worker) { - PicopassDeviceData* dev_data = picopass_worker->dev_data; - PicopassBlock* AA1 = dev_data->AA1; - ReturnCode err; - PicopassWorkerEvent nextState = PicopassWorkerEventSuccess; - - while(picopass_worker->state == PicopassWorkerStateWrite) { - if(picopass_detect_card(1000) == ERR_NONE) { - err = picopass_write_card(AA1); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_write_card error %d", err); - nextState = PicopassWorkerEventFail; - } - - // Notify caller and exit - if(picopass_worker->callback) { - picopass_worker->callback(nextState, picopass_worker->context); - } - break; - } - furi_delay_ms(100); - } -} - -void picopass_worker_write_key(PicopassWorker* picopass_worker) { - PicopassDeviceData* dev_data = picopass_worker->dev_data; - PicopassBlock* AA1 = dev_data->AA1; - PicopassPacs* pacs = &dev_data->pacs; - ReturnCode err; - PicopassWorkerEvent nextState = PicopassWorkerEventSuccess; - - uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data; - uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; - uint8_t fuses = configBlock[7]; - uint8_t* oldKey = AA1[PICOPASS_KD_BLOCK_INDEX].data; - - uint8_t newKey[PICOPASS_BLOCK_LEN] = {0}; - loclass_iclass_calc_div_key(csn, pacs->key, newKey, pacs->elite_kdf); - - if((fuses & 0x80) == 0x80) { - FURI_LOG_D(TAG, "Plain write for personalized mode key change"); - } else { - FURI_LOG_D(TAG, "XOR write for application mode key change"); - // XOR when in application mode - for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { - newKey[i] ^= oldKey[i]; - } - } - - while(picopass_worker->state == PicopassWorkerStateWriteKey) { - if(picopass_detect_card(1000) == ERR_NONE) { - err = picopass_write_block(AA1, PICOPASS_KD_BLOCK_INDEX, newKey); - if(err != ERR_NONE) { - FURI_LOG_E(TAG, "picopass_write_block error %d", err); - nextState = PicopassWorkerEventFail; - } - - // Notify caller and exit - if(picopass_worker->callback) { - picopass_worker->callback(nextState, picopass_worker->context); - } - break; - } - furi_delay_ms(100); - } -} diff --git a/applications/external/picopass/picopass_worker.h b/applications/external/picopass/picopass_worker.h deleted file mode 100644 index e9d37481b..000000000 --- a/applications/external/picopass/picopass_worker.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "picopass_device.h" -#include "picopass_keys.h" - -typedef struct PicopassWorker PicopassWorker; - -typedef enum { - // Init states - PicopassWorkerStateNone, - PicopassWorkerStateBroken, - PicopassWorkerStateReady, - // Main worker states - PicopassWorkerStateDetect, - PicopassWorkerStateWrite, - PicopassWorkerStateWriteKey, - PicopassWorkerStateEliteDictAttack, - // Transition - PicopassWorkerStateStop, -} PicopassWorkerState; - -typedef enum { - // Reserve first 50 events for application events - PicopassWorkerEventReserved = 50, - - // Picopass worker common events - PicopassWorkerEventSuccess, - PicopassWorkerEventFail, - PicopassWorkerEventNoCardDetected, - PicopassWorkerEventSeEnabled, - PicopassWorkerEventAborted, - PicopassWorkerEventCardDetected, - PicopassWorkerEventNewDictKeyBatch, - PicopassWorkerEventNoDictFound, -} PicopassWorkerEvent; - -typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context); - -PicopassWorker* picopass_worker_alloc(); - -PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker); - -void picopass_worker_free(PicopassWorker* picopass_worker); - -void picopass_worker_start( - PicopassWorker* picopass_worker, - PicopassWorkerState state, - PicopassDeviceData* dev_data, - PicopassWorkerCallback callback, - void* context); - -void picopass_worker_stop(PicopassWorker* picopass_worker); diff --git a/applications/external/picopass/picopass_worker_i.h b/applications/external/picopass/picopass_worker_i.h deleted file mode 100644 index f41cfce45..000000000 --- a/applications/external/picopass/picopass_worker_i.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "picopass_worker.h" -#include "picopass_i.h" - -#include -#include - -#include - -#include -#include - -#include - -struct PicopassWorker { - FuriThread* thread; - Storage* storage; - Stream* dict_stream; - - PicopassDeviceData* dev_data; - PicopassWorkerCallback callback; - void* context; - - PicopassWorkerState state; -}; - -void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state); - -int32_t picopass_worker_task(void* context); - -void picopass_worker_detect(PicopassWorker* picopass_worker); -void picopass_worker_write(PicopassWorker* picopass_worker); -void picopass_worker_write_key(PicopassWorker* picopass_worker); diff --git a/applications/external/picopass/rfal_picopass.c b/applications/external/picopass/rfal_picopass.c deleted file mode 100644 index ac66cb92d..000000000 --- a/applications/external/picopass/rfal_picopass.c +++ /dev/null @@ -1,214 +0,0 @@ -#include "rfal_picopass.h" - -#define RFAL_PICOPASS_TXRX_FLAGS \ - (FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | \ - FURI_HAL_NFC_LL_TXRX_FLAGS_PAR_RX_REMV | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP) - -#define TAG "RFAL_PICOPASS" - -typedef struct { - uint8_t CMD; - uint8_t CSN[RFAL_PICOPASS_UID_LEN]; -} rfalPicoPassSelectReq; - -typedef struct { - uint8_t CMD; - uint8_t null[4]; - uint8_t mac[4]; -} rfalPicoPassCheckReq; - -static uint16_t rfalPicoPassUpdateCcitt(uint16_t crcSeed, uint8_t dataByte) { - uint16_t crc = crcSeed; - uint8_t dat = dataByte; - - dat ^= (uint8_t)(crc & 0xFFU); - dat ^= (dat << 4); - - crc = (crc >> 8) ^ (((uint16_t)dat) << 8) ^ (((uint16_t)dat) << 3) ^ (((uint16_t)dat) >> 4); - - return crc; -} - -static uint16_t - rfalPicoPassCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length) { - uint16_t crc = preloadValue; - uint16_t index; - - for(index = 0; index < length; index++) { - crc = rfalPicoPassUpdateCcitt(crc, buf[index]); - } - - return crc; -} - -FuriHalNfcReturn rfalPicoPassPollerInitialize(void) { - FuriHalNfcReturn ret; - - ret = furi_hal_nfc_ll_set_mode( - FuriHalNfcModePollPicopass, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48); - if(ret != FuriHalNfcReturnOk) { - return ret; - }; - - furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc); - furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_PICOPASS); - furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_PICOPASS_POLLER); - furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_PICOPASS_POLLER); - - return FuriHalNfcReturnOk; -} - -FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void) { - FuriHalNfcReturn ret; - uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_ACTALL}; - uint8_t rxBuf[32] = {0}; - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); - - ret = furi_hal_nfc_ll_txrx(txBuf, 1, rxBuf, 32, &recvLen, flags, fwt); - return ret; -} - -FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes) { - FuriHalNfcReturn ret; - - uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_IDENTIFY}; - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); - - ret = furi_hal_nfc_ll_txrx( - txBuf, - sizeof(txBuf), - (uint8_t*)idRes, - sizeof(rfalPicoPassIdentifyRes), - &recvLen, - flags, - fwt); - // printf("identify rx: %d %s\n", recvLen, hex2Str(idRes->CSN, RFAL_PICOPASS_UID_LEN)); - - return ret; -} - -FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes) { - FuriHalNfcReturn ret; - - rfalPicoPassSelectReq selReq; - selReq.CMD = RFAL_PICOPASS_CMD_SELECT; - memcpy(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN); - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); - - ret = furi_hal_nfc_ll_txrx( - (uint8_t*)&selReq, - sizeof(rfalPicoPassSelectReq), - (uint8_t*)selRes, - sizeof(rfalPicoPassSelectRes), - &recvLen, - flags, - fwt); - // printf("select rx: %d %s\n", recvLen, hex2Str(selRes->CSN, RFAL_PICOPASS_UID_LEN)); - if(ret == FuriHalNfcReturnTimeout) { - return FuriHalNfcReturnOk; - } - - return ret; -} - -FuriHalNfcReturn rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes) { - FuriHalNfcReturn ret; - uint8_t txBuf[2] = {RFAL_PICOPASS_CMD_READCHECK, 0x02}; - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); - - ret = furi_hal_nfc_ll_txrx( - txBuf, - sizeof(txBuf), - (uint8_t*)rcRes, - sizeof(rfalPicoPassReadCheckRes), - &recvLen, - flags, - fwt); - // printf("readcheck rx: %d %s\n", recvLen, hex2Str(rcRes->CCNR, 8)); - - if(ret == FuriHalNfcReturnCrc) { - return FuriHalNfcReturnOk; - } - - return ret; -} - -FuriHalNfcReturn rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chkRes) { - FuriHalNfcReturn ret; - rfalPicoPassCheckReq chkReq; - chkReq.CMD = RFAL_PICOPASS_CMD_CHECK; - memcpy(chkReq.mac, mac, 4); - memset(chkReq.null, 0, 4); - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); - - // printf("check tx: %s\n", hex2Str((uint8_t *)&chkReq, sizeof(rfalPicoPassCheckReq))); - ret = furi_hal_nfc_ll_txrx( - (uint8_t*)&chkReq, - sizeof(rfalPicoPassCheckReq), - (uint8_t*)chkRes, - sizeof(rfalPicoPassCheckRes), - &recvLen, - flags, - fwt); - // printf("check rx: %d %s\n", recvLen, hex2Str(chkRes->mac, 4)); - if(ret == FuriHalNfcReturnCrc) { - return FuriHalNfcReturnOk; - } - - return ret; -} - -FuriHalNfcReturn rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes) { - FuriHalNfcReturn ret; - - uint8_t txBuf[4] = {RFAL_PICOPASS_CMD_READ, 0, 0, 0}; - txBuf[1] = blockNum; - uint16_t crc = rfalPicoPassCalculateCcitt(0xE012, txBuf + 1, 1); - memcpy(txBuf + 2, &crc, sizeof(uint16_t)); - - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); - - ret = furi_hal_nfc_ll_txrx( - txBuf, - sizeof(txBuf), - (uint8_t*)readRes, - sizeof(rfalPicoPassReadBlockRes), - &recvLen, - flags, - fwt); - return ret; -} - -FuriHalNfcReturn rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]) { - FuriHalNfcReturn ret; - - uint8_t txBuf[14] = {RFAL_PICOPASS_CMD_WRITE, blockNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - memcpy(txBuf + 2, data, RFAL_PICOPASS_MAX_BLOCK_LEN); - memcpy(txBuf + 10, mac, 4); - - uint16_t recvLen = 0; - uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS; - uint32_t fwt = furi_hal_nfc_ll_ms2fc(20); - rfalPicoPassReadBlockRes block; - - ret = furi_hal_nfc_ll_txrx( - txBuf, sizeof(txBuf), (uint8_t*)&block, sizeof(block), &recvLen, flags, fwt); - - if(ret == FuriHalNfcReturnOk) { - // TODO: compare response - } - - return ret; -} diff --git a/applications/external/picopass/rfal_picopass.h b/applications/external/picopass/rfal_picopass.h deleted file mode 100644 index 6926b2a79..000000000 --- a/applications/external/picopass/rfal_picopass.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include - -#define RFAL_PICOPASS_UID_LEN 8 -#define RFAL_PICOPASS_MAX_BLOCK_LEN 8 - -enum { - RFAL_PICOPASS_CMD_ACTALL = 0x0A, - RFAL_PICOPASS_CMD_IDENTIFY = 0x0C, - RFAL_PICOPASS_CMD_SELECT = 0x81, - RFAL_PICOPASS_CMD_READCHECK = 0x88, - RFAL_PICOPASS_CMD_CHECK = 0x05, - RFAL_PICOPASS_CMD_READ = 0x0C, - RFAL_PICOPASS_CMD_WRITE = 0x87, -}; - -typedef struct { - uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Anti-collision CSN - uint8_t crc[2]; -} rfalPicoPassIdentifyRes; - -typedef struct { - uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Real CSN - uint8_t crc[2]; -} rfalPicoPassSelectRes; - -typedef struct { - uint8_t CCNR[8]; -} rfalPicoPassReadCheckRes; - -typedef struct { - uint8_t mac[4]; -} rfalPicoPassCheckRes; - -typedef struct { - uint8_t data[RFAL_PICOPASS_MAX_BLOCK_LEN]; - uint8_t crc[2]; -} rfalPicoPassReadBlockRes; - -FuriHalNfcReturn rfalPicoPassPollerInitialize(void); -FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void); -FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes); -FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes); -FuriHalNfcReturn rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes); -FuriHalNfcReturn rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chkRes); -FuriHalNfcReturn rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes); -FuriHalNfcReturn rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]); diff --git a/applications/external/picopass/scenes/picopass_scene.c b/applications/external/picopass/scenes/picopass_scene.c deleted file mode 100644 index 61bd5e8fe..000000000 --- a/applications/external/picopass/scenes/picopass_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "picopass_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const picopass_on_enter_handlers[])(void*) = { -#include "picopass_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const picopass_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "picopass_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const picopass_on_exit_handlers[])(void* context) = { -#include "picopass_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers picopass_scene_handlers = { - .on_enter_handlers = picopass_on_enter_handlers, - .on_event_handlers = picopass_on_event_handlers, - .on_exit_handlers = picopass_on_exit_handlers, - .scene_num = PicopassSceneNum, -}; diff --git a/applications/external/picopass/scenes/picopass_scene.h b/applications/external/picopass/scenes/picopass_scene.h deleted file mode 100644 index 2faa80b12..000000000 --- a/applications/external/picopass/scenes/picopass_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) PicopassScene##id, -typedef enum { -#include "picopass_scene_config.h" - PicopassSceneNum, -} PicopassScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers picopass_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "picopass_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "picopass_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "picopass_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/picopass/scenes/picopass_scene_card_menu.c b/applications/external/picopass/scenes/picopass_scene_card_menu.c deleted file mode 100644 index fe63f7c86..000000000 --- a/applications/external/picopass/scenes/picopass_scene_card_menu.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "../picopass_i.h" - -enum SubmenuIndex { - SubmenuIndexSave, - SubmenuIndexSaveAsLF, - SubmenuIndexChangeKey, -}; - -void picopass_scene_card_menu_submenu_callback(void* context, uint32_t index) { - Picopass* picopass = context; - - view_dispatcher_send_custom_event(picopass->view_dispatcher, index); -} - -void picopass_scene_card_menu_on_enter(void* context) { - Picopass* picopass = context; - Submenu* submenu = picopass->submenu; - - submenu_add_item( - submenu, "Save", SubmenuIndexSave, picopass_scene_card_menu_submenu_callback, picopass); - if(picopass->dev->dev_data.pacs.record.valid) { - submenu_add_item( - submenu, - "Save as LF", - SubmenuIndexSaveAsLF, - picopass_scene_card_menu_submenu_callback, - picopass); - } - submenu_add_item( - submenu, - "Change Key", - SubmenuIndexChangeKey, - picopass_scene_card_menu_submenu_callback, - picopass); - - submenu_set_selected_item( - picopass->submenu, - scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneCardMenu)); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); -} - -bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSave) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexSave); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); - picopass->dev->format = PicopassDeviceSaveFormatHF; - consumed = true; - } else if(event.event == SubmenuIndexSaveAsLF) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexSaveAsLF); - picopass->dev->format = PicopassDeviceSaveFormatLF; - scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveName); - consumed = true; - } else if(event.event == SubmenuIndexChangeKey) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexChangeKey); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneKeyMenu); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneStart); - } - - return consumed; -} - -void picopass_scene_card_menu_on_exit(void* context) { - Picopass* picopass = context; - - submenu_reset(picopass->submenu); -} diff --git a/applications/external/picopass/scenes/picopass_scene_config.h b/applications/external/picopass/scenes/picopass_scene_config.h deleted file mode 100644 index 8ea970498..000000000 --- a/applications/external/picopass/scenes/picopass_scene_config.h +++ /dev/null @@ -1,17 +0,0 @@ -ADD_SCENE(picopass, start, Start) -ADD_SCENE(picopass, read_card, ReadCard) -ADD_SCENE(picopass, read_card_success, ReadCardSuccess) -ADD_SCENE(picopass, card_menu, CardMenu) -ADD_SCENE(picopass, save_name, SaveName) -ADD_SCENE(picopass, save_success, SaveSuccess) -ADD_SCENE(picopass, saved_menu, SavedMenu) -ADD_SCENE(picopass, file_select, FileSelect) -ADD_SCENE(picopass, device_info, DeviceInfo) -ADD_SCENE(picopass, delete, Delete) -ADD_SCENE(picopass, delete_success, DeleteSuccess) -ADD_SCENE(picopass, write_card, WriteCard) -ADD_SCENE(picopass, write_card_success, WriteCardSuccess) -ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess) -ADD_SCENE(picopass, write_key, WriteKey) -ADD_SCENE(picopass, key_menu, KeyMenu) -ADD_SCENE(picopass, elite_dict_attack, EliteDictAttack) diff --git a/applications/external/picopass/scenes/picopass_scene_delete.c b/applications/external/picopass/scenes/picopass_scene_delete.c deleted file mode 100644 index fb23cb5d4..000000000 --- a/applications/external/picopass/scenes/picopass_scene_delete.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../picopass_i.h" - -void picopass_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) { - Picopass* picopass = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(picopass->view_dispatcher, result); - } -} - -void picopass_scene_delete_on_enter(void* context) { - Picopass* picopass = context; - - // Setup Custom Widget view - char temp_str[64]; - snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", picopass->dev->dev_name); - widget_add_text_box_element( - picopass->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false); - widget_add_button_element( - picopass->widget, - GuiButtonTypeLeft, - "Back", - picopass_scene_delete_widget_callback, - picopass); - widget_add_button_element( - picopass->widget, - GuiButtonTypeRight, - "Delete", - picopass_scene_delete_widget_callback, - picopass); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); -} - -bool picopass_scene_delete_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - return scene_manager_previous_scene(picopass->scene_manager); - } else if(event.event == GuiButtonTypeRight) { - if(picopass_device_delete(picopass->dev, true)) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneDeleteSuccess); - } else { - scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneStart); - } - consumed = true; - } - } - return consumed; -} - -void picopass_scene_delete_on_exit(void* context) { - Picopass* picopass = context; - - widget_reset(picopass->widget); -} diff --git a/applications/external/picopass/scenes/picopass_scene_delete_success.c b/applications/external/picopass/scenes/picopass_scene_delete_success.c deleted file mode 100644 index f2a36a7fb..000000000 --- a/applications/external/picopass/scenes/picopass_scene_delete_success.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "../picopass_i.h" - -void picopass_scene_delete_success_popup_callback(void* context) { - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventViewExit); -} - -void picopass_scene_delete_success_on_enter(void* context) { - Picopass* picopass = context; - - // Setup view - Popup* popup = picopass->popup; - popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); - popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, picopass); - popup_set_callback(popup, picopass_scene_delete_success_popup_callback); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); -} - -bool picopass_scene_delete_success_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassCustomEventViewExit) { - consumed = scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneStart); - } - } - return consumed; -} - -void picopass_scene_delete_success_on_exit(void* context) { - Picopass* picopass = context; - - // Clear view - popup_reset(picopass->popup); -} diff --git a/applications/external/picopass/scenes/picopass_scene_device_info.c b/applications/external/picopass/scenes/picopass_scene_device_info.c deleted file mode 100644 index bb149aa6b..000000000 --- a/applications/external/picopass/scenes/picopass_scene_device_info.c +++ /dev/null @@ -1,114 +0,0 @@ -#include "../picopass_i.h" -#include - -void picopass_scene_device_info_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - Picopass* picopass = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(picopass->view_dispatcher, result); - } -} - -void picopass_scene_device_info_on_enter(void* context) { - Picopass* picopass = context; - - FuriString* csn_str = furi_string_alloc_set("CSN:"); - FuriString* credential_str = furi_string_alloc(); - FuriString* wiegand_str = furi_string_alloc(); - FuriString* sio_str = furi_string_alloc(); - - dolphin_deed(DolphinDeedNfcReadSuccess); - - // Setup view - PicopassBlock* AA1 = picopass->dev->dev_data.AA1; - PicopassPacs* pacs = &picopass->dev->dev_data.pacs; - Widget* widget = picopass->widget; - - uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; - memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); - for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(csn_str, "%02X ", csn[i]); - } - - if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { - // Neither of these are valid. Indicates the block was all 0x00 or all 0xff - furi_string_cat_printf(wiegand_str, "Invalid PACS"); - } else { - size_t bytesLength = pacs->record.bitLength / 8; - if(pacs->record.bitLength % 8 > 0) { - // Add extra byte if there are bits remaining - bytesLength++; - } - furi_string_set(credential_str, ""); - for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); - } - - if(pacs->record.valid) { - furi_string_cat_printf( - wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); - } else { - furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); - } - - if(pacs->sio) { - furi_string_cat_printf(sio_str, "+SIO"); - } - } - - widget_add_string_element( - widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); - widget_add_string_element( - widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); - widget_add_string_element( - widget, - 64, - 36, - AlignCenter, - AlignCenter, - FontSecondary, - furi_string_get_cstr(credential_str)); - widget_add_string_element( - widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); - - furi_string_free(csn_str); - furi_string_free(credential_str); - furi_string_free(wiegand_str); - furi_string_free(sio_str); - - widget_add_button_element( - picopass->widget, - GuiButtonTypeLeft, - "Back", - picopass_scene_device_info_widget_callback, - picopass); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); -} - -bool picopass_scene_device_info_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(picopass->scene_manager); - } else if(event.event == PicopassCustomEventViewExit) { - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); - consumed = true; - } - return consumed; -} - -void picopass_scene_device_info_on_exit(void* context) { - Picopass* picopass = context; - - // Clear views - widget_reset(picopass->widget); -} diff --git a/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c b/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c deleted file mode 100644 index e6191d5ba..000000000 --- a/applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c +++ /dev/null @@ -1,172 +0,0 @@ -#include "../picopass_i.h" -#include - -#define TAG "IclassEliteDictAttack" - -typedef enum { - DictAttackStateIdle, - DictAttackStateUserDictInProgress, - DictAttackStateFlipperDictInProgress, - DictAttackStateStandardDictInProgress, -} DictAttackState; - -void picopass_dict_attack_worker_callback(PicopassWorkerEvent event, void* context) { - furi_assert(context); - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, event); -} - -void picopass_dict_attack_result_callback(void* context) { - furi_assert(context); - Picopass* picopass = context; - view_dispatcher_send_custom_event( - picopass->view_dispatcher, PicopassCustomEventDictAttackSkip); -} - -static void - picopass_scene_elite_dict_attack_prepare_view(Picopass* picopass, DictAttackState state) { - IclassEliteDictAttackData* dict_attack_data = - &picopass->dev->dev_data.iclass_elite_dict_attack_data; - PicopassWorkerState worker_state = PicopassWorkerStateReady; - IclassEliteDict* dict = NULL; - - // Identify scene state - if(state == DictAttackStateIdle) { - if(iclass_elite_dict_check_presence(IclassEliteDictTypeUser)) { - FURI_LOG_D(TAG, "Starting with user dictionary"); - state = DictAttackStateUserDictInProgress; - } else { - FURI_LOG_D(TAG, "Starting with standard dictionary"); - state = DictAttackStateStandardDictInProgress; - } - } else if(state == DictAttackStateUserDictInProgress) { - FURI_LOG_D(TAG, "Moving from user dictionary to standard dictionary"); - state = DictAttackStateStandardDictInProgress; - } else if(state == DictAttackStateStandardDictInProgress) { - FURI_LOG_D(TAG, "Moving from standard dictionary to elite dictionary"); - state = DictAttackStateFlipperDictInProgress; - } - - // Setup view - if(state == DictAttackStateUserDictInProgress) { - worker_state = PicopassWorkerStateEliteDictAttack; - dict_attack_set_header(picopass->dict_attack, "Elite User Dictionary"); - dict_attack_data->type = IclassEliteDictTypeUser; - dict = iclass_elite_dict_alloc(IclassEliteDictTypeUser); - - // If failed to load user dictionary - try the system dictionary - if(!dict) { - FURI_LOG_E(TAG, "User dictionary not found"); - state = DictAttackStateStandardDictInProgress; - } - } - if(state == DictAttackStateStandardDictInProgress) { - worker_state = PicopassWorkerStateEliteDictAttack; - dict_attack_set_header(picopass->dict_attack, "Standard System Dictionary"); - dict_attack_data->type = IclassStandardDictTypeFlipper; - dict = iclass_elite_dict_alloc(IclassStandardDictTypeFlipper); - - if(!dict) { - FURI_LOG_E(TAG, "Flipper standard dictionary not found"); - state = DictAttackStateFlipperDictInProgress; - } - } - if(state == DictAttackStateFlipperDictInProgress) { - worker_state = PicopassWorkerStateEliteDictAttack; - dict_attack_set_header(picopass->dict_attack, "Elite System Dictionary"); - dict_attack_data->type = IclassEliteDictTypeFlipper; - dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper); - if(!dict) { - FURI_LOG_E(TAG, "Flipper Elite dictionary not found"); - // Pass through to let the worker handle the failure - } - } - // Free previous dictionary - if(dict_attack_data->dict) { - iclass_elite_dict_free(dict_attack_data->dict); - } - dict_attack_data->dict = dict; - scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack, state); - dict_attack_set_callback( - picopass->dict_attack, picopass_dict_attack_result_callback, picopass); - dict_attack_set_current_sector(picopass->dict_attack, 0); - dict_attack_set_card_detected(picopass->dict_attack); - dict_attack_set_total_dict_keys( - picopass->dict_attack, dict ? iclass_elite_dict_get_total_keys(dict) : 0); - picopass_worker_start( - picopass->worker, - worker_state, - &picopass->dev->dev_data, - picopass_dict_attack_worker_callback, - picopass); -} - -void picopass_scene_elite_dict_attack_on_enter(void* context) { - Picopass* picopass = context; - picopass_scene_elite_dict_attack_prepare_view(picopass, DictAttackStateIdle); - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewDictAttack); - picopass_blink_start(picopass); - notification_message(picopass->notifications, &sequence_display_backlight_enforce_on); -} - -bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - uint32_t state = - scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack); - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassWorkerEventSuccess) { - if(state == DictAttackStateUserDictInProgress || - state == DictAttackStateStandardDictInProgress) { - picopass_worker_stop(picopass->worker); - picopass_scene_elite_dict_attack_prepare_view(picopass, state); - consumed = true; - } else { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); - consumed = true; - } - } else if(event.event == PicopassWorkerEventAborted) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); - consumed = true; - } else if(event.event == PicopassWorkerEventCardDetected) { - dict_attack_set_card_detected(picopass->dict_attack); - consumed = true; - } else if(event.event == PicopassWorkerEventNoCardDetected) { - dict_attack_set_card_removed(picopass->dict_attack); - consumed = true; - } else if(event.event == PicopassWorkerEventNewDictKeyBatch) { - dict_attack_inc_current_dict_key(picopass->dict_attack, PICOPASS_DICT_KEY_BATCH_SIZE); - consumed = true; - } else if(event.event == PicopassCustomEventDictAttackSkip) { - if(state == DictAttackStateUserDictInProgress) { - picopass_worker_stop(picopass->worker); - consumed = true; - } else if(state == DictAttackStateFlipperDictInProgress) { - picopass_worker_stop(picopass->worker); - consumed = true; - } else if(state == DictAttackStateStandardDictInProgress) { - picopass_worker_stop(picopass->worker); - consumed = true; - } - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_previous_scene(picopass->scene_manager); - } - return consumed; -} - -void picopass_scene_elite_dict_attack_on_exit(void* context) { - Picopass* picopass = context; - IclassEliteDictAttackData* dict_attack_data = - &picopass->dev->dev_data.iclass_elite_dict_attack_data; - // Stop worker - picopass_worker_stop(picopass->worker); - if(dict_attack_data->dict) { - iclass_elite_dict_free(dict_attack_data->dict); - dict_attack_data->dict = NULL; - } - dict_attack_reset(picopass->dict_attack); - picopass_blink_stop(picopass); - notification_message(picopass->notifications, &sequence_display_backlight_enforce_auto); -} diff --git a/applications/external/picopass/scenes/picopass_scene_file_select.c b/applications/external/picopass/scenes/picopass_scene_file_select.c deleted file mode 100644 index 2fc64746e..000000000 --- a/applications/external/picopass/scenes/picopass_scene_file_select.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "../picopass_i.h" -#include "../picopass_device.h" - -void picopass_scene_file_select_on_enter(void* context) { - Picopass* picopass = context; - // Process file_select return - picopass_device_set_loading_callback(picopass->dev, picopass_show_loading_popup, picopass); - if(picopass_file_select(picopass->dev)) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneSavedMenu); - } else { - scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneStart); - } - picopass_device_set_loading_callback(picopass->dev, NULL, picopass); -} - -bool picopass_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void picopass_scene_file_select_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/picopass/scenes/picopass_scene_key_menu.c b/applications/external/picopass/scenes/picopass_scene_key_menu.c deleted file mode 100644 index 15a32ff44..000000000 --- a/applications/external/picopass/scenes/picopass_scene_key_menu.c +++ /dev/null @@ -1,100 +0,0 @@ -#include "../picopass_i.h" -#include "../picopass_keys.h" - -enum SubmenuIndex { - SubmenuIndexWriteStandard, - SubmenuIndexWriteiCE, - SubmenuIndexWriteiCL, - SubmenuIndexWriteiCS, - SubmenuIndexWriteCustom, //TODO: user input of key -}; - -void picopass_scene_key_menu_submenu_callback(void* context, uint32_t index) { - Picopass* picopass = context; - - view_dispatcher_send_custom_event(picopass->view_dispatcher, index); -} - -void picopass_scene_key_menu_on_enter(void* context) { - Picopass* picopass = context; - Submenu* submenu = picopass->submenu; - - submenu_add_item( - submenu, - "Write Standard", - SubmenuIndexWriteStandard, - picopass_scene_key_menu_submenu_callback, - picopass); - submenu_add_item( - submenu, - "Write iCE", - SubmenuIndexWriteiCE, - picopass_scene_key_menu_submenu_callback, - picopass); - submenu_add_item( - submenu, - "Write iCL", - SubmenuIndexWriteiCL, - picopass_scene_key_menu_submenu_callback, - picopass); - submenu_add_item( - submenu, - "Write iCS", - SubmenuIndexWriteiCS, - picopass_scene_key_menu_submenu_callback, - picopass); - - submenu_set_selected_item( - picopass->submenu, - scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneKeyMenu)); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); -} - -bool picopass_scene_key_menu_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexWriteStandard) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteStandard); - memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN); - picopass->dev->dev_data.pacs.elite_kdf = false; - scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); - consumed = true; - } else if(event.event == SubmenuIndexWriteiCE) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE); - memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, PICOPASS_BLOCK_LEN); - picopass->dev->dev_data.pacs.elite_kdf = true; - scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); - consumed = true; - } else if(event.event == SubmenuIndexWriteiCL) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCL); - memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, PICOPASS_BLOCK_LEN); - picopass->dev->dev_data.pacs.elite_kdf = false; - scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); - consumed = true; - } else if(event.event == SubmenuIndexWriteiCS) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCS); - memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, PICOPASS_BLOCK_LEN); - picopass->dev->dev_data.pacs.elite_kdf = false; - scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - consumed = scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneStart); - } - - return consumed; -} - -void picopass_scene_key_menu_on_exit(void* context) { - Picopass* picopass = context; - - submenu_reset(picopass->submenu); -} diff --git a/applications/external/picopass/scenes/picopass_scene_read_card.c b/applications/external/picopass/scenes/picopass_scene_read_card.c deleted file mode 100644 index c1cc7249c..000000000 --- a/applications/external/picopass/scenes/picopass_scene_read_card.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../picopass_i.h" -#include -#include "../picopass_keys.h" - -void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) { - UNUSED(event); - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); -} - -void picopass_scene_read_card_on_enter(void* context) { - Picopass* picopass = context; - dolphin_deed(DolphinDeedNfcRead); - - // Setup view - Popup* popup = picopass->popup; - popup_set_header(popup, "Detecting\npicopass\ncard", 68, 30, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); - - // Start worker - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); - picopass_worker_start( - picopass->worker, - PicopassWorkerStateDetect, - &picopass->dev->dev_data, - picopass_read_card_worker_callback, - picopass); - - picopass_blink_start(picopass); -} - -bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassCustomEventWorkerExit) { - if(memcmp( - picopass->dev->dev_data.pacs.key, - picopass_factory_debit_key, - PICOPASS_BLOCK_LEN) == 0) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess); - } else { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess); - } - consumed = true; - } - } - return consumed; -} - -void picopass_scene_read_card_on_exit(void* context) { - Picopass* picopass = context; - - // Stop worker - picopass_worker_stop(picopass->worker); - // Clear view - popup_reset(picopass->popup); - - picopass_blink_stop(picopass); -} diff --git a/applications/external/picopass/scenes/picopass_scene_read_card_success.c b/applications/external/picopass/scenes/picopass_scene_read_card_success.c deleted file mode 100644 index ffe7195b7..000000000 --- a/applications/external/picopass/scenes/picopass_scene_read_card_success.c +++ /dev/null @@ -1,172 +0,0 @@ -#include "../picopass_i.h" -#include - -void picopass_scene_read_card_success_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - furi_assert(context); - Picopass* picopass = context; - - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(picopass->view_dispatcher, result); - } -} - -void picopass_scene_read_card_success_on_enter(void* context) { - Picopass* picopass = context; - - FuriString* csn_str = furi_string_alloc_set("CSN:"); - FuriString* credential_str = furi_string_alloc(); - FuriString* wiegand_str = furi_string_alloc(); - FuriString* sio_str = furi_string_alloc(); - - dolphin_deed(DolphinDeedNfcReadSuccess); - - // Send notification - notification_message(picopass->notifications, &sequence_success); - - // Setup view - PicopassBlock* AA1 = picopass->dev->dev_data.AA1; - PicopassPacs* pacs = &picopass->dev->dev_data.pacs; - Widget* widget = picopass->widget; - - uint8_t csn[PICOPASS_BLOCK_LEN] = {0}; - memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN); - for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(csn_str, "%02X", csn[i]); - } - - bool no_key = picopass_is_memset(pacs->key, 0x00, PICOPASS_BLOCK_LEN); - bool empty = - picopass_is_memset(AA1[PICOPASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN); - - if(no_key) { - furi_string_cat_printf(wiegand_str, "Read Failed"); - - if(pacs->se_enabled) { - furi_string_cat_printf(credential_str, "SE enabled"); - } - - widget_add_button_element( - widget, - GuiButtonTypeCenter, - "Menu", - picopass_scene_read_card_success_widget_callback, - picopass); - } else if(empty) { - furi_string_cat_printf(wiegand_str, "Empty"); - widget_add_button_element( - widget, - GuiButtonTypeCenter, - "Menu", - picopass_scene_read_card_success_widget_callback, - picopass); - } else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { - // Neither of these are valid. Indicates the block was all 0x00 or all 0xff - furi_string_cat_printf(wiegand_str, "Invalid PACS"); - - if(pacs->se_enabled) { - furi_string_cat_printf(credential_str, "SE enabled"); - } - widget_add_button_element( - widget, - GuiButtonTypeCenter, - "Menu", - picopass_scene_read_card_success_widget_callback, - picopass); - } else { - size_t bytesLength = 1 + pacs->record.bitLength / 8; - furi_string_set(credential_str, ""); - for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(credential_str, " %02X", pacs->credential[i]); - } - - if(pacs->record.valid) { - furi_string_cat_printf( - wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); - } else { - furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); - } - - if(pacs->sio) { - furi_string_cat_printf(sio_str, "+SIO"); - } - - if(pacs->key) { - if(pacs->sio) { - furi_string_cat_printf(sio_str, " "); - } - furi_string_cat_printf(sio_str, "Key: "); - - uint8_t key[PICOPASS_BLOCK_LEN]; - memcpy(key, &pacs->key, PICOPASS_BLOCK_LEN); - for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(sio_str, "%02X", key[i]); - } - } - - widget_add_button_element( - widget, - GuiButtonTypeRight, - "More", - picopass_scene_read_card_success_widget_callback, - picopass); - } - - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - picopass_scene_read_card_success_widget_callback, - picopass); - - widget_add_string_element( - widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(csn_str)); - widget_add_string_element( - widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(wiegand_str)); - widget_add_string_element( - widget, - 64, - 36, - AlignCenter, - AlignCenter, - FontSecondary, - furi_string_get_cstr(credential_str)); - widget_add_string_element( - widget, 64, 46, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(sio_str)); - - furi_string_free(csn_str); - furi_string_free(credential_str); - furi_string_free(wiegand_str); - furi_string_free(sio_str); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); -} - -bool picopass_scene_read_card_success_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(picopass->scene_manager); - } else if(event.event == GuiButtonTypeRight) { - // Clear device name - picopass_device_set_name(picopass->dev, ""); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu); - consumed = true; - } else if(event.event == GuiButtonTypeCenter) { - consumed = scene_manager_search_and_switch_to_another_scene( - picopass->scene_manager, PicopassSceneStart); - } - } - return consumed; -} - -void picopass_scene_read_card_success_on_exit(void* context) { - Picopass* picopass = context; - - // Clear view - widget_reset(picopass->widget); -} diff --git a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c b/applications/external/picopass/scenes/picopass_scene_read_factory_success.c deleted file mode 100644 index f5fcd10fd..000000000 --- a/applications/external/picopass/scenes/picopass_scene_read_factory_success.c +++ /dev/null @@ -1,80 +0,0 @@ -#include "../picopass_i.h" -#include -#include "../picopass_keys.h" - -void picopass_scene_read_factory_success_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - furi_assert(context); - Picopass* picopass = context; - - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(picopass->view_dispatcher, result); - } -} - -void picopass_scene_read_factory_success_on_enter(void* context) { - Picopass* picopass = context; - FuriString* title = furi_string_alloc_set("Factory Default"); - FuriString* subtitle = furi_string_alloc_set(""); - - dolphin_deed(DolphinDeedNfcReadSuccess); - - // Send notification - notification_message(picopass->notifications, &sequence_success); - - // Setup view - Widget* widget = picopass->widget; - //PicopassPacs* pacs = &picopass->dev->dev_data.pacs; - PicopassBlock* AA1 = picopass->dev->dev_data.AA1; - - uint8_t* configBlock = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data; - uint8_t fuses = configBlock[7]; - - if((fuses & 0x80) == 0x80) { - furi_string_cat_printf(subtitle, "Personalization mode"); - } else { - furi_string_cat_printf(subtitle, "Application mode"); - } - - widget_add_button_element( - widget, - GuiButtonTypeCenter, - "Write Standard iClass Key", - picopass_scene_read_factory_success_widget_callback, - picopass); - - widget_add_string_element( - widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(title)); - widget_add_string_element( - widget, 64, 20, AlignCenter, AlignCenter, FontPrimary, furi_string_get_cstr(subtitle)); - - furi_string_free(title); - furi_string_free(subtitle); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); -} - -bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(picopass->scene_manager); - } else if(event.event == GuiButtonTypeCenter) { - memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, PICOPASS_BLOCK_LEN); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey); - consumed = true; - } - } - return consumed; -} - -void picopass_scene_read_factory_success_on_exit(void* context) { - Picopass* picopass = context; - - // Clear view - widget_reset(picopass->widget); -} diff --git a/applications/external/picopass/scenes/picopass_scene_save_name.c b/applications/external/picopass/scenes/picopass_scene_save_name.c deleted file mode 100644 index baf882b80..000000000 --- a/applications/external/picopass/scenes/picopass_scene_save_name.c +++ /dev/null @@ -1,81 +0,0 @@ -#include "../picopass_i.h" -#include -#include -#include - -void picopass_scene_save_name_text_input_callback(void* context) { - Picopass* picopass = context; - - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventTextInputDone); -} - -void picopass_scene_save_name_on_enter(void* context) { - Picopass* picopass = context; - - // Setup view - TextInput* text_input = picopass->text_input; - bool dev_name_empty = false; - if(!strcmp(picopass->dev->dev_name, "")) { - set_random_name(picopass->text_store, sizeof(picopass->text_store)); - dev_name_empty = true; - } else { - picopass_text_store_set(picopass, picopass->dev->dev_name); - } - text_input_set_header_text(text_input, "Name the card"); - text_input_set_result_callback( - text_input, - picopass_scene_save_name_text_input_callback, - picopass, - picopass->text_store, - PICOPASS_DEV_NAME_MAX_LEN, - dev_name_empty); - - FuriString* folder_path; - folder_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX); - - if(furi_string_end_with(picopass->dev->load_path, PICOPASS_APP_EXTENSION)) { - path_extract_dirname(furi_string_get_cstr(picopass->dev->load_path), folder_path); - } - - ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - furi_string_get_cstr(folder_path), PICOPASS_APP_EXTENSION, picopass->dev->dev_name); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewTextInput); - - furi_string_free(folder_path); -} - -bool picopass_scene_save_name_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassCustomEventTextInputDone) { - if(strcmp(picopass->dev->dev_name, "") != 0) { - // picopass_device_delete(picopass->dev, true); - } - strlcpy( - picopass->dev->dev_name, picopass->text_store, strlen(picopass->text_store) + 1); - if(picopass_device_save(picopass->dev, picopass->text_store)) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneSaveSuccess); - consumed = true; - } else { - consumed = scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneStart); - } - } - } - return consumed; -} - -void picopass_scene_save_name_on_exit(void* context) { - Picopass* picopass = context; - - // Clear view - void* validator_context = text_input_get_validator_callback_context(picopass->text_input); - text_input_set_validator(picopass->text_input, NULL, NULL); - validator_is_file_free(validator_context); - - text_input_reset(picopass->text_input); -} diff --git a/applications/external/picopass/scenes/picopass_scene_save_success.c b/applications/external/picopass/scenes/picopass_scene_save_success.c deleted file mode 100644 index 3b0a1cadd..000000000 --- a/applications/external/picopass/scenes/picopass_scene_save_success.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "../picopass_i.h" -#include - -void picopass_scene_save_success_popup_callback(void* context) { - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventViewExit); -} - -void picopass_scene_save_success_on_enter(void* context) { - Picopass* picopass = context; - dolphin_deed(DolphinDeedNfcSave); - - // Setup view - Popup* popup = picopass->popup; - popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(popup, "Saved!", 13, 22, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, picopass); - popup_set_callback(popup, picopass_scene_save_success_popup_callback); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); -} - -bool picopass_scene_save_success_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassCustomEventViewExit) { - if(scene_manager_has_previous_scene(picopass->scene_manager, PicopassSceneCardMenu)) { - consumed = scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneCardMenu); - } else { - consumed = scene_manager_search_and_switch_to_previous_scene( - picopass->scene_manager, PicopassSceneStart); - } - } - } - return consumed; -} - -void picopass_scene_save_success_on_exit(void* context) { - Picopass* picopass = context; - - // Clear view - popup_reset(picopass->popup); -} diff --git a/applications/external/picopass/scenes/picopass_scene_saved_menu.c b/applications/external/picopass/scenes/picopass_scene_saved_menu.c deleted file mode 100644 index 90a27ee81..000000000 --- a/applications/external/picopass/scenes/picopass_scene_saved_menu.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "../picopass_i.h" - -enum SubmenuIndex { - SubmenuIndexDelete, - SubmenuIndexInfo, - SubmenuIndexWrite, -}; - -void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) { - Picopass* picopass = context; - - view_dispatcher_send_custom_event(picopass->view_dispatcher, index); -} - -void picopass_scene_saved_menu_on_enter(void* context) { - Picopass* picopass = context; - Submenu* submenu = picopass->submenu; - - submenu_add_item( - submenu, - "Delete", - SubmenuIndexDelete, - picopass_scene_saved_menu_submenu_callback, - picopass); - submenu_add_item( - submenu, "Info", SubmenuIndexInfo, picopass_scene_saved_menu_submenu_callback, picopass); - submenu_add_item( - submenu, "Write", SubmenuIndexWrite, picopass_scene_saved_menu_submenu_callback, picopass); - - submenu_set_selected_item( - picopass->submenu, - scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneSavedMenu)); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); -} - -bool picopass_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneSavedMenu, event.event); - - if(event.event == SubmenuIndexDelete) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneDelete); - consumed = true; - } else if(event.event == SubmenuIndexInfo) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneDeviceInfo); - consumed = true; - } else if(event.event == SubmenuIndexWrite) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCard); - consumed = true; - } - } - - return consumed; -} - -void picopass_scene_saved_menu_on_exit(void* context) { - Picopass* picopass = context; - - submenu_reset(picopass->submenu); -} diff --git a/applications/external/picopass/scenes/picopass_scene_start.c b/applications/external/picopass/scenes/picopass_scene_start.c deleted file mode 100644 index 8f7b627aa..000000000 --- a/applications/external/picopass/scenes/picopass_scene_start.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "../picopass_i.h" -enum SubmenuIndex { - SubmenuIndexRead, - SubmenuIndexEliteDictAttack, - SubmenuIndexSaved, -}; - -void picopass_scene_start_submenu_callback(void* context, uint32_t index) { - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, index); -} -void picopass_scene_start_on_enter(void* context) { - Picopass* picopass = context; - - Submenu* submenu = picopass->submenu; - submenu_add_item( - submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass); - submenu_add_item( - submenu, - "Elite Dict. Attack", - SubmenuIndexEliteDictAttack, - picopass_scene_start_submenu_callback, - picopass); - submenu_add_item( - submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart)); - picopass_device_clear(picopass->dev); - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewMenu); -} - -bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexRead) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneStart, SubmenuIndexRead); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard); - consumed = true; - } else if(event.event == SubmenuIndexSaved) { - // Explicitly save state so that the correct item is - // reselected if the user cancels loading a file. - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect); - consumed = true; - } else if(event.event == SubmenuIndexEliteDictAttack) { - scene_manager_set_scene_state( - picopass->scene_manager, PicopassSceneStart, SubmenuIndexEliteDictAttack); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteDictAttack); - consumed = true; - } - } - - return consumed; -} - -void picopass_scene_start_on_exit(void* context) { - Picopass* picopass = context; - submenu_reset(picopass->submenu); -} diff --git a/applications/external/picopass/scenes/picopass_scene_write_card.c b/applications/external/picopass/scenes/picopass_scene_write_card.c deleted file mode 100644 index ce396fc10..000000000 --- a/applications/external/picopass/scenes/picopass_scene_write_card.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "../picopass_i.h" -#include - -void picopass_write_card_worker_callback(PicopassWorkerEvent event, void* context) { - UNUSED(event); - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); -} - -void picopass_scene_write_card_on_enter(void* context) { - Picopass* picopass = context; - dolphin_deed(DolphinDeedNfcSave); - - // Setup view - Popup* popup = picopass->popup; - popup_set_header(popup, "Writing\npicopass\ncard", 68, 30, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); - - // Start worker - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); - picopass_worker_start( - picopass->worker, - PicopassWorkerStateWrite, - &picopass->dev->dev_data, - picopass_write_card_worker_callback, - picopass); - - picopass_blink_start(picopass); -} - -bool picopass_scene_write_card_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassCustomEventWorkerExit) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess); - consumed = true; - } - } - return consumed; -} - -void picopass_scene_write_card_on_exit(void* context) { - Picopass* picopass = context; - - // Stop worker - picopass_worker_stop(picopass->worker); - // Clear view - popup_reset(picopass->popup); - - picopass_blink_stop(picopass); -} diff --git a/applications/external/picopass/scenes/picopass_scene_write_card_success.c b/applications/external/picopass/scenes/picopass_scene_write_card_success.c deleted file mode 100644 index cd760272f..000000000 --- a/applications/external/picopass/scenes/picopass_scene_write_card_success.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "../picopass_i.h" -#include - -void picopass_scene_write_card_success_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - furi_assert(context); - Picopass* picopass = context; - - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(picopass->view_dispatcher, result); - } -} - -void picopass_scene_write_card_success_on_enter(void* context) { - Picopass* picopass = context; - Widget* widget = picopass->widget; - FuriString* str = furi_string_alloc_set("Write Success!"); - - dolphin_deed(DolphinDeedNfcReadSuccess); - - // Send notification - notification_message(picopass->notifications, &sequence_success); - - widget_add_button_element( - widget, - GuiButtonTypeLeft, - "Retry", - picopass_scene_write_card_success_widget_callback, - picopass); - - widget_add_button_element( - widget, - GuiButtonTypeRight, - "Menu", - picopass_scene_write_card_success_widget_callback, - picopass); - - widget_add_string_element( - widget, 64, 5, AlignCenter, AlignCenter, FontSecondary, furi_string_get_cstr(str)); - - furi_string_free(str); - - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget); -} - -bool picopass_scene_write_card_success_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == GuiButtonTypeLeft) { - consumed = scene_manager_previous_scene(picopass->scene_manager); - } else if(event.event == GuiButtonTypeRight) { - // Clear device name - picopass_device_set_name(picopass->dev, ""); - scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu); - consumed = true; - } - } - return consumed; -} - -void picopass_scene_write_card_success_on_exit(void* context) { - Picopass* picopass = context; - - // Clear view - widget_reset(picopass->widget); -} diff --git a/applications/external/picopass/scenes/picopass_scene_write_key.c b/applications/external/picopass/scenes/picopass_scene_write_key.c deleted file mode 100644 index 806a2b5a8..000000000 --- a/applications/external/picopass/scenes/picopass_scene_write_key.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "../picopass_i.h" -#include - -void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context) { - UNUSED(event); - Picopass* picopass = context; - view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit); -} - -void picopass_scene_write_key_on_enter(void* context) { - Picopass* picopass = context; - dolphin_deed(DolphinDeedNfcSave); - - // Setup view - Popup* popup = picopass->popup; - popup_set_header(popup, "Writing\niClass\nkey", 68, 30, AlignLeft, AlignTop); - popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); - - // Start worker - view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup); - picopass_worker_start( - picopass->worker, - PicopassWorkerStateWriteKey, - &picopass->dev->dev_data, - picopass_write_key_worker_callback, - picopass); - - picopass_blink_start(picopass); -} - -bool picopass_scene_write_key_on_event(void* context, SceneManagerEvent event) { - Picopass* picopass = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == PicopassCustomEventWorkerExit) { - scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess); - consumed = true; - } - } - return consumed; -} - -void picopass_scene_write_key_on_exit(void* context) { - Picopass* picopass = context; - - // Stop worker - picopass_worker_stop(picopass->worker); - // Clear view - popup_reset(picopass->popup); - - picopass_blink_stop(picopass); -} diff --git a/applications/external/picopass/views/dict_attack.c b/applications/external/picopass/views/dict_attack.c deleted file mode 100644 index fb7335f6c..000000000 --- a/applications/external/picopass/views/dict_attack.c +++ /dev/null @@ -1,281 +0,0 @@ -#include "dict_attack.h" - -#include - -typedef enum { - DictAttackStateRead, - DictAttackStateCardRemoved, -} DictAttackState; - -struct DictAttack { - View* view; - DictAttackCallback callback; - void* context; -}; - -typedef struct { - DictAttackState state; - MfClassicType type; - FuriString* header; - uint8_t sectors_total; - uint8_t sectors_read; - uint8_t sector_current; - uint8_t keys_total; - uint8_t keys_found; - uint16_t dict_keys_total; - uint16_t dict_keys_current; - bool is_key_attack; - uint8_t key_attack_current_sector; -} DictAttackViewModel; - -static void dict_attack_draw_callback(Canvas* canvas, void* model) { - DictAttackViewModel* m = model; - if(m->state == DictAttackStateCardRemoved) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!"); - canvas_set_font(canvas, FontSecondary); - elements_multiline_text_aligned( - canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly."); - } else if(m->state == DictAttackStateRead) { - char draw_str[32] = {}; - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header)); - if(m->is_key_attack) { - snprintf( - draw_str, - sizeof(draw_str), - "Reuse key check for sector: %d", - m->key_attack_current_sector); - } else { - snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current); - } - canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str); - float dict_progress = m->dict_keys_total == 0 ? - 0 : - (float)(m->dict_keys_current) / (float)(m->dict_keys_total); - float progress = m->sectors_total == 0 ? 0 : - ((float)(m->sector_current) + dict_progress) / - (float)(m->sectors_total); - if(progress > 1.0) { - progress = 1.0; - } - if(m->dict_keys_current == 0) { - // Cause when people see 0 they think it's broken - snprintf(draw_str, sizeof(draw_str), "%d/%d", 1, m->dict_keys_total); - } else { - snprintf( - draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total); - } - elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str); - canvas_set_font(canvas, FontSecondary); - snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total); - canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str); - snprintf( - draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total); - canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str); - } - elements_button_center(canvas, "Skip"); -} - -static bool dict_attack_input_callback(InputEvent* event, void* context) { - DictAttack* dict_attack = context; - bool consumed = false; - if(event->type == InputTypeShort && event->key == InputKeyOk) { - if(dict_attack->callback) { - dict_attack->callback(dict_attack->context); - } - consumed = true; - } - return consumed; -} - -DictAttack* dict_attack_alloc() { - DictAttack* dict_attack = malloc(sizeof(DictAttack)); - dict_attack->view = view_alloc(); - view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel)); - view_set_draw_callback(dict_attack->view, dict_attack_draw_callback); - view_set_input_callback(dict_attack->view, dict_attack_input_callback); - view_set_context(dict_attack->view, dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { model->header = furi_string_alloc(); }, - false); - return dict_attack; -} - -void dict_attack_free(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { furi_string_free(model->header); }, - false); - view_free(dict_attack->view); - free(dict_attack); -} - -void dict_attack_reset(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - model->state = DictAttackStateRead; - model->type = MfClassicType1k; - model->sectors_total = 1; - model->sectors_read = 0; - model->sector_current = 0; - model->keys_total = 0; - model->keys_found = 0; - model->dict_keys_total = 0; - model->dict_keys_current = 0; - model->is_key_attack = false; - furi_string_reset(model->header); - }, - false); -} - -View* dict_attack_get_view(DictAttack* dict_attack) { - furi_assert(dict_attack); - return dict_attack->view; -} - -void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) { - furi_assert(dict_attack); - furi_assert(callback); - dict_attack->callback = callback; - dict_attack->context = context; -} - -void dict_attack_set_header(DictAttack* dict_attack, const char* header) { - furi_assert(dict_attack); - furi_assert(header); - - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { furi_string_set(model->header, header); }, - true); -} - -void dict_attack_set_card_detected(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - model->state = DictAttackStateRead; - model->sectors_total = 1; - model->keys_total = model->sectors_total; - }, - true); -} - -void dict_attack_set_card_removed(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { model->state = DictAttackStateCardRemoved; }, - true); -} - -void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true); -} - -void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true); -} - -void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - model->sector_current = curr_sec; - model->dict_keys_current = 0; - }, - true); -} - -void dict_attack_inc_current_sector(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - if(model->sector_current < model->sectors_total) { - model->sector_current++; - model->dict_keys_current = 0; - } - }, - true); -} - -void dict_attack_inc_keys_found(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - if(model->keys_found < model->keys_total) { - model->keys_found++; - } - }, - true); -} - -void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { model->dict_keys_total = dict_keys_total; }, - true); -} - -void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - if(model->dict_keys_current + keys_tried < model->dict_keys_total) { - model->dict_keys_current += keys_tried; - } - }, - true); -} - -void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - model->is_key_attack = is_key_attack; - model->key_attack_current_sector = sector; - }, - true); -} - -void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) { - furi_assert(dict_attack); - with_view_model( - dict_attack->view, - DictAttackViewModel * model, - { - if(model->key_attack_current_sector < model->sectors_total) { - model->key_attack_current_sector++; - } - }, - true); -} diff --git a/applications/external/picopass/views/dict_attack.h b/applications/external/picopass/views/dict_attack.h deleted file mode 100644 index bdfa3e952..000000000 --- a/applications/external/picopass/views/dict_attack.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once -#include -#include -#include - -#include - -typedef struct DictAttack DictAttack; - -typedef void (*DictAttackCallback)(void* context); - -DictAttack* dict_attack_alloc(); - -void dict_attack_free(DictAttack* dict_attack); - -void dict_attack_reset(DictAttack* dict_attack); - -View* dict_attack_get_view(DictAttack* dict_attack); - -void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context); - -void dict_attack_set_header(DictAttack* dict_attack, const char* header); - -void dict_attack_set_card_detected(DictAttack* dict_attack); - -void dict_attack_set_card_removed(DictAttack* dict_attack); - -void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read); - -void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found); - -void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec); - -void dict_attack_inc_current_sector(DictAttack* dict_attack); - -void dict_attack_inc_keys_found(DictAttack* dict_attack); - -void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total); - -void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried); - -void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector); - -void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack); diff --git a/applications/external/signal_generator/application.fam b/applications/external/signal_generator/application.fam deleted file mode 100644 index 094e784cc..000000000 --- a/applications/external/signal_generator/application.fam +++ /dev/null @@ -1,12 +0,0 @@ -App( - appid="signal_generator", - name="Signal Generator", - apptype=FlipperAppType.EXTERNAL, - entry_point="signal_gen_app", - requires=["gui"], - stack_size=1 * 1024, - order=50, - fap_icon="signal_gen_10px.png", - fap_category="GPIO", - fap_icon_assets="icons", -) diff --git a/applications/external/signal_generator/icons/SmallArrowDown_3x5.png b/applications/external/signal_generator/icons/SmallArrowDown_3x5.png deleted file mode 100644 index 1912e5d246268d75a20984bdc8b996d503f3d166..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3592 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 BDRuw= diff --git a/applications/external/signal_generator/icons/SmallArrowUp_3x5.png b/applications/external/signal_generator/icons/SmallArrowUp_3x5.png deleted file mode 100644 index 9c6242078d3bcc9a93eb55b6b5f358720ee6662c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7976 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 - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) SignalGenScene##id, -typedef enum { -#include "signal_gen_scene_config.h" - SignalGenSceneNum, -} SignalGenScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers signal_gen_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "signal_gen_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "signal_gen_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "signal_gen_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_config.h b/applications/external/signal_generator/scenes/signal_gen_scene_config.h deleted file mode 100644 index b6c750256..000000000 --- a/applications/external/signal_generator/scenes/signal_gen_scene_config.h +++ /dev/null @@ -1,3 +0,0 @@ -ADD_SCENE(signal_gen, start, Start) -ADD_SCENE(signal_gen, pwm, Pwm) -ADD_SCENE(signal_gen, mco, Mco) diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_mco.c b/applications/external/signal_generator/scenes/signal_gen_scene_mco.c deleted file mode 100644 index 0855cde0a..000000000 --- a/applications/external/signal_generator/scenes/signal_gen_scene_mco.c +++ /dev/null @@ -1,145 +0,0 @@ -#include "../signal_gen_app_i.h" - -typedef enum { - LineIndexPin, - LineIndexSource, - LineIndexDivision, -} LineIndex; - -static const char* const mco_pin_names[] = { - "13(Tx)", -}; - -static const char* const mco_source_names[] = { - "32768Hz", - "64MHz", - "~100K", - "~200K", - "~400K", - "~800K", - "~1MHz", - "~2MHz", - "~4MHz", - "~8MHz", - "~16MHz", - "~24MHz", - "~32MHz", - "~48MHz", -}; - -static const FuriHalClockMcoSourceId mco_sources[] = { - FuriHalClockMcoLse, - FuriHalClockMcoSysclk, - FuriHalClockMcoMsi100k, - FuriHalClockMcoMsi200k, - FuriHalClockMcoMsi400k, - FuriHalClockMcoMsi800k, - FuriHalClockMcoMsi1m, - FuriHalClockMcoMsi2m, - FuriHalClockMcoMsi4m, - FuriHalClockMcoMsi8m, - FuriHalClockMcoMsi16m, - FuriHalClockMcoMsi24m, - FuriHalClockMcoMsi32m, - FuriHalClockMcoMsi48m, -}; - -static const char* const mco_divisor_names[] = { - "1", - "2", - "4", - "8", - "16", -}; - -static const FuriHalClockMcoDivisorId mco_divisors[] = { - FuriHalClockMcoDiv1, - FuriHalClockMcoDiv2, - FuriHalClockMcoDiv4, - FuriHalClockMcoDiv8, - FuriHalClockMcoDiv16, -}; - -static void mco_source_list_change_callback(VariableItem* item) { - SignalGenApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, mco_source_names[index]); - - app->mco_src = mco_sources[index]; - - view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenMcoEventUpdate); -} - -static void mco_divisor_list_change_callback(VariableItem* item) { - SignalGenApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, mco_divisor_names[index]); - - app->mco_div = mco_divisors[index]; - - view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenMcoEventUpdate); -} - -void signal_gen_scene_mco_on_enter(void* context) { - SignalGenApp* app = context; - VariableItemList* var_item_list = app->var_item_list; - - VariableItem* item; - - item = variable_item_list_add(var_item_list, "GPIO Pin", COUNT_OF(mco_pin_names), NULL, NULL); - variable_item_set_current_value_index(item, 0); - variable_item_set_current_value_text(item, mco_pin_names[0]); - - item = variable_item_list_add( - var_item_list, - "Frequency", - COUNT_OF(mco_source_names), - mco_source_list_change_callback, - app); - variable_item_set_current_value_index(item, 0); - variable_item_set_current_value_text(item, mco_source_names[0]); - - item = variable_item_list_add( - var_item_list, - "Freq. divider", - COUNT_OF(mco_divisor_names), - mco_divisor_list_change_callback, - app); - variable_item_set_current_value_index(item, 0); - variable_item_set_current_value_text(item, mco_divisor_names[0]); - - variable_item_list_set_selected_item(var_item_list, LineIndexSource); - - view_dispatcher_switch_to_view(app->view_dispatcher, SignalGenViewVarItemList); - - app->mco_src = FuriHalClockMcoLse; - app->mco_div = FuriHalClockMcoDiv1; - furi_hal_clock_mco_enable(app->mco_src, app->mco_div); - furi_hal_gpio_init_ex( - &gpio_usart_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedVeryHigh, GpioAltFn0MCO); -} - -bool signal_gen_scene_mco_on_event(void* context, SceneManagerEvent event) { - SignalGenApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SignalGenMcoEventUpdate) { - consumed = true; - furi_hal_clock_mco_enable(app->mco_src, app->mco_div); - } - } - return consumed; -} - -void signal_gen_scene_mco_on_exit(void* context) { - SignalGenApp* app = context; - variable_item_list_reset(app->var_item_list); - furi_hal_gpio_init_ex( - &gpio_usart_tx, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn7USART1); - furi_hal_clock_mco_disable(); -} diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c b/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c deleted file mode 100644 index 1cadb3a1a..000000000 --- a/applications/external/signal_generator/scenes/signal_gen_scene_pwm.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "../signal_gen_app_i.h" - -static const FuriHalPwmOutputId pwm_ch_id[] = { - FuriHalPwmOutputIdTim1PA7, - FuriHalPwmOutputIdLptim2PA4, -}; - -#define DEFAULT_FREQ 1000 -#define DEFAULT_DUTY 50 - -static void - signal_gen_pwm_callback(uint8_t channel_id, uint32_t freq, uint8_t duty, void* context) { - SignalGenApp* app = context; - - app->pwm_freq = freq; - app->pwm_duty = duty; - - if(app->pwm_ch != pwm_ch_id[channel_id]) { //-V1051 - app->pwm_ch_prev = app->pwm_ch; - app->pwm_ch = pwm_ch_id[channel_id]; - view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenPwmEventChannelChange); - } else { - app->pwm_ch = pwm_ch_id[channel_id]; //-V1048 - view_dispatcher_send_custom_event(app->view_dispatcher, SignalGenPwmEventUpdate); - } -} - -void signal_gen_scene_pwm_on_enter(void* context) { - SignalGenApp* app = context; - - view_dispatcher_switch_to_view(app->view_dispatcher, SignalGenViewPwm); - - signal_gen_pwm_set_callback(app->pwm_view, signal_gen_pwm_callback, app); - - signal_gen_pwm_set_params(app->pwm_view, 0, DEFAULT_FREQ, DEFAULT_DUTY); - - if(!furi_hal_pwm_is_running(pwm_ch_id[0])) { - furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); - } else { - furi_hal_pwm_stop(pwm_ch_id[0]); - furi_hal_pwm_start(pwm_ch_id[0], DEFAULT_FREQ, DEFAULT_DUTY); - } -} - -bool signal_gen_scene_pwm_on_event(void* context, SceneManagerEvent event) { - SignalGenApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SignalGenPwmEventUpdate) { - consumed = true; - furi_hal_pwm_set_params(app->pwm_ch, app->pwm_freq, app->pwm_duty); - } else if(event.event == SignalGenPwmEventChannelChange) { - consumed = true; - // Stop previous channel PWM - if(furi_hal_pwm_is_running(app->pwm_ch_prev)) { - furi_hal_pwm_stop(app->pwm_ch_prev); - } - - // Start PWM and restart if it was starter already - if(furi_hal_pwm_is_running(app->pwm_ch)) { - furi_hal_pwm_stop(app->pwm_ch); - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); - } else { - furi_hal_pwm_start(app->pwm_ch, app->pwm_freq, app->pwm_duty); - } - } - } - return consumed; -} - -void signal_gen_scene_pwm_on_exit(void* context) { - SignalGenApp* app = context; - variable_item_list_reset(app->var_item_list); - - if(furi_hal_pwm_is_running(app->pwm_ch)) { - furi_hal_pwm_stop(app->pwm_ch); - } -} diff --git a/applications/external/signal_generator/scenes/signal_gen_scene_start.c b/applications/external/signal_generator/scenes/signal_gen_scene_start.c deleted file mode 100644 index 3c7b9cc32..000000000 --- a/applications/external/signal_generator/scenes/signal_gen_scene_start.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "../signal_gen_app_i.h" - -typedef enum { - SubmenuIndexPwm, - SubmenuIndexClockOutput, -} SubmenuIndex; - -void signal_gen_scene_start_submenu_callback(void* context, uint32_t index) { - SignalGenApp* app = context; - - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void signal_gen_scene_start_on_enter(void* context) { - SignalGenApp* app = context; - Submenu* submenu = app->submenu; - - submenu_add_item( - submenu, "PWM Generator", SubmenuIndexPwm, signal_gen_scene_start_submenu_callback, app); - submenu_add_item( - submenu, - "Clock Generator", - SubmenuIndexClockOutput, - signal_gen_scene_start_submenu_callback, - app); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, SignalGenSceneStart)); - - view_dispatcher_switch_to_view(app->view_dispatcher, SignalGenViewSubmenu); -} - -bool signal_gen_scene_start_on_event(void* context, SceneManagerEvent event) { - SignalGenApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexPwm) { - scene_manager_next_scene(app->scene_manager, SignalGenScenePwm); - consumed = true; - } else if(event.event == SubmenuIndexClockOutput) { - scene_manager_next_scene(app->scene_manager, SignalGenSceneMco); - consumed = true; - } - scene_manager_set_scene_state(app->scene_manager, SignalGenSceneStart, event.event); - } - - return consumed; -} - -void signal_gen_scene_start_on_exit(void* context) { - SignalGenApp* app = context; - - submenu_reset(app->submenu); -} diff --git a/applications/external/signal_generator/signal_gen_10px.png b/applications/external/signal_generator/signal_gen_10px.png deleted file mode 100644 index 9f6dcc5d0d9a9c1fbc54b7459b46c50c16c7e863..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6082 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 diff --git a/applications/external/signal_generator/signal_gen_app.c b/applications/external/signal_generator/signal_gen_app.c deleted file mode 100644 index ca065d330..000000000 --- a/applications/external/signal_generator/signal_gen_app.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "signal_gen_app_i.h" - -#include -#include - -static bool signal_gen_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - SignalGenApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool signal_gen_app_back_event_callback(void* context) { - furi_assert(context); - SignalGenApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void signal_gen_app_tick_event_callback(void* context) { - furi_assert(context); - SignalGenApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -SignalGenApp* signal_gen_app_alloc() { - SignalGenApp* app = malloc(sizeof(SignalGenApp)); - - app->gui = furi_record_open(RECORD_GUI); - - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&signal_gen_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, signal_gen_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, signal_gen_app_back_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, signal_gen_app_tick_event_callback, 100); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->var_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - SignalGenViewVarItemList, - variable_item_list_get_view(app->var_item_list)); - - app->submenu = submenu_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, SignalGenViewSubmenu, submenu_get_view(app->submenu)); - - app->pwm_view = signal_gen_pwm_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, SignalGenViewPwm, signal_gen_pwm_get_view(app->pwm_view)); - - scene_manager_next_scene(app->scene_manager, SignalGenSceneStart); - - return app; -} - -void signal_gen_app_free(SignalGenApp* app) { - furi_assert(app); - - // Views - view_dispatcher_remove_view(app->view_dispatcher, SignalGenViewVarItemList); - view_dispatcher_remove_view(app->view_dispatcher, SignalGenViewSubmenu); - view_dispatcher_remove_view(app->view_dispatcher, SignalGenViewPwm); - - submenu_free(app->submenu); - variable_item_list_free(app->var_item_list); - signal_gen_pwm_free(app->pwm_view); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Close records - furi_record_close(RECORD_GUI); - - free(app); -} - -int32_t signal_gen_app(void* p) { - UNUSED(p); - SignalGenApp* signal_gen_app = signal_gen_app_alloc(); - - view_dispatcher_run(signal_gen_app->view_dispatcher); - - signal_gen_app_free(signal_gen_app); - - return 0; -} diff --git a/applications/external/signal_generator/signal_gen_app_i.h b/applications/external/signal_generator/signal_gen_app_i.h deleted file mode 100644 index 60e4d7ed9..000000000 --- a/applications/external/signal_generator/signal_gen_app_i.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "scenes/signal_gen_scene.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include "views/signal_gen_pwm.h" - -typedef struct SignalGenApp SignalGenApp; - -struct SignalGenApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - - VariableItemList* var_item_list; - Submenu* submenu; - SignalGenPwm* pwm_view; - - FuriHalClockMcoSourceId mco_src; - FuriHalClockMcoDivisorId mco_div; - - FuriHalPwmOutputId pwm_ch_prev; - FuriHalPwmOutputId pwm_ch; - uint32_t pwm_freq; - uint8_t pwm_duty; -}; - -typedef enum { - SignalGenViewVarItemList, - SignalGenViewSubmenu, - SignalGenViewPwm, -} SignalGenAppView; - -typedef enum { - SignalGenMcoEventUpdate, - SignalGenPwmEventUpdate, - SignalGenPwmEventChannelChange, -} SignalGenCustomEvent; diff --git a/applications/external/signal_generator/views/signal_gen_pwm.c b/applications/external/signal_generator/views/signal_gen_pwm.c deleted file mode 100644 index d625ed5a9..000000000 --- a/applications/external/signal_generator/views/signal_gen_pwm.c +++ /dev/null @@ -1,310 +0,0 @@ -#include "../signal_gen_app_i.h" -#include -#include -#include - -typedef enum { - LineIndexChannel, - LineIndexFrequency, - LineIndexDuty, - LineIndexTotalCount -} LineIndex; - -static const char* const pwm_ch_names[] = {"2(A7)", "4(A4)"}; - -struct SignalGenPwm { - View* view; - SignalGenPwmViewCallback callback; - void* context; -}; - -typedef struct { - LineIndex line_sel; - bool edit_mode; - uint8_t edit_digit; - - uint8_t channel_id; - uint32_t freq; - uint8_t duty; - -} SignalGenPwmViewModel; - -#define ITEM_H 64 / 3 -#define ITEM_W 128 - -#define VALUE_X 100 -#define VALUE_W 45 - -#define FREQ_VALUE_X 62 -#define FREQ_MAX 1000000UL -#define FREQ_DIGITS_NB 7 - -static void pwm_set_config(SignalGenPwm* pwm) { - FuriHalPwmOutputId channel; - uint32_t freq; - uint8_t duty; - - with_view_model( - pwm->view, - SignalGenPwmViewModel * model, - { - channel = model->channel_id; - freq = model->freq; - duty = model->duty; - }, - false); - - furi_assert(pwm->callback); - pwm->callback(channel, freq, duty, pwm->context); -} - -static void pwm_channel_change(SignalGenPwmViewModel* model, InputEvent* event) { - if(event->key == InputKeyLeft) { - if(model->channel_id > 0) { - model->channel_id--; - } - } else if(event->key == InputKeyRight) { - if(model->channel_id < (COUNT_OF(pwm_ch_names) - 1)) { - model->channel_id++; - } - } -} - -static void pwm_duty_change(SignalGenPwmViewModel* model, InputEvent* event) { - if(event->key == InputKeyLeft) { - if(model->duty > 0) { - model->duty--; - } - } else if(event->key == InputKeyRight) { - if(model->duty < 100) { - model->duty++; - } - } -} - -static bool pwm_freq_edit(SignalGenPwmViewModel* model, InputEvent* event) { - bool consumed = false; - if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { - if(event->key == InputKeyRight) { - if(model->edit_digit > 0) { - model->edit_digit--; - } - consumed = true; - } else if(event->key == InputKeyLeft) { - if(model->edit_digit < (FREQ_DIGITS_NB - 1)) { - model->edit_digit++; - } - consumed = true; - } else if(event->key == InputKeyUp) { - uint32_t step = 1; - for(uint8_t i = 0; i < model->edit_digit; i++) { - step *= 10; - } - if((model->freq + step) < FREQ_MAX) { - model->freq += step; - } else { - model->freq = FREQ_MAX; - } - consumed = true; - } else if(event->key == InputKeyDown) { - uint32_t step = 1; - for(uint8_t i = 0; i < model->edit_digit; i++) { - step *= 10; - } - if(model->freq > (step + 1)) { - model->freq -= step; - } else { - model->freq = 1; - } - consumed = true; - } - } - return consumed; -} - -static void signal_gen_pwm_draw_callback(Canvas* canvas, void* _model) { - SignalGenPwmViewModel* model = _model; - char* line_label = NULL; - char val_text[16]; - - for(size_t line = 0; line < LineIndexTotalCount; line++) { - if(line == LineIndexChannel) { - line_label = "GPIO Pin"; - } else if(line == LineIndexFrequency) { - line_label = "Frequency"; - } else if(line == LineIndexDuty) { //-V547 - line_label = "Pulse width"; - } - - canvas_set_color(canvas, ColorBlack); - if(line == model->line_sel) { - elements_slightly_rounded_box(canvas, 0, ITEM_H * line + 1, ITEM_W, ITEM_H - 1); - canvas_set_color(canvas, ColorWhite); - } - - uint8_t text_y = ITEM_H * line + ITEM_H / 2 + 2; - - canvas_draw_str_aligned(canvas, 6, text_y, AlignLeft, AlignCenter, line_label); - - if(line == LineIndexChannel) { - snprintf(val_text, sizeof(val_text), "%s", pwm_ch_names[model->channel_id]); - canvas_draw_str_aligned(canvas, VALUE_X, text_y, AlignCenter, AlignCenter, val_text); - if(model->channel_id != 0) { - canvas_draw_str_aligned( - canvas, VALUE_X - VALUE_W / 2, text_y, AlignCenter, AlignCenter, "<"); - } - if(model->channel_id != (COUNT_OF(pwm_ch_names) - 1)) { - canvas_draw_str_aligned( - canvas, VALUE_X + VALUE_W / 2, text_y, AlignCenter, AlignCenter, ">"); - } - } else if(line == LineIndexFrequency) { - snprintf(val_text, sizeof(val_text), "%7lu Hz", model->freq); - canvas_set_font(canvas, FontKeyboard); - canvas_draw_str_aligned( - canvas, FREQ_VALUE_X, text_y, AlignLeft, AlignCenter, val_text); - canvas_set_font(canvas, FontSecondary); - - if(model->edit_mode) { - uint8_t icon_x = (FREQ_VALUE_X) + (FREQ_DIGITS_NB - model->edit_digit - 1) * 6; - canvas_draw_icon(canvas, icon_x, text_y - 9, &I_SmallArrowUp_3x5); - canvas_draw_icon(canvas, icon_x, text_y + 5, &I_SmallArrowDown_3x5); - } - } else if(line == LineIndexDuty) { //-V547 - snprintf(val_text, sizeof(val_text), "%d%%", model->duty); - canvas_draw_str_aligned(canvas, VALUE_X, text_y, AlignCenter, AlignCenter, val_text); - if(model->duty != 0) { - canvas_draw_str_aligned( - canvas, VALUE_X - VALUE_W / 2, text_y, AlignCenter, AlignCenter, "<"); - } - if(model->duty != 100) { - canvas_draw_str_aligned( - canvas, VALUE_X + VALUE_W / 2, text_y, AlignCenter, AlignCenter, ">"); - } - } - } -} - -static bool signal_gen_pwm_input_callback(InputEvent* event, void* context) { - furi_assert(context); - SignalGenPwm* pwm = context; - bool consumed = false; - bool need_update = false; - - with_view_model( - pwm->view, - SignalGenPwmViewModel * model, - { - if(model->edit_mode == false) { - if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { - if(event->key == InputKeyUp) { - if(model->line_sel == 0) { - model->line_sel = LineIndexTotalCount - 1; - } else { - model->line_sel = - CLAMP(model->line_sel - 1, LineIndexTotalCount - 1, 0); - } - consumed = true; - } else if(event->key == InputKeyDown) { - if(model->line_sel == LineIndexTotalCount - 1) { - model->line_sel = 0; - } else { - model->line_sel = - CLAMP(model->line_sel + 1, LineIndexTotalCount - 1, 0); - } - consumed = true; - } else if((event->key == InputKeyLeft) || (event->key == InputKeyRight)) { - if(model->line_sel == LineIndexChannel) { - pwm_channel_change(model, event); - need_update = true; - } else if(model->line_sel == LineIndexDuty) { - pwm_duty_change(model, event); - need_update = true; - } else if(model->line_sel == LineIndexFrequency) { - model->edit_mode = true; - } - consumed = true; - } else if(event->key == InputKeyOk) { - if(model->line_sel == LineIndexFrequency) { - model->edit_mode = true; - } - consumed = true; - } - } - } else { - if((event->key == InputKeyOk) || (event->key == InputKeyBack)) { - if(event->type == InputTypeShort) { - model->edit_mode = false; - consumed = true; - } - } else { - if(model->line_sel == LineIndexFrequency) { - consumed = pwm_freq_edit(model, event); - need_update = consumed; - } - } - } - }, - true); - - if(need_update) { - pwm_set_config(pwm); - } - - return consumed; -} - -SignalGenPwm* signal_gen_pwm_alloc() { - SignalGenPwm* pwm = malloc(sizeof(SignalGenPwm)); - - pwm->view = view_alloc(); - view_allocate_model(pwm->view, ViewModelTypeLocking, sizeof(SignalGenPwmViewModel)); - view_set_context(pwm->view, pwm); - view_set_draw_callback(pwm->view, signal_gen_pwm_draw_callback); - view_set_input_callback(pwm->view, signal_gen_pwm_input_callback); - - return pwm; -} - -void signal_gen_pwm_free(SignalGenPwm* pwm) { - furi_assert(pwm); - view_free(pwm->view); - free(pwm); -} - -View* signal_gen_pwm_get_view(SignalGenPwm* pwm) { - furi_assert(pwm); - return pwm->view; -} - -void signal_gen_pwm_set_callback( - SignalGenPwm* pwm, - SignalGenPwmViewCallback callback, - void* context) { - furi_assert(pwm); - furi_assert(callback); - - with_view_model( - pwm->view, - SignalGenPwmViewModel * model, - { - UNUSED(model); - pwm->callback = callback; - pwm->context = context; - }, - false); -} - -void signal_gen_pwm_set_params(SignalGenPwm* pwm, uint8_t channel_id, uint32_t freq, uint8_t duty) { - with_view_model( - pwm->view, - SignalGenPwmViewModel * model, - { - model->channel_id = channel_id; - model->freq = freq; - model->duty = duty; - }, - true); - - furi_assert(pwm->callback); - pwm->callback(channel_id, freq, duty, pwm->context); -} diff --git a/applications/external/signal_generator/views/signal_gen_pwm.h b/applications/external/signal_generator/views/signal_gen_pwm.h deleted file mode 100644 index 986794e7a..000000000 --- a/applications/external/signal_generator/views/signal_gen_pwm.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include "../signal_gen_app_i.h" - -typedef struct SignalGenPwm SignalGenPwm; -typedef void ( - *SignalGenPwmViewCallback)(uint8_t channel_id, uint32_t freq, uint8_t duty, void* context); - -SignalGenPwm* signal_gen_pwm_alloc(); - -void signal_gen_pwm_free(SignalGenPwm* pwm); - -View* signal_gen_pwm_get_view(SignalGenPwm* pwm); - -void signal_gen_pwm_set_callback( - SignalGenPwm* pwm, - SignalGenPwmViewCallback callback, - void* context); - -void signal_gen_pwm_set_params(SignalGenPwm* pwm, uint8_t channel_id, uint32_t freq, uint8_t duty); diff --git a/applications/external/spi_mem_manager/application.fam b/applications/external/spi_mem_manager/application.fam deleted file mode 100644 index c1b10bfee..000000000 --- a/applications/external/spi_mem_manager/application.fam +++ /dev/null @@ -1,17 +0,0 @@ -App( - appid="spi_mem_manager", - name="SPI Mem Manager", - apptype=FlipperAppType.EXTERNAL, - entry_point="spi_mem_app", - requires=["gui"], - stack_size=1 * 2048, - order=30, - fap_icon="images/Dip8_10px.png", - fap_category="GPIO", - fap_icon_assets="images", - fap_private_libs=[ - Lib( - name="spi", - ), - ], -) diff --git a/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_01.png b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_01.png deleted file mode 100644 index 4ff2e3042e540d8b499118e9edf4ecd180b39511..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7231 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 NY%KPgSDX4C`yZHc9FG71 diff --git a/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_03.png b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_03.png deleted file mode 100644 index 1342dc7bf95265db5d0ab2924e1ef03fe4df9943..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7308 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 diff --git a/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_rate b/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_rate deleted file mode 100644 index d8263ee98..000000000 --- a/applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_rate +++ /dev/null @@ -1 +0,0 @@ -2 \ No newline at end of file diff --git a/applications/external/spi_mem_manager/images/Dip8_10px.png b/applications/external/spi_mem_manager/images/Dip8_10px.png deleted file mode 100644 index 9de9364d13c5cbd7fe43a6df59789193d9133057..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ih{XE z)7O>#85cXB6sN>NS&&A_64!_l=ltB<)VvY~=c3falGGH1^30M91$R&1fbd2>aiAhw zPZ!4!iOaqHj$8}|9EV^1`~UonL4lUvGtG!wL5x*034X4|#-7<_>P#}{{_l#8SgPZ+ js&kpz9*+3KCj7Uj_`9EfRa>$PXb^*^tDnm{r-UW|gHJf_ diff --git a/applications/external/spi_mem_manager/images/Dip8_32x36.png b/applications/external/spi_mem_manager/images/Dip8_32x36.png deleted file mode 100644 index 8f01af2760363b31029cbc425d9a9f2e2becf96b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 977 zcmeAS@N?(olHy`uVBq!ia0vp^3P7yF!3-qto1VD9z`)E9;1l8s2$&H|6fVg?3oVGw3ym^DWNC|K_4;uvDl`*xDGP=kVibG*#E`lk^cO$zev z+}xJ~R3@KooBBeTVTU7E1RI;jy{)QdNiPgUSiTrM5ocMrlj*@Cjo*LH^-f?g|F=lv zl)Fs&L8nKli4M~yCl?8HDNptH;hVjxE12WemhD};j?@R{i;C*6a`dzQvVm!LtxQw_ S>l}Mfba=Y@xvXZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj diff --git a/applications/external/spi_mem_manager/images/DolphinNice_96x59.png b/applications/external/spi_mem_manager/images/DolphinNice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Q8gyJRa{_+4UIP z$!=1i2t*O^Q!1)9DGybMBGF3a6SW|L6h5LVfJD{LegT4thW>zPQHvPws{yrXept!t zv3=&;bMHMf^XAC#V8_NS8##{a$PeX4*}aQh-5b`i|CfLH7_-|w{?PLw$KCsIeBHqv zeQy)T-TjJN7>~xyod%|r1hT0`619rY&>XjYN6klgf<(MUimsOtE`R=|z`J%v*qt1RpF9hwQq*vxPN&rD$57IyUT+iM0RsE`QpwMy9wjao*i^BQa%zm^2P4v8i*LT?<9 zA2&z%EDZ>+C4h(lkolCJfSRgm;7MKvGLS%0g0cuT1E>Z}@y(yWq6M~NjOGTKvDi~a zC`FNPNK&<0O;nWx4T=)fbzK6oB+DX0h~cysp_=H0T`h(j331^1kxM;3W<(a9j4}dK z+DM_|w`skwSteF6sfK(BCP1803uv0FLo1awI*j_KSd^yTn-YhGX`e`=B&3r8CjC>y zi@I9D{1T05SfaPk*8co2g*I*n^e2OIy*xISNSRa^cgV1?uFp5J0YMQB3Y3;xjT&i1 zyC$M##e?pUVhLRKj&_L)9?Wld>u%HCq;SStTN}Y*kccThRe>U`i)-U2J}i z;>oxY@%)BuZHgI3yPAgMs43vsNJP47iHfQ!qLpHl$)u9T2onYB=@#3rz-223l~=OH zs_a-*O3@VL0MW4s7KyDoOqHyNDzSA4a2n~Dsk#w2OUpDcsm-dZtbCu(W=8_*xMlVs z93AZA^Zi*3>Y66X2`KP3HXIsM5Hp%vK}90@UNN>klflv*azobR>E=QjBQG^aWtXqJ z(?B?06d3`>ZXmYMeC^(>%xg-hL0c^mM!Jei8nBQ$Q56NGx5!#@TNg^V5+9y5Z&x22##B&{B?u64ye+M3KZ=XlsY71%@jTp=Dy zHDISkcY5&@J8>5Bx!%I~{^i3@-@m}$cNaW+{nKk_j+x(U>F+k}c?6!^@X+fADi3lO z_rLKG{`yum;ZU>ayk!Z+q>VzZOn^bqQ>?UO0Drz@_TNg$rjt zNcQEpt?wS&gMaKe^8V2SorGL{ZtT%R?yPd~`n9FG(}zAOOPl5My;t&Z3(*F9I?g}- zvp)ag@#QCe{o%9hrGrn+&dpu9`rFcD;MqUV>^-?JG4|Gpeamy-PL6)jzu5T`{Cd7~ fwr}T&J8Rt1;5!ej|9##1_yo=O59dzx?S1thQe1#H diff --git a/applications/external/spi_mem_manager/images/Wiring_SPI_128x64.png b/applications/external/spi_mem_manager/images/Wiring_SPI_128x64.png deleted file mode 100644 index e6c3ce363f801cff5080d10599ca258d07783fa1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4446 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 != SPIMemChipVendorUnknown && vendor->vendor_enum != vendor_enum) - vendor++; - return vendor->vendor_name; -} - -bool spi_mem_chip_find_all(SPIMemChip* chip_info, found_chips_t found_chips) { - const SPIMemChip* chip_info_arr; - found_chips_reset(found_chips); - for(chip_info_arr = SPIMemChips; chip_info_arr->model_name != NULL; chip_info_arr++) { - if(chip_info->vendor_id != chip_info_arr->vendor_id) continue; - if(chip_info->type_id != chip_info_arr->type_id) continue; - if(chip_info->capacity_id != chip_info_arr->capacity_id) continue; - found_chips_push_back(found_chips, chip_info_arr); - } - if(found_chips_size(found_chips)) return true; - return false; -} - -void spi_mem_chip_copy_chip_info(SPIMemChip* dest, const SPIMemChip* src) { - memcpy(dest, src, sizeof(SPIMemChip)); -} - -size_t spi_mem_chip_get_size(SPIMemChip* chip) { - return (chip->size); -} - -const char* spi_mem_chip_get_vendor_name(const SPIMemChip* chip) { - return (spi_mem_chip_search_vendor_name(chip->vendor_enum)); -} - -const char* spi_mem_chip_get_vendor_name_by_enum(uint32_t vendor_enum) { - return (spi_mem_chip_search_vendor_name(vendor_enum)); -} - -const char* spi_mem_chip_get_model_name(const SPIMemChip* chip) { - return (chip->model_name); -} - -uint8_t spi_mem_chip_get_vendor_id(SPIMemChip* chip) { - return (chip->vendor_id); -} - -uint8_t spi_mem_chip_get_type_id(SPIMemChip* chip) { - return (chip->type_id); -} - -uint8_t spi_mem_chip_get_capacity_id(SPIMemChip* chip) { - return (chip->capacity_id); -} - -SPIMemChipWriteMode spi_mem_chip_get_write_mode(SPIMemChip* chip) { - return (chip->write_mode); -} - -size_t spi_mem_chip_get_page_size(SPIMemChip* chip) { - return (chip->page_size); -} - -uint32_t spi_mem_chip_get_vendor_enum(const SPIMemChip* chip) { - return ((uint32_t)chip->vendor_enum); -} diff --git a/applications/external/spi_mem_manager/lib/spi/spi_mem_chip.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip.h deleted file mode 100644 index 683937df4..000000000 --- a/applications/external/spi_mem_manager/lib/spi/spi_mem_chip.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include - -typedef struct SPIMemChip SPIMemChip; - -ARRAY_DEF(found_chips, const SPIMemChip*, M_POD_OPLIST) - -typedef enum { - SPIMemChipStatusBusy, - SPIMemChipStatusIdle, - SPIMemChipStatusError -} SPIMemChipStatus; - -typedef enum { - SPIMemChipWriteModeUnknown = 0, - SPIMemChipWriteModePage = (0x01 << 0), - SPIMemChipWriteModeAAIByte = (0x01 << 1), - SPIMemChipWriteModeAAIWord = (0x01 << 2), -} SPIMemChipWriteMode; - -const char* spi_mem_chip_get_vendor_name(const SPIMemChip* chip); -const char* spi_mem_chip_get_model_name(const SPIMemChip* chip); -size_t spi_mem_chip_get_size(SPIMemChip* chip); -uint8_t spi_mem_chip_get_vendor_id(SPIMemChip* chip); -uint8_t spi_mem_chip_get_type_id(SPIMemChip* chip); -uint8_t spi_mem_chip_get_capacity_id(SPIMemChip* chip); -SPIMemChipWriteMode spi_mem_chip_get_write_mode(SPIMemChip* chip); -size_t spi_mem_chip_get_page_size(SPIMemChip* chip); -bool spi_mem_chip_find_all(SPIMemChip* chip_info, found_chips_t found_chips); -void spi_mem_chip_copy_chip_info(SPIMemChip* dest, const SPIMemChip* src); -uint32_t spi_mem_chip_get_vendor_enum(const SPIMemChip* chip); -const char* spi_mem_chip_get_vendor_name_by_enum(uint32_t vendor_enum); diff --git a/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_arr.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_arr.c deleted file mode 100644 index 25b237d68..000000000 --- a/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_arr.c +++ /dev/null @@ -1,1399 +0,0 @@ -#include "spi_mem_chip_i.h" -const SPIMemChip SPIMemChips[] = { - {0x1F, 0x40, 0x00, "AT25DN256", 32768, 256, SPIMemChipVendorADESTO, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x20, "A25L05PT", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x10, "A25L05PU", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x21, "A25L10PT", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x11, "A25L10PU", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x22, "A25L20PT", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x12, "A25L20PU", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x23, "A25L40PT", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x13, "A25L40PU", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x24, "A25L80PT", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x14, "A25L80PU", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x25, "A25L16PT", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x15, "A25L16PU", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x10, "A25L512", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x11, "A25L010", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x12, "A25L020", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x13, "A25L040", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x14, "A25L080", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x15, "A25L016", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x16, "A25L032", 4194304, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x40, 0x15, "A25LQ16", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x37, 0x40, 0x16, "A25LQ32A", 4194304, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, - {0x68, 0x40, 0x14, "BY25D80", 1048576, 256, SPIMemChipVendorBoya, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x10, "EN25B05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x10, "EN25B05T", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x11, "EN25B10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x11, "EN25B10T", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x12, "EN25B20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x12, "EN25B20T", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x13, "EN25B40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x13, "EN25B40T", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x14, "EN25B80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x14, "EN25B80T", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x15, "EN25B16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x15, "EN25B16T", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x16, "EN25B32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x16, "EN25B32T", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x17, "EN25B64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x17, "EN25B64T", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x10, "EN25F05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x11, "EN25F10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x12, "EN25F20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x13, "EN25F40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x14, "EN25F80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x15, "EN25F16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x16, "EN25F32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x10, "EN25LF05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x11, "EN25LF10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x12, "EN25LF20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x13, "EN25LF40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x10, "EN25P05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x11, "EN25P10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x12, "EN25P20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x13, "EN25P40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x14, "EN25P80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x15, "EN25P16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x16, "EN25P32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x17, "EN25P64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x30, 0x13, "EN25Q40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x30, 0x14, "EN25Q80A", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x30, 0x15, "EN25Q16A", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x30, 0x16, "EN25Q32A", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x70, 0x16, "EN25Q32A", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x30, 0x16, "EN25Q32B", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x30, 0x17, "EN25Q64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x30, 0x18, "EN25Q128", 16777216, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x70, 0x15, "EN25QH16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x70, 0x16, "EN25QH32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x70, 0x17, "EN25QH64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x70, 0x18, "EN25QH128", 16777216, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x70, 0x19, "EN25QH256", 33554432, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x51, 0x14, "EN25T80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x51, 0x15, "EN25T16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x1C, 0x31, 0x17, "EN25F64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, - {0x7F, 0x9D, 0x7C, "Pm25LV010", 131072, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, - {0x7F, 0x9D, 0x21, "Pm25LD010", 131072, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, - {0x7F, 0x9D, 0x22, "Pm25LV020", 262144, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, - {0x7F, 0x9D, 0x7D, "Pm25W020", 262144, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, - {0x7F, 0x9D, 0x7E, "Pm25LV040", 524288, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x10, "TS25L512A", 65536, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x11, "TS25L010A", 131072, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x12, "TS25L020A", 262144, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x15, "TS25L16AP", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x15, "TS25L16BP", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x15, "ZP25L16P", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0x20, 0x80, 0x15, "TS25L16PE", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0x20, 0x80, 0x14, "TS25L80PE", 1048576, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x16, "TS25L032A", 4194304, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x13, "TS25L40P", 524288, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x10, - "GPR25L005E", - 65536, - 256, - SPIMemChipVendorGeneralplus, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x15, - "GPR25L161B", - 262144, - 256, - SPIMemChipVendorGeneralplus, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x12, - "GPR25L020B", - 262144, - 256, - SPIMemChipVendorGeneralplus, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "GPR25L3203F", - 4194304, - 256, - SPIMemChipVendorGeneralplus, - SPIMemChipWriteModePage}, - {0x9D, 0x7B, 0x00, "AC25LV512", 65536, 256, SPIMemChipVendorDEUTRON, SPIMemChipWriteModePage}, - {0x9D, 0x7C, 0x00, "AC25LV010", 131072, 256, SPIMemChipVendorDEUTRON, SPIMemChipWriteModePage}, - {0x9D, 0x7B, 0x00, "EM25LV512", 65536, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x9D, 0x7C, 0x00, "EM25LV010", 131072, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x20, 0x13, "F25L004A", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x20, 0x14, "F25L008A", 1048576, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x20, 0x15, "F25L016A", 2097152, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x8C, 0x8C, "F25L04UA", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x20, 0x13, "F25L04P", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x30, 0x13, "F25S04P", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x20, 0x14, "F25L08P", 1048576, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x20, 0x15, "F25L16P", 2097152, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x20, 0x16, "F25L32P", 4194304, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x8C, 0x40, 0x16, "F25L32Q", 4194304, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, - {0x4A, 0x20, 0x11, "ES25P10", 131072, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, - {0x4A, 0x20, 0x12, "ES25P20", 262144, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, - {0x4A, 0x20, 0x13, "ES25P40", 524288, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, - {0x4A, 0x20, 0x14, "ES25P80", 1048576, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, - {0x4A, 0x20, 0x15, "ES25P16", 2097152, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, - {0x4A, 0x20, 0x16, "ES25P32", 4194304, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, - {0x4A, 0x32, 0x13, "ES25M40A", 524288, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, - {0x4A, 0x32, 0x14, "ES25M80A", 1048576, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, - {0x4A, 0x32, 0x15, "ES25M16A", 2097152, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, - {0xF8, 0x32, 0x14, "FM25Q08A", 1048576, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, - {0xF8, 0x32, 0x15, "FM25Q16A", 2097152, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, - {0xF8, 0x32, 0x15, "FM25Q16B", 2097152, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, - {0xF8, 0x32, 0x16, "FM25Q32A", 4194304, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, - {0xF8, 0x32, 0x17, "FM25Q64A", 8388608, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, - {0xC8, 0x30, 0x13, "GD25D40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, 0x30, 0x14, "GD25D80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, 0x20, 0x13, "GD25F40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, 0x20, 0x14, "GD25F80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, 0x40, 0x10, "GD25Q512", 65536, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, 0x40, 0x11, "GD25Q10", 131072, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, 0x40, 0x12, "GD25Q20", 262144, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, - 0x60, - 0x12, - "GD25LQ20C_1.8V", - 262144, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, 0x40, 0x13, "GD25Q40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, 0x40, 0x14, "GD25Q80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, - 0x40, - 0x14, - "GD25Q80B", - 1048576, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, - 0x40, - 0x14, - "GD25Q80C", - 1048576, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, 0x40, 0x15, "GD25Q16", 2097152, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, - 0x40, - 0x15, - "GD25Q16B", - 2097152, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, 0x40, 0x16, "GD25Q32", 4194304, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, - 0x40, - 0x16, - "GD25Q32B", - 4194304, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, 0x40, 0x17, "GD25Q64", 8388608, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0xC8, - 0x40, - 0x17, - "GD25Q64B", - 8388608, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, - 0x40, - 0x17, - "GD25B64C", - 8388608, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, - 0x40, - 0x18, - "GD25Q128B", - 16777216, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, - 0x40, - 0x18, - "GD25Q128C", - 16777216, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, - 0x60, - 0x17, - "GD25LQ064C_1.8V", - 8388608, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, - 0x60, - 0x18, - "GD25LQ128C_1.8V", - 16777216, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, - 0x60, - 0x19, - "GD25LQ256C_1.8V", - 33554432, - 256, - SPIMemChipVendorGIGADEVICE, - SPIMemChipWriteModePage}, - {0xC8, 0x31, 0x14, "MD25T80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0x51, 0x40, 0x12, "MD25D20", 262144, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0x51, 0x40, 0x13, "MD25D40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0x51, 0x40, 0x14, "MD25D80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0x51, 0x40, 0x15, "MD25D16", 2097152, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, - {0x1C, 0x20, 0x10, "ICE25P05", 65536, 128, SPIMemChipVendorICE, SPIMemChipWriteModePage}, - {0x89, 0x89, 0x11, "QB25F016S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, - {0x89, 0x89, 0x11, "QB25F160S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, - {0x89, 0x89, 0x12, "QB25F320S33B", 4194304, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, - {0x89, 0x89, 0x13, "QB25F640S33B", 8388608, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, - {0x89, 0x89, 0x11, "QH25F016S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, - {0x89, 0x89, 0x11, "QH25F160S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, - {0x89, 0x89, 0x12, "QH25F320S33B", 4194304, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x11, "KH25L1005", 131072, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x11, "KH25L1005A", 131072, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x12, "KH25L2005", 262144, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x13, "KH25L4005", 524288, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x13, "KH25L4005A", 524288, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x10, "KH25L512", 65536, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x10, "KH25L512A", 65536, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x14, "KH25L8005", 1048576, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, - {0xC2, 0x26, 0x15, "KH25L8036D", 1048576, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x11, "MX25L1005", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x11, "MX25L1005A", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x11, "MX25L1005C", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x11, "MX25L1006E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x22, 0x11, "MX25L1021E", 131072, 32, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x11, "MX25L1025C", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x11, "MX25L1026E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12805D", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12835E", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12835F", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12836E", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12839F", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12845E", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12845G", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12845F", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12865E", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12865F", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12873F", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x18, - "MX25L12875F", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x19, - "MX25L25635E", - 33554432, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x15, "MX25L1605", 2097152, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x15, - "MX25L1605A", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x15, - "MX25L1605D", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x15, - "MX25L1606E", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x24, - 0x15, - "MX25L1633E", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x24, - 0x15, - "MX25L1635D", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x15, - "MX25L1635E", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x24, - 0x15, - "MX25L1636D", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x15, - "MX25L1636E", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x24, - 0x15, - "MX25L1673E", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x24, - 0x15, - "MX25L1675E", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x12, "MX25L2005", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x12, "MX25L2005C", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x12, "MX25L2006E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x12, "MX25L2026C", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x12, "MX25L2026E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x16, "MX25L3205", 4194304, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "MX25L3205A", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "MX25L3205D", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "MX25L3206E", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "MX25L3208E", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x5E, - 0x16, - "MX25L3225D", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "MX25L3233F", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x5E, - 0x16, - "MX25L3235D", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "MX25L3235E", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x5E, - 0x16, - "MX25L3236D", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x5E, - 0x16, - "MX25L3237D", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x36, - "MX25L3239E", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "MX25L3273E", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "MX25L3273F", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x16, - "MX25L3275E", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x13, "MX25L4005", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x13, "MX25L4005A", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x13, "MX25L4005C", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x13, "MX25L4006E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x13, "MX25L4026E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x10, "MX25L512", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x10, "MX25L512A", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x10, "MX25L512C", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x22, 0x10, "MX25L5121E", 65536, 32, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x17, "MX25L6405", 8388608, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6405D", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6406E", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6408E", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6433F", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6435E", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6436E", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6436F", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x37, - "MX25L6439E", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6445E", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6465E", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6473E", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6473F", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x17, - "MX25L6475E", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x14, "MX25L8005", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x14, - "MX25L8006E", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x14, - "MX25L8008E", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x14, - "MX25L8035E", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x14, - "MX25L8036E", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x14, - "MX25L8073E", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x14, - "MX25L8075E", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x19, - "MX25L25673G", - 33554432, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, 0x28, 0x10, "MX25R512F", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x28, 0x11, "MX25R1035F", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x28, - 0x15, - "MX25R1635F", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, 0x28, 0x12, "MX25R2035F", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x28, - 0x16, - "MX25R3235F", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, 0x28, 0x13, "MX25R4035F", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x28, - 0x17, - "MX25R6435F", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x28, - 0x14, - "MX25R8035F", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x31, - "MX25U1001E_1.8V", - 131072, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x18, - "MX25U12835F_1.8V", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x39, - "MX25U25673G_1.8V", - 33554432, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x39, - "MX25U25645G_1.8V", - 33554432, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x35, - "MX25U1635E_1.8V", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x35, - "MX25U1635F_1.8V", - 2097152, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x32, - "MX25U2032E_1.8V", - 262144, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x32, - "MX25U2033E_1.8V", - 262144, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x36, - "MX25U3235E_1.8V", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x36, - "MX25U3235F_1.8V", - 4194304, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x33, - "MX25U4032E_1.8V", - 524288, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x33, - "MX25U4033E_1.8V", - 524288, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x33, - "MX25U4035_1.8V", - 524288, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x30, - "MX25U5121E_1.8V", - 65536, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x37, - "MX25U6435F_1.8V", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x37, - "MX25U6473F_1.8V", - 8388608, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x34, - "MX25U8032E_1.8V", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x34, - "MX25U8033E_1.8V", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x34, - "MX25U8035_1.8V", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x34, - "MX25U8035E_1.8V", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x38, - "MX25U12873F_1.8V", - 16777216, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x11, "MX25V1006E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x23, 0x11, "MX25V1035F", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x12, "MX25V2006E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x23, 0x12, "MX25V2035F", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x10, "MX25V512", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x10, "MX25V512C", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x10, "MX25V512E", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x23, 0x10, "MX25V512F", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x13, "MX25V4005", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x13, "MX25V4006E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x25, 0x53, "MX25V4035", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x23, 0x13, "MX25V4035F", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, 0x20, 0x14, "MX25V8005", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x20, - 0x14, - "MX25V8006E", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, 0x25, 0x54, "MX25V8035", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, - {0xC2, - 0x23, - 0x14, - "MX25V8035F", - 1048576, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x3A, - "MX66U51235F_1.8V", - 67108864, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0xC2, - 0x25, - 0x3B, - "MX66U1G45G_1.8V", - 134217728, - 256, - SPIMemChipVendorMACRONIX, - SPIMemChipWriteModePage}, - {0x20, 0xBA, 0x16, "N25Q032A", 4194304, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, - {0x20, 0xBA, 0x17, "N25Q064A", 8388608, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, - {0x20, 0xBA, 0x19, "N25Q256A13", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, - {0x20, 0xBA, 0x20, "N25Q512A83", 67108864, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, - {0x2C, 0xCB, 0x19, "N25W256A11", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, - {0x20, - 0xBA, - 0x18, - "MT25QL128AB", - 16777216, - 256, - SPIMemChipVendorMICRON, - SPIMemChipWriteModePage}, - {0x20, 0xBA, 0x19, "MT25QL256A", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, - {0x20, 0xBA, 0x20, "MT25QL512A", 67108864, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, - {0x20, - 0xBA, - 0x22, - "MT25QL02GC", - 268435456, - 256, - SPIMemChipVendorMICRON, - SPIMemChipWriteModePage}, - {0x20, 0xBB, 0x19, "MT25QU256", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, - {0x20, - 0xBA, - 0x21, - "N25Q00AA13G", - 134217728, - 256, - SPIMemChipVendorMICRON, - SPIMemChipWriteModePage}, - {0x37, 0x30, 0x10, "MS25X512", 65536, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x11, "MS25X10", 131072, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x12, "MS25X20", 262144, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x13, "MS25X40", 524288, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x14, "MS25X80", 1048576, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x15, "MS25X16", 2097152, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x16, "MS25X32", 4194304, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, - {0xD5, 0x30, 0x11, "N25S10", 131072, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, - {0xD5, 0x30, 0x12, "N25S20", 262144, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, - {0xD5, 0x30, 0x13, "N25S40", 524288, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, - {0xD5, 0x30, 0x15, "N25S16", 2097152, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, - {0xD5, 0x30, 0x16, "N25S32", 4194304, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, - {0xD5, 0x30, 0x14, "N25S80", 1048576, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, - {0x9D, 0x7F, 0x7C, "NX25P10", 131072, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, - {0xEF, 0x20, 0x15, "NX25P16", 2097152, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, - {0x9D, 0x7F, 0x7D, "NX25P20", 262144, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, - {0xEF, 0x20, 0x16, "NX25P32", 4194304, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, - {0x9D, 0x7F, 0x7E, "NX25P40", 524288, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, - {0x9D, 0x7F, 0x13, "NX25P80", 1048576, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, - {0x20, 0x40, 0x15, "M45PE16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x10, "M25P05", 65536, 128, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x10, "M25P05A", 65536, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x11, "M25P10", 131072, 128, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x11, "M25P10A", 131072, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x12, "M25P20", 262144, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x13, "M25P40", 524288, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x14, "M25P80", 1048576, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x15, "M25P16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x16, "M25P32", 4194304, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x17, "M25P64", 8388608, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, - 0x20, - 0x18, - "M25P128_ST25P28V6G", - 16777216, - 256, - SPIMemChipVendorNUMONYX, - SPIMemChipWriteModePage}, - {0x20, 0x80, 0x11, "M25PE10", 131072, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x80, 0x15, "M25PE16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x80, 0x12, "M25PE20", 262144, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x80, 0x13, "M25PE40", 524288, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0x20, 0x80, 0x14, "M25PE80", 1048576, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, - {0xBF, 0x43, 0x00, "PCT25LF020A", 262144, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, - {0xBF, 0x49, 0x00, "PCT25VF010A", 131072, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, - {0xBF, 0x25, 0x41, "PCT25VF016B", 2097152, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, - {0xBF, 0x43, 0x00, "PCT25VF020A", 262144, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, - {0xBF, 0x25, 0x4A, "PCT25VF032B", 4194304, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, - {0xBF, 0x44, 0x00, "PCT25VF040A", 524288, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, - {0xBF, 0x25, 0x8D, "PCT25VF040B", 524288, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, - {0xBF, 0x25, 0x8E, "PCT25VF080B", 1048576, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x10, "S25FL001D", 131072, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x11, "S25FL002D", 262144, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x12, "S25FL004A", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x12, "S25FL004D", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x13, "S25FL004K", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x13, "S25FL008A", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x13, "S25FL008D", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x14, "S25FL008K", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x14, "S25FL016A", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x15, "S25FL016K", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x15, "S25FL032A", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x16, "S25FL032K", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x15, "S25FL032P", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x12, "S25FL040A", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, - 0x02, - 0x26, - "S25FL040A_BOT", - 524288, - 256, - SPIMemChipVendorSPANSION, - SPIMemChipWriteModePage}, - {0x01, - 0x02, - 0x25, - "S25FL040A_TOP", - 524288, - 256, - SPIMemChipVendorSPANSION, - SPIMemChipWriteModePage}, - {0x01, 0x02, 0x16, "S25FL064A", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x17, "S25FL064K", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x02, 0x16, "S25FL064P", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x40, 0x15, "S25FL116K", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0xEF, - 0x40, - 0x18, - "S25FL128K", - 16777216, - 256, - SPIMemChipVendorSPANSION, - SPIMemChipWriteModePage}, - {0x01, - 0x20, - 0x18, - "S25FL128P", - 16777216, - 256, - SPIMemChipVendorSPANSION, - SPIMemChipWriteModePage}, - {0x01, - 0x20, - 0x18, - "S25FL128S", - 16777216, - 256, - SPIMemChipVendorSPANSION, - SPIMemChipWriteModePage}, - {0x01, 0x40, 0x16, "S25FL132K", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, 0x40, 0x17, "S25FL164K", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, - {0x01, - 0x02, - 0x19, - "S25FL256S", - 33554432, - 256, - SPIMemChipVendorSPANSION, - SPIMemChipWriteModePage}, - {0xBF, 0x25, 0x41, "SST25VF016B", 2097152, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, - {0xBF, 0x25, 0x8C, "SST25VF020B", 262144, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, - {0xBF, 0x25, 0x4A, "SST25VF032B", 4194304, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, - {0xBF, 0x25, 0x4B, "SST25VF064C", 8388608, 256, SPIMemChipVendorSST, SPIMemChipWriteModePage}, - {0xBF, 0x25, 0x8D, "SST25VF040B", 524288, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, - {0xBF, 0x25, 0x8E, "SST25VF080B", 1048576, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, - {0x20, 0x71, 0x15, "M25PX16", 2097152, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x71, 0x16, "M25PX32", 4194304, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x71, 0x17, "M25PX64", 8388608, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x71, 0x14, "M25PX80", 1048576, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x10, "ST25P05", 65536, 128, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x10, "ST25P05A", 65536, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x11, "ST25P10", 131072, 128, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x11, "ST25P10A", 131072, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x15, "ST25P16", 2097152, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x12, "ST25P20", 262144, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x16, "ST25P32", 4194304, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x13, "ST25P40", 524288, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x17, "ST25P64", 8388608, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x14, "ST25P80", 1048576, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, - {0xEF, 0x10, 0x00, "W25P10", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x20, 0x15, "W25P16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x11, 0x00, "W25P20", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x20, 0x16, "W25P32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x12, 0x00, "W25P40", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x20, 0x17, "W25P64", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x20, 0x14, "W25P80", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, - 0x60, - 0x11, - "W25Q10EW_1.8V", - 131072, - 256, - SPIMemChipVendorWINBOND, - SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x18, "W25Q128BV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x18, "W25Q128FV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x70, 0x18, "W25Q128JV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x19, "W25Q256FV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x19, "W25Q256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x70, 0x19, "W25Q256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, - 0x60, - 0x18, - "W25Q128FW_1.8V", - 16777216, - 256, - SPIMemChipVendorWINBOND, - SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x15, "W25Q16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x15, "W25Q16BV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x15, "W25Q16CL", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x15, "W25Q16CV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x15, "W25Q16DV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, - 0x60, - 0x15, - "W25Q16FW_1.8V", - 2097152, - 256, - SPIMemChipVendorWINBOND, - SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x15, "W25Q16V", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x12, "W25Q20CL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, - 0x60, - 0x12, - "W25Q20EW_1.8V", - 262144, - 256, - SPIMemChipVendorWINBOND, - SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x16, "W25Q32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x16, "W25Q32BV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x16, "W25Q32FV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, - 0x60, - 0x16, - "W25Q32FW_1.8V", - 4194304, - 256, - SPIMemChipVendorWINBOND, - SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x16, "W25Q32V", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x13, "W25Q40BL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x13, "W25Q40BV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x13, "W25Q40CL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, - 0x60, - 0x13, - "W25Q40EW_1.8V", - 524288, - 256, - SPIMemChipVendorWINBOND, - SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x17, "W25Q64BV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x17, "W25Q64CV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x17, "W25Q64FV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x17, "W25Q64JV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, - 0x60, - 0x17, - "W25Q64FW_1.8V", - 8388608, - 256, - SPIMemChipVendorWINBOND, - SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x14, "W25Q80BL", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x14, "W25Q80BV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, - 0x50, - 0x14, - "W25Q80BW_1.8V", - 1048576, - 256, - SPIMemChipVendorWINBOND, - SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x14, "W25Q80DV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, - 0x60, - 0x14, - "W25Q80EW_1.8V", - 1048576, - 256, - SPIMemChipVendorWINBOND, - SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x10, "W25X05", 65536, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x10, "W25X05CL", 65536, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x11, "W25X10AV", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x11, "W25X10BL", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x11, "W25X10BV", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x11, "W25X10CL", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x11, "W25X10L", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x11, "W25X10V", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x15, "W25X16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x15, "W25X16AL", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x15, "W25X16AV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x15, "W25X16BV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x15, "W25X16V", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x12, "W25X20AL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x12, "W25X20AV", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x12, "W25X20BL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x12, "W25X20BV", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x12, "W25X20CL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x12, "W25X20L", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x12, "W25X20V", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x16, "W25X32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x16, "W25X32AV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x16, "W25X32BV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x16, "W25X32V", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x13, "W25X40AL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x13, "W25X40AV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x13, "W25X40BL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x13, "W25X40BV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x13, "W25X40CL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x13, "W25X40L", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x13, "W25X40V", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x17, "W25X64", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x17, "W25X64BV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x17, "W25X64V", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x14, "W25X80AL", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x14, "W25X80AV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x14, "W25X80BV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x14, "W25X80L", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x30, 0x14, "W25X80V", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x71, 0x19, "W25M512JV", 67108864, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0xEF, 0x40, 0x19, "W25R256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x10, "TS25L512A", 65536, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x11, "TS25L010A", 131072, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, - {0x37, 0x30, 0x12, "TS25L020A", 262144, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x15, "TS25L16AP", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, - {0x20, 0x20, 0x15, "TS25L16BP", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, - {0x37, 0x20, 0x15, "TS25L16P", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, - {0x5E, 0x40, 0x15, "ZB25D16", 2097152, 256, SPIMemChipVendorZbit, SPIMemChipWriteModePage}, - {0xE0, 0x40, 0x13, "BG25Q40A", 524288, 256, SPIMemChipVendorBerg_Micro, SPIMemChipWriteModePage}, - {0xE0, - 0x40, - 0x14, - "BG25Q80A", - 1048576, - 256, - SPIMemChipVendorBerg_Micro, - SPIMemChipWriteModePage}, - {0xE0, - 0x40, - 0x15, - "BG25Q16A", - 2097152, - 256, - SPIMemChipVendorBerg_Micro, - SPIMemChipWriteModePage}, - {0xE0, - 0x40, - 0x16, - "BG25Q32A", - 4194304, - 256, - SPIMemChipVendorBerg_Micro, - SPIMemChipWriteModePage}, - {0x1F, 0x23, 0x00, "AT45DB021D", 270336, 264, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x24, 0x00, "AT45DB041D", 540672, 264, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x26, 0x00, "AT45DB161D", 2162688, 528, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x27, 0x01, "AT45DB321D", 4325376, 528, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x43, 0x00, "AT25DF021", 262144, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x44, 0x00, "AT25DF041", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x44, 0x00, "AT25DF041A", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x84, 0x00, "AT25SF041", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x45, 0x00, "AT25DF081", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x45, 0x00, "AT25DF081A", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x46, 0x00, "AT25DF161", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x47, 0x00, "AT25DF321", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x47, 0x00, "AT25DF321A", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x48, 0x00, "AT25DF641", 8388608, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x65, 0x00, "AT25F512B", 65536, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x45, 0x00, "AT26DF081", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x45, 0x00, "AT26DF081A", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x46, 0x00, "AT26DF161", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x46, 0x00, "AT26DF161A", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x47, 0x00, "AT26DF321", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x47, 0x00, "AT26DF321A", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0x1F, 0x04, 0x00, "AT26F004", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, - {0xE0, - 0x60, - 0x18, - "ACE25A128G_1.8V", - 16777216, - 256, - SPIMemChipVendorACE, - SPIMemChipWriteModePage}, - {0x9B, 0x32, 0x16, "ATO25Q32", 4194304, 256, SPIMemChipVendorATO, SPIMemChipWriteModePage}, - {0x54, 0x40, 0x17, "DQ25Q64A", 8388608, 256, SPIMemChipVendorDOUQI, SPIMemChipWriteModePage}, - {0x0E, 0x40, 0x15, "FT25H16", 2097152, 256, SPIMemChipVendorFremont, SPIMemChipWriteModePage}, - {0xA1, 0x40, 0x13, "FM25Q04A", 524288, 256, SPIMemChipVendorFudan, SPIMemChipWriteModePage}, - {0xA1, 0x40, 0x16, "FM25Q32", 4194304, 256, SPIMemChipVendorFudan, SPIMemChipWriteModePage}, - {0xE0, 0x40, 0x14, "GT25Q80A", 1048576, 256, SPIMemChipVendorGenitop, SPIMemChipWriteModePage}, - {0xE0, 0x40, 0x13, "PN25F04A", 524288, 256, SPIMemChipVendorParagon, SPIMemChipWriteModePage}}; diff --git a/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_i.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_i.h deleted file mode 100644 index 30d607094..000000000 --- a/applications/external/spi_mem_manager/lib/spi/spi_mem_chip_i.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include -#include "spi_mem_chip.h" - -typedef enum { - SPIMemChipVendorUnknown, - SPIMemChipVendorADESTO, - SPIMemChipVendorAMIC, - SPIMemChipVendorBoya, - SPIMemChipVendorEON, - SPIMemChipVendorPFLASH, - SPIMemChipVendorTERRA, - SPIMemChipVendorGeneralplus, - SPIMemChipVendorDEUTRON, - SPIMemChipVendorEFST, - SPIMemChipVendorEXCELSEMI, - SPIMemChipVendorFIDELIX, - SPIMemChipVendorGIGADEVICE, - SPIMemChipVendorICE, - SPIMemChipVendorINTEL, - SPIMemChipVendorKHIC, - SPIMemChipVendorMACRONIX, - SPIMemChipVendorMICRON, - SPIMemChipVendorMSHINE, - SPIMemChipVendorNANTRONICS, - SPIMemChipVendorNEXFLASH, - SPIMemChipVendorNUMONYX, - SPIMemChipVendorPCT, - SPIMemChipVendorSPANSION, - SPIMemChipVendorSST, - SPIMemChipVendorST, - SPIMemChipVendorWINBOND, - SPIMemChipVendorZEMPRO, - SPIMemChipVendorZbit, - SPIMemChipVendorBerg_Micro, - SPIMemChipVendorATMEL, - SPIMemChipVendorACE, - SPIMemChipVendorATO, - SPIMemChipVendorDOUQI, - SPIMemChipVendorFremont, - SPIMemChipVendorFudan, - SPIMemChipVendorGenitop, - SPIMemChipVendorParagon -} SPIMemChipVendor; - -typedef enum { - SPIMemChipCMDReadJEDECChipID = 0x9F, - SPIMemChipCMDReadData = 0x03, - SPIMemChipCMDChipErase = 0xC7, - SPIMemChipCMDWriteEnable = 0x06, - SPIMemChipCMDWriteDisable = 0x04, - SPIMemChipCMDReadStatus = 0x05, - SPIMemChipCMDWriteData = 0x02, - SPIMemChipCMDReleasePowerDown = 0xAB -} SPIMemChipCMD; - -enum SPIMemChipStatusBit { - SPIMemChipStatusBitBusy = (0x01 << 0), - SPIMemChipStatusBitWriteEnabled = (0x01 << 1), - SPIMemChipStatusBitBitProtection1 = (0x01 << 2), - SPIMemChipStatusBitBitProtection2 = (0x01 << 3), - SPIMemChipStatusBitBitProtection3 = (0x01 << 4), - SPIMemChipStatusBitTopBottomProtection = (0x01 << 5), - SPIMemChipStatusBitSectorProtect = (0x01 << 6), - SPIMemChipStatusBitRegisterProtect = (0x01 << 7) -}; - -typedef struct { - const char* vendor_name; - SPIMemChipVendor vendor_enum; -} SPIMemChipVendorName; - -struct SPIMemChip { - uint8_t vendor_id; - uint8_t type_id; - uint8_t capacity_id; - const char* model_name; - size_t size; - size_t page_size; - SPIMemChipVendor vendor_enum; - SPIMemChipWriteMode write_mode; -}; - -extern const SPIMemChip SPIMemChips[]; diff --git a/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.c deleted file mode 100644 index 3518ca25c..000000000 --- a/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.c +++ /dev/null @@ -1,152 +0,0 @@ -#include -#include -#include "spi_mem_chip_i.h" -#include "spi_mem_tools.h" - -static uint8_t spi_mem_tools_addr_to_byte_arr(uint32_t addr, uint8_t* cmd) { - uint8_t len = 3; // TODO(add support of 4 bytes address mode) - for(uint8_t i = 0; i < len; i++) { - cmd[i] = (addr >> ((len - (i + 1)) * 8)) & 0xFF; - } - return len; -} - -static bool spi_mem_tools_trx( - SPIMemChipCMD cmd, - uint8_t* tx_buf, - size_t tx_size, - uint8_t* rx_buf, - size_t rx_size) { - bool success = false; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external); - do { - if(!furi_hal_spi_bus_tx( - &furi_hal_spi_bus_handle_external, (uint8_t*)&cmd, 1, SPI_MEM_SPI_TIMEOUT)) - break; - if(tx_buf) { - if(!furi_hal_spi_bus_tx( - &furi_hal_spi_bus_handle_external, tx_buf, tx_size, SPI_MEM_SPI_TIMEOUT)) - break; - } - if(rx_buf) { - if(!furi_hal_spi_bus_rx( - &furi_hal_spi_bus_handle_external, rx_buf, rx_size, SPI_MEM_SPI_TIMEOUT)) - break; - } - success = true; - } while(0); - furi_hal_spi_release(&furi_hal_spi_bus_handle_external); - return success; -} - -static bool spi_mem_tools_write_buffer(uint8_t* data, size_t size, size_t offset) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external); - uint8_t cmd = (uint8_t)SPIMemChipCMDWriteData; - uint8_t address[4]; - uint8_t address_size = spi_mem_tools_addr_to_byte_arr(offset, address); - bool success = false; - do { - if(!furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_external, &cmd, 1, SPI_MEM_SPI_TIMEOUT)) - break; - if(!furi_hal_spi_bus_tx( - &furi_hal_spi_bus_handle_external, address, address_size, SPI_MEM_SPI_TIMEOUT)) - break; - if(!furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_external, data, size, SPI_MEM_SPI_TIMEOUT)) - break; - success = true; - } while(0); - furi_hal_spi_release(&furi_hal_spi_bus_handle_external); - return success; -} - -bool spi_mem_tools_read_chip_info(SPIMemChip* chip) { - uint8_t rx_buf[3] = {0, 0, 0}; - do { - if(!spi_mem_tools_trx(SPIMemChipCMDReadJEDECChipID, NULL, 0, rx_buf, 3)) break; - if(rx_buf[0] == 0 || rx_buf[0] == 255) break; - chip->vendor_id = rx_buf[0]; - chip->type_id = rx_buf[1]; - chip->capacity_id = rx_buf[2]; - return true; - } while(0); - return false; -} - -bool spi_mem_tools_check_chip_info(SPIMemChip* chip) { - SPIMemChip new_chip_info; - spi_mem_tools_read_chip_info(&new_chip_info); - do { - if(chip->vendor_id != new_chip_info.vendor_id) break; - if(chip->type_id != new_chip_info.type_id) break; - if(chip->capacity_id != new_chip_info.capacity_id) break; - return true; - } while(0); - return false; -} - -bool spi_mem_tools_read_block(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size) { - if(!spi_mem_tools_check_chip_info(chip)) return false; - for(size_t i = 0; i < block_size; i += SPI_MEM_MAX_BLOCK_SIZE) { - uint8_t cmd[4]; - if((offset + SPI_MEM_MAX_BLOCK_SIZE) > chip->size) return false; - if(!spi_mem_tools_trx( - SPIMemChipCMDReadData, - cmd, - spi_mem_tools_addr_to_byte_arr(offset, cmd), - data, - SPI_MEM_MAX_BLOCK_SIZE)) - return false; - offset += SPI_MEM_MAX_BLOCK_SIZE; - data += SPI_MEM_MAX_BLOCK_SIZE; - } - return true; -} - -size_t spi_mem_tools_get_file_max_block_size(SPIMemChip* chip) { - UNUSED(chip); - return (SPI_MEM_FILE_BUFFER_SIZE); -} - -SPIMemChipStatus spi_mem_tools_get_chip_status(SPIMemChip* chip) { - UNUSED(chip); - uint8_t status; - if(!spi_mem_tools_trx(SPIMemChipCMDReadStatus, NULL, 0, &status, 1)) - return SPIMemChipStatusError; - if(status & SPIMemChipStatusBitBusy) return SPIMemChipStatusBusy; - return SPIMemChipStatusIdle; -} - -static bool spi_mem_tools_set_write_enabled(SPIMemChip* chip, bool enable) { - UNUSED(chip); - uint8_t status; - SPIMemChipCMD cmd = SPIMemChipCMDWriteDisable; - if(enable) cmd = SPIMemChipCMDWriteEnable; - do { - if(!spi_mem_tools_trx(cmd, NULL, 0, NULL, 0)) break; - if(!spi_mem_tools_trx(SPIMemChipCMDReadStatus, NULL, 0, &status, 1)) break; - if(!(status & SPIMemChipStatusBitWriteEnabled) && enable) break; - if((status & SPIMemChipStatusBitWriteEnabled) && !enable) break; - return true; - } while(0); - return false; -} - -bool spi_mem_tools_erase_chip(SPIMemChip* chip) { - do { - if(!spi_mem_tools_set_write_enabled(chip, true)) break; - if(!spi_mem_tools_trx(SPIMemChipCMDChipErase, NULL, 0, NULL, 0)) break; - return true; - } while(0); - return true; -} - -bool spi_mem_tools_write_bytes(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size) { - do { - if(!spi_mem_tools_check_chip_info(chip)) break; - if(!spi_mem_tools_set_write_enabled(chip, true)) break; - if((offset + block_size) > chip->size) break; - if(!spi_mem_tools_write_buffer(data, block_size, offset)) break; - return true; - } while(0); - return false; -} diff --git a/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.h deleted file mode 100644 index ad006b8ff..000000000 --- a/applications/external/spi_mem_manager/lib/spi/spi_mem_tools.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "spi_mem_chip.h" - -#define SPI_MEM_SPI_TIMEOUT 1000 -#define SPI_MEM_MAX_BLOCK_SIZE 256 -#define SPI_MEM_FILE_BUFFER_SIZE 4096 - -bool spi_mem_tools_read_chip_info(SPIMemChip* chip); -bool spi_mem_tools_read_block(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size); -size_t spi_mem_tools_get_file_max_block_size(SPIMemChip* chip); -SPIMemChipStatus spi_mem_tools_get_chip_status(SPIMemChip* chip); -bool spi_mem_tools_erase_chip(SPIMemChip* chip); -bool spi_mem_tools_write_bytes(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size); diff --git a/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.c deleted file mode 100644 index 438f338f1..000000000 --- a/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.c +++ /dev/null @@ -1,129 +0,0 @@ -#include "spi_mem_worker_i.h" - -typedef enum { - SPIMemEventStopThread = (1 << 0), - SPIMemEventChipDetect = (1 << 1), - SPIMemEventRead = (1 << 2), - SPIMemEventVerify = (1 << 3), - SPIMemEventErase = (1 << 4), - SPIMemEventWrite = (1 << 5), - SPIMemEventAll = - (SPIMemEventStopThread | SPIMemEventChipDetect | SPIMemEventRead | SPIMemEventVerify | - SPIMemEventErase | SPIMemEventWrite) -} SPIMemEventEventType; - -static int32_t spi_mem_worker_thread(void* thread_context); - -SPIMemWorker* spi_mem_worker_alloc() { - SPIMemWorker* worker = malloc(sizeof(SPIMemWorker)); - worker->callback = NULL; - worker->thread = furi_thread_alloc(); - worker->mode_index = SPIMemWorkerModeIdle; - furi_thread_set_name(worker->thread, "SPIMemWorker"); - furi_thread_set_callback(worker->thread, spi_mem_worker_thread); - furi_thread_set_context(worker->thread, worker); - furi_thread_set_stack_size(worker->thread, 10240); - return worker; -} - -void spi_mem_worker_free(SPIMemWorker* worker) { - furi_thread_free(worker->thread); - free(worker); -} - -bool spi_mem_worker_check_for_stop(SPIMemWorker* worker) { - UNUSED(worker); - uint32_t flags = furi_thread_flags_get(); - return (flags & SPIMemEventStopThread); -} - -static int32_t spi_mem_worker_thread(void* thread_context) { - SPIMemWorker* worker = thread_context; - while(true) { - uint32_t flags = furi_thread_flags_wait(SPIMemEventAll, FuriFlagWaitAny, FuriWaitForever); - if(flags != (unsigned)FuriFlagErrorTimeout) { - if(flags & SPIMemEventStopThread) break; - if(flags & SPIMemEventChipDetect) worker->mode_index = SPIMemWorkerModeChipDetect; - if(flags & SPIMemEventRead) worker->mode_index = SPIMemWorkerModeRead; - if(flags & SPIMemEventVerify) worker->mode_index = SPIMemWorkerModeVerify; - if(flags & SPIMemEventErase) worker->mode_index = SPIMemWorkerModeErase; - if(flags & SPIMemEventWrite) worker->mode_index = SPIMemWorkerModeWrite; - if(spi_mem_worker_modes[worker->mode_index].process) { - spi_mem_worker_modes[worker->mode_index].process(worker); - } - worker->mode_index = SPIMemWorkerModeIdle; - } - } - return 0; -} - -void spi_mem_worker_start_thread(SPIMemWorker* worker) { - furi_thread_start(worker->thread); -} - -void spi_mem_worker_stop_thread(SPIMemWorker* worker) { - furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventStopThread); - furi_thread_join(worker->thread); -} - -void spi_mem_worker_chip_detect_start( - SPIMemChip* chip_info, - found_chips_t* found_chips, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context) { - furi_check(worker->mode_index == SPIMemWorkerModeIdle); - worker->callback = callback; - worker->cb_ctx = context; - worker->chip_info = chip_info; - worker->found_chips = found_chips; - furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventChipDetect); -} - -void spi_mem_worker_read_start( - SPIMemChip* chip_info, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context) { - furi_check(worker->mode_index == SPIMemWorkerModeIdle); - worker->callback = callback; - worker->cb_ctx = context; - worker->chip_info = chip_info; - furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventRead); -} - -void spi_mem_worker_verify_start( - SPIMemChip* chip_info, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context) { - furi_check(worker->mode_index == SPIMemWorkerModeIdle); - worker->callback = callback; - worker->cb_ctx = context; - worker->chip_info = chip_info; - furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventVerify); -} - -void spi_mem_worker_erase_start( - SPIMemChip* chip_info, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context) { - furi_check(worker->mode_index == SPIMemWorkerModeIdle); - worker->callback = callback; - worker->cb_ctx = context; - worker->chip_info = chip_info; - furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventErase); -} - -void spi_mem_worker_write_start( - SPIMemChip* chip_info, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context) { - furi_check(worker->mode_index == SPIMemWorkerModeIdle); - worker->callback = callback; - worker->cb_ctx = context; - worker->chip_info = chip_info; - furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventWrite); -} diff --git a/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.h deleted file mode 100644 index c3761cd5a..000000000 --- a/applications/external/spi_mem_manager/lib/spi/spi_mem_worker.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#include "spi_mem_chip.h" - -typedef struct SPIMemWorker SPIMemWorker; - -typedef struct { - void (*const process)(SPIMemWorker* worker); -} SPIMemWorkerModeType; - -typedef enum { - SPIMemCustomEventWorkerChipIdentified, - SPIMemCustomEventWorkerChipUnknown, - SPIMemCustomEventWorkerBlockReaded, - SPIMemCustomEventWorkerChipFail, - SPIMemCustomEventWorkerFileFail, - SPIMemCustomEventWorkerDone, - SPIMemCustomEventWorkerVerifyFail, -} SPIMemCustomEventWorker; - -typedef void (*SPIMemWorkerCallback)(void* context, SPIMemCustomEventWorker event); - -SPIMemWorker* spi_mem_worker_alloc(); -void spi_mem_worker_free(SPIMemWorker* worker); -void spi_mem_worker_start_thread(SPIMemWorker* worker); -void spi_mem_worker_stop_thread(SPIMemWorker* worker); -bool spi_mem_worker_check_for_stop(SPIMemWorker* worker); -void spi_mem_worker_chip_detect_start( - SPIMemChip* chip_info, - found_chips_t* found_chips, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context); -void spi_mem_worker_read_start( - SPIMemChip* chip_info, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context); -void spi_mem_worker_verify_start( - SPIMemChip* chip_info, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context); -void spi_mem_worker_erase_start( - SPIMemChip* chip_info, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context); -void spi_mem_worker_write_start( - SPIMemChip* chip_info, - SPIMemWorker* worker, - SPIMemWorkerCallback callback, - void* context); diff --git a/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_i.h b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_i.h deleted file mode 100644 index 43e2d2287..000000000 --- a/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_i.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "spi_mem_worker.h" - -typedef enum { - SPIMemWorkerModeIdle, - SPIMemWorkerModeChipDetect, - SPIMemWorkerModeRead, - SPIMemWorkerModeVerify, - SPIMemWorkerModeErase, - SPIMemWorkerModeWrite -} SPIMemWorkerMode; - -struct SPIMemWorker { - SPIMemChip* chip_info; - found_chips_t* found_chips; - SPIMemWorkerMode mode_index; - SPIMemWorkerCallback callback; - void* cb_ctx; - FuriThread* thread; - FuriString* file_name; -}; - -extern const SPIMemWorkerModeType spi_mem_worker_modes[]; diff --git a/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_modes.c b/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_modes.c deleted file mode 100644 index a393e5490..000000000 --- a/applications/external/spi_mem_manager/lib/spi/spi_mem_worker_modes.c +++ /dev/null @@ -1,214 +0,0 @@ -#include "spi_mem_worker_i.h" -#include "spi_mem_chip.h" -#include "spi_mem_tools.h" -#include "../../spi_mem_files.h" - -static void spi_mem_worker_chip_detect_process(SPIMemWorker* worker); -static void spi_mem_worker_read_process(SPIMemWorker* worker); -static void spi_mem_worker_verify_process(SPIMemWorker* worker); -static void spi_mem_worker_erase_process(SPIMemWorker* worker); -static void spi_mem_worker_write_process(SPIMemWorker* worker); - -const SPIMemWorkerModeType spi_mem_worker_modes[] = { - [SPIMemWorkerModeIdle] = {.process = NULL}, - [SPIMemWorkerModeChipDetect] = {.process = spi_mem_worker_chip_detect_process}, - [SPIMemWorkerModeRead] = {.process = spi_mem_worker_read_process}, - [SPIMemWorkerModeVerify] = {.process = spi_mem_worker_verify_process}, - [SPIMemWorkerModeErase] = {.process = spi_mem_worker_erase_process}, - [SPIMemWorkerModeWrite] = {.process = spi_mem_worker_write_process}}; - -static void spi_mem_worker_run_callback(SPIMemWorker* worker, SPIMemCustomEventWorker event) { - if(worker->callback) { - worker->callback(worker->cb_ctx, event); - } -} - -static bool spi_mem_worker_await_chip_busy(SPIMemWorker* worker) { - while(true) { - furi_delay_tick(10); // to give some time to OS - if(spi_mem_worker_check_for_stop(worker)) return true; - SPIMemChipStatus chip_status = spi_mem_tools_get_chip_status(worker->chip_info); - if(chip_status == SPIMemChipStatusError) return false; - if(chip_status == SPIMemChipStatusBusy) continue; - return true; - } -} - -static size_t spi_mem_worker_modes_get_total_size(SPIMemWorker* worker) { - size_t chip_size = spi_mem_chip_get_size(worker->chip_info); - size_t file_size = spi_mem_file_get_size(worker->cb_ctx); - size_t total_size = chip_size; - if(chip_size > file_size) total_size = file_size; - return total_size; -} - -// ChipDetect -static void spi_mem_worker_chip_detect_process(SPIMemWorker* worker) { - SPIMemCustomEventWorker event; - while(!spi_mem_tools_read_chip_info(worker->chip_info)) { - furi_delay_tick(10); // to give some time to OS - if(spi_mem_worker_check_for_stop(worker)) return; - } - if(spi_mem_chip_find_all(worker->chip_info, *worker->found_chips)) { - event = SPIMemCustomEventWorkerChipIdentified; - } else { - event = SPIMemCustomEventWorkerChipUnknown; - } - spi_mem_worker_run_callback(worker, event); -} - -// Read -static bool spi_mem_worker_read(SPIMemWorker* worker, SPIMemCustomEventWorker* event) { - uint8_t data_buffer[SPI_MEM_FILE_BUFFER_SIZE]; - size_t chip_size = spi_mem_chip_get_size(worker->chip_info); - size_t offset = 0; - bool success = true; - while(true) { - furi_delay_tick(10); // to give some time to OS - size_t block_size = SPI_MEM_FILE_BUFFER_SIZE; - if(spi_mem_worker_check_for_stop(worker)) break; - if(offset >= chip_size) break; - if((offset + block_size) > chip_size) block_size = chip_size - offset; - if(!spi_mem_tools_read_block(worker->chip_info, offset, data_buffer, block_size)) { - *event = SPIMemCustomEventWorkerChipFail; - success = false; - break; - } - if(!spi_mem_file_write_block(worker->cb_ctx, data_buffer, block_size)) { - success = false; - break; - } - offset += block_size; - spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded); - } - if(success) *event = SPIMemCustomEventWorkerDone; - return success; -} - -static void spi_mem_worker_read_process(SPIMemWorker* worker) { - SPIMemCustomEventWorker event = SPIMemCustomEventWorkerFileFail; - do { - if(!spi_mem_worker_await_chip_busy(worker)) break; - if(!spi_mem_file_create_open(worker->cb_ctx)) break; - if(!spi_mem_worker_read(worker, &event)) break; - } while(0); - spi_mem_file_close(worker->cb_ctx); - spi_mem_worker_run_callback(worker, event); -} - -// Verify -static bool - spi_mem_worker_verify(SPIMemWorker* worker, size_t total_size, SPIMemCustomEventWorker* event) { - uint8_t data_buffer_chip[SPI_MEM_FILE_BUFFER_SIZE]; - uint8_t data_buffer_file[SPI_MEM_FILE_BUFFER_SIZE]; - size_t offset = 0; - bool success = true; - while(true) { - furi_delay_tick(10); // to give some time to OS - size_t block_size = SPI_MEM_FILE_BUFFER_SIZE; - if(spi_mem_worker_check_for_stop(worker)) break; - if(offset >= total_size) break; - if((offset + block_size) > total_size) block_size = total_size - offset; - if(!spi_mem_tools_read_block(worker->chip_info, offset, data_buffer_chip, block_size)) { - *event = SPIMemCustomEventWorkerChipFail; - success = false; - break; - } - if(!spi_mem_file_read_block(worker->cb_ctx, data_buffer_file, block_size)) { - success = false; - break; - } - if(memcmp(data_buffer_chip, data_buffer_file, block_size) != 0) { - *event = SPIMemCustomEventWorkerVerifyFail; - success = false; - break; - } - offset += block_size; - spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded); - } - if(success) *event = SPIMemCustomEventWorkerDone; - return success; -} - -static void spi_mem_worker_verify_process(SPIMemWorker* worker) { - SPIMemCustomEventWorker event = SPIMemCustomEventWorkerFileFail; - size_t total_size = spi_mem_worker_modes_get_total_size(worker); - do { - if(!spi_mem_worker_await_chip_busy(worker)) break; - if(!spi_mem_file_open(worker->cb_ctx)) break; - if(!spi_mem_worker_verify(worker, total_size, &event)) break; - } while(0); - spi_mem_file_close(worker->cb_ctx); - spi_mem_worker_run_callback(worker, event); -} - -// Erase -static void spi_mem_worker_erase_process(SPIMemWorker* worker) { - SPIMemCustomEventWorker event = SPIMemCustomEventWorkerChipFail; - do { - if(!spi_mem_worker_await_chip_busy(worker)) break; - if(!spi_mem_tools_erase_chip(worker->chip_info)) break; - if(!spi_mem_worker_await_chip_busy(worker)) break; - event = SPIMemCustomEventWorkerDone; - } while(0); - spi_mem_worker_run_callback(worker, event); -} - -// Write -static bool spi_mem_worker_write_block_by_page( - SPIMemWorker* worker, - size_t offset, - uint8_t* data, - size_t block_size, - size_t page_size) { - for(size_t i = 0; i < block_size; i += page_size) { - if(!spi_mem_worker_await_chip_busy(worker)) return false; - if(!spi_mem_tools_write_bytes(worker->chip_info, offset, data, page_size)) return false; - offset += page_size; - data += page_size; - } - return true; -} - -static bool - spi_mem_worker_write(SPIMemWorker* worker, size_t total_size, SPIMemCustomEventWorker* event) { - bool success = true; - uint8_t data_buffer[SPI_MEM_FILE_BUFFER_SIZE]; - size_t page_size = spi_mem_chip_get_page_size(worker->chip_info); - size_t offset = 0; - while(true) { - furi_delay_tick(10); // to give some time to OS - size_t block_size = SPI_MEM_FILE_BUFFER_SIZE; - if(spi_mem_worker_check_for_stop(worker)) break; - if(offset >= total_size) break; - if((offset + block_size) > total_size) block_size = total_size - offset; - if(!spi_mem_file_read_block(worker->cb_ctx, data_buffer, block_size)) { - *event = SPIMemCustomEventWorkerFileFail; - success = false; - break; - } - if(!spi_mem_worker_write_block_by_page( - worker, offset, data_buffer, block_size, page_size)) { - success = false; - break; - } - offset += block_size; - spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded); - } - return success; -} - -static void spi_mem_worker_write_process(SPIMemWorker* worker) { - SPIMemCustomEventWorker event = SPIMemCustomEventWorkerChipFail; - size_t total_size = - spi_mem_worker_modes_get_total_size(worker); // need to be executed before opening file - do { - if(!spi_mem_file_open(worker->cb_ctx)) break; - if(!spi_mem_worker_await_chip_busy(worker)) break; - if(!spi_mem_worker_write(worker, total_size, &event)) break; - if(!spi_mem_worker_await_chip_busy(worker)) break; - event = SPIMemCustomEventWorkerDone; - } while(0); - spi_mem_file_close(worker->cb_ctx); - spi_mem_worker_run_callback(worker, event); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene.c deleted file mode 100644 index 7780005f4..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "spi_mem_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const spi_mem_on_enter_handlers[])(void*) = { -#include "spi_mem_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const spi_mem_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "spi_mem_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const spi_mem_on_exit_handlers[])(void* context) = { -#include "spi_mem_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers spi_mem_scene_handlers = { - .on_enter_handlers = spi_mem_on_enter_handlers, - .on_event_handlers = spi_mem_on_event_handlers, - .on_exit_handlers = spi_mem_on_exit_handlers, - .scene_num = SPIMemSceneNum, -}; diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene.h b/applications/external/spi_mem_manager/scenes/spi_mem_scene.h deleted file mode 100644 index 2ac6d21e3..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) SPIMemScene##id, -typedef enum { -#include "spi_mem_scene_config.h" - SPIMemSceneNum, -} SPIMemScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers spi_mem_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "spi_mem_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "spi_mem_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "spi_mem_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_about.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_about.c deleted file mode 100644 index dc0cc4fe4..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_about.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../lib/spi/spi_mem_chip.h" - -#define SPI_MEM_VERSION_APP "0.1.0" -#define SPI_MEM_DEVELOPER "DrunkBatya" -#define SPI_MEM_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" -#define SPI_MEM_NAME "\e#\e! SPI Mem Manager \e!\n" -#define SPI_MEM_BLANK_INV "\e#\e! \e!\n" - -void spi_mem_scene_about_on_enter(void* context) { - SPIMemApp* app = context; - FuriString* tmp_string = furi_string_alloc(); - - widget_add_text_box_element( - app->widget, 0, 0, 128, 14, AlignCenter, AlignBottom, SPI_MEM_BLANK_INV, false); - widget_add_text_box_element( - app->widget, 0, 2, 128, 14, AlignCenter, AlignBottom, SPI_MEM_NAME, false); - furi_string_printf(tmp_string, "\e#%s\n", "Information"); - furi_string_cat_printf(tmp_string, "Version: %s\n", SPI_MEM_VERSION_APP); - furi_string_cat_printf(tmp_string, "Developed by: %s\n", SPI_MEM_DEVELOPER); - furi_string_cat_printf(tmp_string, "Github: %s\n\n", SPI_MEM_GITHUB); - furi_string_cat_printf(tmp_string, "\e#%s\n", "Description"); - furi_string_cat_printf( - tmp_string, - "SPI memory dumper\n" - "Originally written by Hedger, ghettorce and x893 at\n" - "Flipper Hackathon 2021\n\n"); - widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(tmp_string)); - - furi_string_free(tmp_string); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); -} - -bool spi_mem_scene_about_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} -void spi_mem_scene_about_on_exit(void* context) { - SPIMemApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c deleted file mode 100644 index d9b8f0aa3..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "../spi_mem_app_i.h" - -static void spi_mem_scene_chip_detect_callback(void* context, SPIMemCustomEventWorker event) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void spi_mem_scene_chip_detect_on_enter(void* context) { - SPIMemApp* app = context; - notification_message(app->notifications, &sequence_blink_start_yellow); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewDetect); - spi_mem_worker_start_thread(app->worker); - spi_mem_worker_chip_detect_start( - app->chip_info, &app->found_chips, app->worker, spi_mem_scene_chip_detect_callback, app); -} - -bool spi_mem_scene_chip_detect_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == SPIMemCustomEventWorkerChipIdentified) { - scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, 0); - scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectVendor); - } else if(event.event == SPIMemCustomEventWorkerChipUnknown) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetectFail); - } - } - return success; -} - -void spi_mem_scene_chip_detect_on_exit(void* context) { - SPIMemApp* app = context; - spi_mem_worker_stop_thread(app->worker); - notification_message(app->notifications, &sequence_blink_stop); - popup_reset(app->popup); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c deleted file mode 100644 index 876a28721..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../lib/spi/spi_mem_chip.h" - -static void spi_mem_scene_chip_detect_fail_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - SPIMemApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -void spi_mem_scene_chip_detect_fail_on_enter(void* context) { - SPIMemApp* app = context; - FuriString* str = furi_string_alloc(); - widget_add_button_element( - app->widget, - GuiButtonTypeCenter, - "Retry", - spi_mem_scene_chip_detect_fail_widget_callback, - app); - widget_add_string_element( - app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Detected"); - widget_add_string_element( - app->widget, 64, 20, AlignCenter, AlignBottom, FontPrimary, "unknown SPI chip"); - furi_string_printf(str, "Vendor\nid: 0x%02X", spi_mem_chip_get_vendor_id(app->chip_info)); - widget_add_string_multiline_element( - app->widget, 16, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); - furi_string_printf(str, "Type\nid: 0x%02X", spi_mem_chip_get_type_id(app->chip_info)); - widget_add_string_multiline_element( - app->widget, 64, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); - furi_string_printf(str, "Capacity\nid: 0x%02X", spi_mem_chip_get_capacity_id(app->chip_info)); - widget_add_string_multiline_element( - app->widget, 110, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); - furi_string_free(str); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); -} - -bool spi_mem_scene_chip_detect_fail_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SPIMemSceneStart); - } else if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == GuiButtonTypeCenter) { - scene_manager_previous_scene(app->scene_manager); - } - } - return success; -} -void spi_mem_scene_chip_detect_fail_on_exit(void* context) { - SPIMemApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c deleted file mode 100644 index 539578a45..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c +++ /dev/null @@ -1,94 +0,0 @@ -#include "../spi_mem_app_i.h" - -static void spi_mem_scene_chip_detected_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - SPIMemApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -static void spi_mem_scene_chip_detected_print_chip_info(Widget* widget, SPIMemChip* chip_info) { - FuriString* tmp_string = furi_string_alloc(); - widget_add_string_element( - widget, - 40, - 12, - AlignLeft, - AlignTop, - FontSecondary, - spi_mem_chip_get_vendor_name(chip_info)); - widget_add_string_element( - widget, 40, 20, AlignLeft, AlignTop, FontSecondary, spi_mem_chip_get_model_name(chip_info)); - furi_string_printf(tmp_string, "Size: %zu KB", spi_mem_chip_get_size(chip_info) / 1024); - widget_add_string_element( - widget, 40, 28, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string)); - furi_string_free(tmp_string); -} - -static void spi_mem_scene_chip_detect_draw_next_button(SPIMemApp* app) { - FuriString* str = furi_string_alloc(); - if(app->mode == SPIMemModeRead) furi_string_printf(str, "%s", "Read"); - if(app->mode == SPIMemModeWrite) furi_string_printf(str, "%s", "Write"); - if(app->mode == SPIMemModeErase) furi_string_printf(str, "%s", "Erase"); - if(app->mode == SPIMemModeCompare) furi_string_printf(str, "%s", "Check"); - widget_add_button_element( - app->widget, - GuiButtonTypeRight, - furi_string_get_cstr(str), - spi_mem_scene_chip_detected_widget_callback, - app); - furi_string_free(str); -} - -static void spi_mem_scene_chip_detected_set_previous_scene(SPIMemApp* app) { - uint32_t scene = SPIMemSceneStart; - if(app->mode == SPIMemModeCompare || app->mode == SPIMemModeWrite) - scene = SPIMemSceneSavedFileMenu; - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); -} - -static void spi_mem_scene_chip_detected_set_next_scene(SPIMemApp* app) { - uint32_t scene = SPIMemSceneStart; - if(app->mode == SPIMemModeRead) scene = SPIMemSceneReadFilename; - if(app->mode == SPIMemModeWrite) scene = SPIMemSceneErase; - if(app->mode == SPIMemModeErase) scene = SPIMemSceneErase; - if(app->mode == SPIMemModeCompare) scene = SPIMemSceneVerify; - scene_manager_next_scene(app->scene_manager, scene); -} - -void spi_mem_scene_chip_detected_on_enter(void* context) { - SPIMemApp* app = context; - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Retry", spi_mem_scene_chip_detected_widget_callback, app); - spi_mem_scene_chip_detect_draw_next_button(app); - widget_add_icon_element(app->widget, 0, 12, &I_Dip8_32x36); - widget_add_string_element( - app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Detected SPI chip"); - spi_mem_scene_chip_detected_print_chip_info(app->widget, app->chip_info); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); -} - -bool spi_mem_scene_chip_detected_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - spi_mem_scene_chip_detected_set_previous_scene(app); - } else if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == GuiButtonTypeLeft) { - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, SPIMemSceneChipDetect); - } else if(event.event == GuiButtonTypeRight) { - spi_mem_scene_chip_detected_set_next_scene(app); - } - } - return success; -} -void spi_mem_scene_chip_detected_on_exit(void* context) { - SPIMemApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_error.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_error.c deleted file mode 100644 index ca4b765a2..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_error.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "../spi_mem_app_i.h" - -static void - spi_mem_scene_chip_error_widget_callback(GuiButtonType result, InputType type, void* context) { - SPIMemApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -void spi_mem_scene_chip_error_on_enter(void* context) { - SPIMemApp* app = context; - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_chip_error_widget_callback, app); - widget_add_string_element( - app->widget, 85, 15, AlignCenter, AlignBottom, FontPrimary, "SPI chip error"); - widget_add_string_multiline_element( - app->widget, - 85, - 52, - AlignCenter, - AlignBottom, - FontSecondary, - "Error while\ncommunicating\nwith chip"); - widget_add_icon_element(app->widget, 5, 6, &I_Dip8_32x36); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); -} - -static void spi_mem_scene_chip_error_set_previous_scene(SPIMemApp* app) { - uint32_t scene = SPIMemSceneChipDetect; - if(app->mode == SPIMemModeRead || app->mode == SPIMemModeErase) scene = SPIMemSceneStart; - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); -} - -bool spi_mem_scene_chip_error_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - spi_mem_scene_chip_error_set_previous_scene(app); - } else if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == GuiButtonTypeLeft) { - spi_mem_scene_chip_error_set_previous_scene(app); - } - } - return success; -} -void spi_mem_scene_chip_error_on_exit(void* context) { - SPIMemApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_config.h b/applications/external/spi_mem_manager/scenes/spi_mem_scene_config.h deleted file mode 100644 index c0e377303..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_config.h +++ /dev/null @@ -1,21 +0,0 @@ -ADD_SCENE(spi_mem, start, Start) -ADD_SCENE(spi_mem, chip_detect, ChipDetect) -ADD_SCENE(spi_mem, chip_detected, ChipDetected) -ADD_SCENE(spi_mem, chip_detect_fail, ChipDetectFail) -ADD_SCENE(spi_mem, select_file, SelectFile) -ADD_SCENE(spi_mem, saved_file_menu, SavedFileMenu) -ADD_SCENE(spi_mem, read, Read) -ADD_SCENE(spi_mem, read_filename, ReadFilename) -ADD_SCENE(spi_mem, delete_confirm, DeleteConfirm) -ADD_SCENE(spi_mem, success, Success) -ADD_SCENE(spi_mem, about, About) -ADD_SCENE(spi_mem, verify, Verify) -ADD_SCENE(spi_mem, file_info, FileInfo) -ADD_SCENE(spi_mem, erase, Erase) -ADD_SCENE(spi_mem, chip_error, ChipError) -ADD_SCENE(spi_mem, verify_error, VerifyError) -ADD_SCENE(spi_mem, write, Write) -ADD_SCENE(spi_mem, storage_error, StorageError) -ADD_SCENE(spi_mem, select_vendor, SelectVendor) -ADD_SCENE(spi_mem, select_model, SelectModel) -ADD_SCENE(spi_mem, wiring, Wiring) diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c deleted file mode 100644 index bb5142452..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../spi_mem_files.h" - -static void spi_mem_scene_delete_confirm_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - SPIMemApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -void spi_mem_scene_delete_confirm_on_enter(void* context) { - SPIMemApp* app = context; - FuriString* file_name = furi_string_alloc(); - FuriString* message = furi_string_alloc(); - path_extract_filename(app->file_path, file_name, true); - furi_string_printf(message, "\e#Delete %s?\e#", furi_string_get_cstr(file_name)); - widget_add_text_box_element( - app->widget, 0, 0, 128, 27, AlignCenter, AlignCenter, furi_string_get_cstr(message), true); - widget_add_button_element( - app->widget, - GuiButtonTypeLeft, - "Cancel", - spi_mem_scene_delete_confirm_widget_callback, - app); - widget_add_button_element( - app->widget, - GuiButtonTypeRight, - "Delete", - spi_mem_scene_delete_confirm_widget_callback, - app); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); - furi_string_free(file_name); - furi_string_free(message); -} - -bool spi_mem_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == GuiButtonTypeRight) { - app->mode = SPIMemModeDelete; - if(spi_mem_file_delete(app)) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess); - } else { - scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); - } - } else if(event.event == GuiButtonTypeLeft) { - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, SPIMemSceneSavedFileMenu); - } - } - return success; -} - -void spi_mem_scene_delete_confirm_on_exit(void* context) { - SPIMemApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_erase.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_erase.c deleted file mode 100644 index 0d3ae66bf..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_erase.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "../spi_mem_app_i.h" - -static void - spi_mem_scene_erase_widget_callback(GuiButtonType result, InputType type, void* context) { - SPIMemApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -static void spi_mem_scene_erase_callback(void* context, SPIMemCustomEventWorker event) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void spi_mem_scene_erase_on_enter(void* context) { - SPIMemApp* app = context; - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Cancel", spi_mem_scene_erase_widget_callback, app); - widget_add_string_element( - app->widget, 64, 15, AlignCenter, AlignBottom, FontPrimary, "Erasing SPI chip"); - widget_add_string_element( - app->widget, 64, 27, AlignCenter, AlignBottom, FontSecondary, "Please be patient"); - notification_message(app->notifications, &sequence_blink_start_magenta); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); - spi_mem_worker_start_thread(app->worker); - spi_mem_worker_erase_start(app->chip_info, app->worker, spi_mem_scene_erase_callback, app); -} - -static void spi_mem_scene_erase_set_previous_scene(SPIMemApp* app) { - uint32_t scene = SPIMemSceneStart; - if(app->mode == SPIMemModeWrite) scene = SPIMemSceneSavedFileMenu; - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); -} - -static void spi_mem_scene_erase_set_next_scene(SPIMemApp* app) { - uint32_t scene = SPIMemSceneSuccess; - if(app->mode == SPIMemModeWrite) scene = SPIMemSceneWrite; - scene_manager_next_scene(app->scene_manager, scene); -} - -bool spi_mem_scene_erase_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - spi_mem_scene_erase_set_previous_scene(app); - } else if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == GuiButtonTypeLeft) { - scene_manager_previous_scene(app->scene_manager); - } else if(event.event == SPIMemCustomEventWorkerDone) { - spi_mem_scene_erase_set_next_scene(app); - } else if(event.event == SPIMemCustomEventWorkerChipFail) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); - } - } - return success; -} -void spi_mem_scene_erase_on_exit(void* context) { - SPIMemApp* app = context; - spi_mem_worker_stop_thread(app->worker); - notification_message(app->notifications, &sequence_blink_stop); - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_file_info.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_file_info.c deleted file mode 100644 index 687f17f81..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_file_info.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../spi_mem_files.h" - -void spi_mem_scene_file_info_on_enter(void* context) { - SPIMemApp* app = context; - FuriString* str = furi_string_alloc(); - furi_string_printf(str, "Size: %zu KB", spi_mem_file_get_size(app) / 1024); - widget_add_string_element( - app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "File info"); - widget_add_string_element( - app->widget, 64, 20, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); - furi_string_free(str); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); -} - -bool spi_mem_scene_file_info_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, SPIMemSceneSavedFileMenu); - } - return success; -} -void spi_mem_scene_file_info_on_exit(void* context) { - SPIMemApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_read.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_read.c deleted file mode 100644 index bbf38a303..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_read.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../spi_mem_files.h" -#include "../lib/spi/spi_mem_chip.h" -#include "../lib/spi/spi_mem_tools.h" - -void spi_mem_scene_read_progress_view_result_callback(void* context) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewReadCancel); -} - -static void spi_mem_scene_read_callback(void* context, SPIMemCustomEventWorker event) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void spi_mem_scene_read_on_enter(void* context) { - SPIMemApp* app = context; - spi_mem_view_progress_set_read_callback( - app->view_progress, spi_mem_scene_read_progress_view_result_callback, app); - notification_message(app->notifications, &sequence_blink_start_blue); - spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info)); - spi_mem_view_progress_set_block_size( - app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info)); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress); - spi_mem_worker_start_thread(app->worker); - spi_mem_worker_read_start(app->chip_info, app->worker, spi_mem_scene_read_callback, app); -} - -bool spi_mem_scene_read_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - UNUSED(app); - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - } else if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == SPIMemCustomEventViewReadCancel) { - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, SPIMemSceneChipDetect); - } else if(event.event == SPIMemCustomEventWorkerBlockReaded) { - spi_mem_view_progress_inc_progress(app->view_progress); - } else if(event.event == SPIMemCustomEventWorkerDone) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneVerify); - } else if(event.event == SPIMemCustomEventWorkerChipFail) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); - } else if(event.event == SPIMemCustomEventWorkerFileFail) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); - } - } - return success; -} -void spi_mem_scene_read_on_exit(void* context) { - SPIMemApp* app = context; - spi_mem_worker_stop_thread(app->worker); - spi_mem_view_progress_reset(app->view_progress); - notification_message(app->notifications, &sequence_blink_stop); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_read_filename.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_read_filename.c deleted file mode 100644 index 4b16baa2e..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_read_filename.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../spi_mem_files.h" - -void spi_mem_scene_read_filename_view_result_callback(void* context) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventTextEditResult); -} - -void spi_mem_scene_read_set_random_filename(SPIMemApp* app) { - if(furi_string_end_with(app->file_path, SPI_MEM_FILE_EXTENSION)) { - size_t filename_start = furi_string_search_rchar(app->file_path, '/'); - furi_string_left(app->file_path, filename_start); - } - set_random_name(app->text_buffer, SPI_MEM_TEXT_BUFFER_SIZE); -} - -void spi_mem_scene_read_filename_on_enter(void* context) { - SPIMemApp* app = context; - spi_mem_scene_read_set_random_filename(app); - text_input_set_header_text(app->text_input, "Name the dump"); - text_input_set_result_callback( - app->text_input, - spi_mem_scene_read_filename_view_result_callback, - app, - app->text_buffer, - SPI_MEM_FILE_NAME_SIZE, - true); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewTextInput); -} - -bool spi_mem_scene_read_filename_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - UNUSED(app); - bool success = false; - if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == SPIMemCustomEventTextEditResult) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneRead); - } - } - return success; -} -void spi_mem_scene_read_filename_on_exit(void* context) { - SPIMemApp* app = context; - text_input_reset(app->text_input); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c deleted file mode 100644 index d5767455e..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "../spi_mem_app_i.h" - -typedef enum { - SPIMemSceneSavedFileMenuSubmenuIndexWrite, - SPIMemSceneSavedFileMenuSubmenuIndexCompare, - SPIMemSceneSavedFileMenuSubmenuIndexInfo, - SPIMemSceneSavedFileMenuSubmenuIndexDelete, -} SPIMemSceneSavedFileMenuSubmenuIndex; - -static void spi_mem_scene_saved_file_menu_submenu_callback(void* context, uint32_t index) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void spi_mem_scene_saved_file_menu_on_enter(void* context) { - SPIMemApp* app = context; - submenu_add_item( - app->submenu, - "Write", - SPIMemSceneSavedFileMenuSubmenuIndexWrite, - spi_mem_scene_saved_file_menu_submenu_callback, - app); - submenu_add_item( - app->submenu, - "Compare", - SPIMemSceneSavedFileMenuSubmenuIndexCompare, - spi_mem_scene_saved_file_menu_submenu_callback, - app); - submenu_add_item( - app->submenu, - "Info", - SPIMemSceneSavedFileMenuSubmenuIndexInfo, - spi_mem_scene_saved_file_menu_submenu_callback, - app); - submenu_add_item( - app->submenu, - "Delete", - SPIMemSceneSavedFileMenuSubmenuIndexDelete, - spi_mem_scene_saved_file_menu_submenu_callback, - app); - submenu_set_selected_item( - app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu)); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); -} - -bool spi_mem_scene_saved_file_menu_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu, event.event); - if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexWrite) { - app->mode = SPIMemModeWrite; - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); - success = true; - } - if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexCompare) { - app->mode = SPIMemModeCompare; - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); - success = true; - } - if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexDelete) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneDeleteConfirm); - success = true; - } - if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexInfo) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneFileInfo); - success = true; - } - } - return success; -} - -void spi_mem_scene_saved_file_menu_on_exit(void* context) { - SPIMemApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_file.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_file.c deleted file mode 100644 index cb48035b5..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_file.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../spi_mem_files.h" - -void spi_mem_scene_select_file_on_enter(void* context) { - SPIMemApp* app = context; - if(spi_mem_file_select(app)) { - scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu, 0); - scene_manager_next_scene(app->scene_manager, SPIMemSceneSavedFileMenu); - } else { - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SPIMemSceneStart); - } -} - -bool spi_mem_scene_select_file_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void spi_mem_scene_select_file_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_model.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_model.c deleted file mode 100644 index c39c4a182..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_model.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../spi_mem_app_i.h" - -static void spi_mem_scene_select_model_submenu_callback(void* context, uint32_t index) { - SPIMemApp* app = context; - spi_mem_chip_copy_chip_info(app->chip_info, *found_chips_get(app->found_chips, index)); - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void spi_mem_scene_select_model_on_enter(void* context) { - SPIMemApp* app = context; - size_t models_on_vendor = 0; - for(size_t index = 0; index < found_chips_size(app->found_chips); index++) { - if(spi_mem_chip_get_vendor_enum(*found_chips_get(app->found_chips, index)) != - app->chip_vendor_enum) - continue; - submenu_add_item( - app->submenu, - spi_mem_chip_get_model_name(*found_chips_get(app->found_chips, index)), - index, - spi_mem_scene_select_model_submenu_callback, - app); - models_on_vendor++; - } - if(models_on_vendor == 1) spi_mem_scene_select_model_submenu_callback(context, 0); - submenu_set_header(app->submenu, "Choose chip model"); - submenu_set_selected_item( - app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSelectVendor)); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); -} - -bool spi_mem_scene_select_model_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, event.event); - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetected); - success = true; - } - return success; -} - -void spi_mem_scene_select_model_on_exit(void* context) { - SPIMemApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c deleted file mode 100644 index c7f736f88..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "../spi_mem_app_i.h" -#include -#include - -ARRAY_DEF(vendors, uint32_t) -ALGO_DEF(vendors, ARRAY_OPLIST(vendors)) - -static void spi_mem_scene_select_vendor_submenu_callback(void* context, uint32_t index) { - SPIMemApp* app = context; - app->chip_vendor_enum = index; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -static void spi_mem_scene_select_vendor_sort_vendors(SPIMemApp* app, vendors_t vendors_arr) { - for(size_t index = 0; index < found_chips_size(app->found_chips); index++) { - vendors_push_back( - vendors_arr, spi_mem_chip_get_vendor_enum(*found_chips_get(app->found_chips, index))); - } - vendors_uniq(vendors_arr); -} - -void spi_mem_scene_select_vendor_on_enter(void* context) { - SPIMemApp* app = context; - vendors_t vendors_arr; - vendors_init(vendors_arr); - spi_mem_scene_select_vendor_sort_vendors(app, vendors_arr); - size_t vendors_arr_size = vendors_size(vendors_arr); - if(vendors_arr_size == 1) - spi_mem_scene_select_vendor_submenu_callback(context, *vendors_get(vendors_arr, 0)); - for(size_t index = 0; index < vendors_arr_size; index++) { - uint32_t vendor_enum = *vendors_get(vendors_arr, index); - submenu_add_item( - app->submenu, - spi_mem_chip_get_vendor_name_by_enum(vendor_enum), - vendor_enum, - spi_mem_scene_select_vendor_submenu_callback, - app); - } - vendors_clear(vendors_arr); - submenu_set_header(app->submenu, "Choose chip vendor"); - submenu_set_selected_item( - app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSelectVendor)); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); -} - -static void spi_mem_scene_select_vendor_set_previous_scene(SPIMemApp* app) { - uint32_t scene = SPIMemSceneStart; - if(app->mode == SPIMemModeCompare || app->mode == SPIMemModeWrite) - scene = SPIMemSceneSavedFileMenu; - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); -} - -bool spi_mem_scene_select_vendor_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - spi_mem_scene_select_vendor_set_previous_scene(app); - } else if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, event.event); - scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectModel); - success = true; - } - return success; -} - -void spi_mem_scene_select_vendor_on_exit(void* context) { - SPIMemApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_start.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_start.c deleted file mode 100644 index 38d064a4d..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_start.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "../spi_mem_app_i.h" - -typedef enum { - SPIMemSceneStartSubmenuIndexRead, - SPIMemSceneStartSubmenuIndexSaved, - SPIMemSceneStartSubmenuIndexErase, - SPIMemSceneStartSubmenuIndexWiring, - SPIMemSceneStartSubmenuIndexAbout -} SPIMemSceneStartSubmenuIndex; - -static void spi_mem_scene_start_submenu_callback(void* context, uint32_t index) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void spi_mem_scene_start_on_enter(void* context) { - SPIMemApp* app = context; - submenu_add_item( - app->submenu, - "Read", - SPIMemSceneStartSubmenuIndexRead, - spi_mem_scene_start_submenu_callback, - app); - submenu_add_item( - app->submenu, - "Saved", - SPIMemSceneStartSubmenuIndexSaved, - spi_mem_scene_start_submenu_callback, - app); - submenu_add_item( - app->submenu, - "Erase", - SPIMemSceneStartSubmenuIndexErase, - spi_mem_scene_start_submenu_callback, - app); - submenu_add_item( - app->submenu, - "Wiring", - SPIMemSceneStartSubmenuIndexWiring, - spi_mem_scene_start_submenu_callback, - app); - submenu_add_item( - app->submenu, - "About", - SPIMemSceneStartSubmenuIndexAbout, - spi_mem_scene_start_submenu_callback, - app); - submenu_set_selected_item( - app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneStart)); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); -} - -bool spi_mem_scene_start_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(app->scene_manager, SPIMemSceneStart, event.event); - if(event.event == SPIMemSceneStartSubmenuIndexRead) { - app->mode = SPIMemModeRead; - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); - success = true; - } else if(event.event == SPIMemSceneStartSubmenuIndexSaved) { - furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); - scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectFile); - success = true; - } else if(event.event == SPIMemSceneStartSubmenuIndexErase) { - app->mode = SPIMemModeErase; - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); - success = true; - } else if(event.event == SPIMemSceneStartSubmenuIndexWiring) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneWiring); - success = true; - } else if(event.event == SPIMemSceneStartSubmenuIndexAbout) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneAbout); - success = true; - } - } - return success; -} - -void spi_mem_scene_start_on_exit(void* context) { - SPIMemApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_storage_error.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_storage_error.c deleted file mode 100644 index d5e289e24..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_storage_error.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "../spi_mem_app_i.h" - -static void spi_mem_scene_storage_error_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - SPIMemApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -void spi_mem_scene_storage_error_on_enter(void* context) { - SPIMemApp* app = context; - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_storage_error_widget_callback, app); - widget_add_string_element( - app->widget, 85, 15, AlignCenter, AlignBottom, FontPrimary, "Storage error"); - widget_add_string_multiline_element( - app->widget, - 85, - 52, - AlignCenter, - AlignBottom, - FontSecondary, - "Error while\nworking with\nfilesystem"); - widget_add_icon_element(app->widget, 5, 6, &I_SDQuestion_35x43); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); -} - -static void spi_mem_scene_storage_error_set_previous_scene(SPIMemApp* app) { - uint32_t scene = SPIMemSceneChipDetect; - if(app->mode == SPIMemModeRead) scene = SPIMemSceneStart; - if(app->mode == SPIMemModeErase) scene = SPIMemSceneStart; - if(app->mode == SPIMemModeDelete) scene = SPIMemSceneStart; - scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); -} - -bool spi_mem_scene_storage_error_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - spi_mem_scene_storage_error_set_previous_scene(app); - } else if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == GuiButtonTypeLeft) { - spi_mem_scene_storage_error_set_previous_scene(app); - } - } - return success; -} -void spi_mem_scene_storage_error_on_exit(void* context) { - SPIMemApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_success.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_success.c deleted file mode 100644 index 39039466f..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_success.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "../spi_mem_app_i.h" - -static void spi_mem_scene_success_popup_callback(void* context) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventPopupBack); -} - -void spi_mem_scene_success_on_enter(void* context) { - SPIMemApp* app = context; - popup_set_icon(app->popup, 32, 5, &I_DolphinNice_96x59); - popup_set_header(app->popup, "Success!", 5, 7, AlignLeft, AlignTop); - popup_set_callback(app->popup, spi_mem_scene_success_popup_callback); - popup_set_context(app->popup, app); - popup_set_timeout(app->popup, 1500); - popup_enable_timeout(app->popup); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewPopup); -} - -static void spi_mem_scene_success_set_previous_scene(SPIMemApp* app) { - uint32_t scene = SPIMemSceneSelectFile; - if(app->mode == SPIMemModeErase) scene = SPIMemSceneStart; - scene_manager_search_and_switch_to_another_scene(app->scene_manager, scene); -} - -bool spi_mem_scene_success_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == SPIMemCustomEventPopupBack) { - spi_mem_scene_success_set_previous_scene(app); - } - } - return success; -} - -void spi_mem_scene_success_on_exit(void* context) { - SPIMemApp* app = context; - popup_reset(app->popup); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify.c deleted file mode 100644 index 08a8d1057..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../spi_mem_files.h" -#include "../lib/spi/spi_mem_chip.h" -#include "../lib/spi/spi_mem_tools.h" - -void spi_mem_scene_verify_view_result_callback(void* context) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewVerifySkip); -} - -static void spi_mem_scene_verify_callback(void* context, SPIMemCustomEventWorker event) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void spi_mem_scene_verify_on_enter(void* context) { - SPIMemApp* app = context; - spi_mem_view_progress_set_verify_callback( - app->view_progress, spi_mem_scene_verify_view_result_callback, app); - notification_message(app->notifications, &sequence_blink_start_cyan); - spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info)); - spi_mem_view_progress_set_file_size(app->view_progress, spi_mem_file_get_size(app)); - spi_mem_view_progress_set_block_size( - app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info)); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress); - spi_mem_worker_start_thread(app->worker); - spi_mem_worker_verify_start(app->chip_info, app->worker, spi_mem_scene_verify_callback, app); -} - -bool spi_mem_scene_verify_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - UNUSED(app); - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - } else if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == SPIMemCustomEventViewVerifySkip) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess); - } else if(event.event == SPIMemCustomEventWorkerBlockReaded) { - spi_mem_view_progress_inc_progress(app->view_progress); - } else if(event.event == SPIMemCustomEventWorkerChipFail) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); - } else if(event.event == SPIMemCustomEventWorkerFileFail) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); - } else if(event.event == SPIMemCustomEventWorkerDone) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess); - } else if(event.event == SPIMemCustomEventWorkerVerifyFail) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneVerifyError); - } - } - return success; -} -void spi_mem_scene_verify_on_exit(void* context) { - SPIMemApp* app = context; - spi_mem_worker_stop_thread(app->worker); - spi_mem_view_progress_reset(app->view_progress); - notification_message(app->notifications, &sequence_blink_stop); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify_error.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify_error.c deleted file mode 100644 index fbe954fa6..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_verify_error.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "../spi_mem_app_i.h" - -static void spi_mem_scene_verify_error_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - SPIMemApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -void spi_mem_scene_verify_error_on_enter(void* context) { - SPIMemApp* app = context; - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_verify_error_widget_callback, app); - widget_add_string_element( - app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Verification error"); - widget_add_string_element( - app->widget, 64, 21, AlignCenter, AlignBottom, FontSecondary, "Data mismatch"); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); -} - -bool spi_mem_scene_verify_error_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, SPIMemSceneChipDetect); - } else if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == GuiButtonTypeLeft) { - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, SPIMemSceneChipDetect); - } - } - return success; -} -void spi_mem_scene_verify_error_on_exit(void* context) { - SPIMemApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_wiring.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_wiring.c deleted file mode 100644 index 22036f4bc..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_wiring.c +++ /dev/null @@ -1,18 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../lib/spi/spi_mem_chip.h" - -void spi_mem_scene_wiring_on_enter(void* context) { - SPIMemApp* app = context; - widget_add_icon_element(app->widget, 0, 0, &I_Wiring_SPI_128x64); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); -} - -bool spi_mem_scene_wiring_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} -void spi_mem_scene_wiring_on_exit(void* context) { - SPIMemApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/spi_mem_manager/scenes/spi_mem_scene_write.c b/applications/external/spi_mem_manager/scenes/spi_mem_scene_write.c deleted file mode 100644 index dfa384fbb..000000000 --- a/applications/external/spi_mem_manager/scenes/spi_mem_scene_write.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../spi_mem_app_i.h" -#include "../spi_mem_files.h" -#include "../lib/spi/spi_mem_chip.h" -#include "../lib/spi/spi_mem_tools.h" - -void spi_mem_scene_write_progress_view_result_callback(void* context) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewReadCancel); -} - -static void spi_mem_scene_write_callback(void* context, SPIMemCustomEventWorker event) { - SPIMemApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void spi_mem_scene_write_on_enter(void* context) { - SPIMemApp* app = context; - spi_mem_view_progress_set_write_callback( - app->view_progress, spi_mem_scene_write_progress_view_result_callback, app); - notification_message(app->notifications, &sequence_blink_start_cyan); - spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info)); - spi_mem_view_progress_set_file_size(app->view_progress, spi_mem_file_get_size(app)); - spi_mem_view_progress_set_block_size( - app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info)); - view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress); - spi_mem_worker_start_thread(app->worker); - spi_mem_worker_write_start(app->chip_info, app->worker, spi_mem_scene_write_callback, app); -} - -bool spi_mem_scene_write_on_event(void* context, SceneManagerEvent event) { - SPIMemApp* app = context; - UNUSED(app); - bool success = false; - if(event.type == SceneManagerEventTypeBack) { - success = true; - } else if(event.type == SceneManagerEventTypeCustom) { - success = true; - if(event.event == SPIMemCustomEventViewReadCancel) { - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, SPIMemSceneChipDetect); - } else if(event.event == SPIMemCustomEventWorkerBlockReaded) { - spi_mem_view_progress_inc_progress(app->view_progress); - } else if(event.event == SPIMemCustomEventWorkerDone) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneVerify); - } else if(event.event == SPIMemCustomEventWorkerChipFail) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); - } else if(event.event == SPIMemCustomEventWorkerFileFail) { - scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); - } - } - return success; -} -void spi_mem_scene_write_on_exit(void* context) { - SPIMemApp* app = context; - spi_mem_worker_stop_thread(app->worker); - spi_mem_view_progress_reset(app->view_progress); - notification_message(app->notifications, &sequence_blink_stop); -} diff --git a/applications/external/spi_mem_manager/spi_mem_app.c b/applications/external/spi_mem_manager/spi_mem_app.c deleted file mode 100644 index 96c3632d0..000000000 --- a/applications/external/spi_mem_manager/spi_mem_app.c +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include "spi_mem_app_i.h" -#include "spi_mem_files.h" -#include "lib/spi/spi_mem_chip_i.h" - -static bool spi_mem_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - SPIMemApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool spi_mem_back_event_callback(void* context) { - furi_assert(context); - SPIMemApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -SPIMemApp* spi_mem_alloc(void) { - SPIMemApp* instance = malloc(sizeof(SPIMemApp)); //-V799 - - instance->file_path = furi_string_alloc_set(STORAGE_APP_DATA_PATH_PREFIX); - instance->gui = furi_record_open(RECORD_GUI); - instance->notifications = furi_record_open(RECORD_NOTIFICATION); - instance->view_dispatcher = view_dispatcher_alloc(); - instance->scene_manager = scene_manager_alloc(&spi_mem_scene_handlers, instance); - instance->submenu = submenu_alloc(); - instance->dialog_ex = dialog_ex_alloc(); - instance->popup = popup_alloc(); - instance->worker = spi_mem_worker_alloc(); - instance->dialogs = furi_record_open(RECORD_DIALOGS); - instance->storage = furi_record_open(RECORD_STORAGE); - instance->widget = widget_alloc(); - instance->chip_info = malloc(sizeof(SPIMemChip)); - found_chips_init(instance->found_chips); - instance->view_progress = spi_mem_view_progress_alloc(); - instance->view_detect = spi_mem_view_detect_alloc(); - instance->text_input = text_input_alloc(); - instance->mode = SPIMemModeUnknown; - - // Migrate data from old sd-card folder - storage_common_migrate(instance->storage, EXT_PATH("spimem"), STORAGE_APP_DATA_PATH_PREFIX); - - view_dispatcher_enable_queue(instance->view_dispatcher); - view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance); - view_dispatcher_set_custom_event_callback( - instance->view_dispatcher, spi_mem_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - instance->view_dispatcher, spi_mem_back_event_callback); - view_dispatcher_attach_to_gui( - instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); - view_dispatcher_add_view( - instance->view_dispatcher, SPIMemViewSubmenu, submenu_get_view(instance->submenu)); - view_dispatcher_add_view( - instance->view_dispatcher, SPIMemViewDialogEx, dialog_ex_get_view(instance->dialog_ex)); - view_dispatcher_add_view( - instance->view_dispatcher, SPIMemViewPopup, popup_get_view(instance->popup)); - view_dispatcher_add_view( - instance->view_dispatcher, SPIMemViewWidget, widget_get_view(instance->widget)); - view_dispatcher_add_view( - instance->view_dispatcher, - SPIMemViewProgress, - spi_mem_view_progress_get_view(instance->view_progress)); - view_dispatcher_add_view( - instance->view_dispatcher, - SPIMemViewDetect, - spi_mem_view_detect_get_view(instance->view_detect)); - view_dispatcher_add_view( - instance->view_dispatcher, SPIMemViewTextInput, text_input_get_view(instance->text_input)); - - furi_hal_power_enable_otg(); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external); - scene_manager_next_scene(instance->scene_manager, SPIMemSceneStart); - return instance; -} //-V773 - -void spi_mem_free(SPIMemApp* instance) { - view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewSubmenu); - view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewDialogEx); - view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewPopup); - view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewWidget); - view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewProgress); - view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewDetect); - view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewTextInput); - spi_mem_view_progress_free(instance->view_progress); - spi_mem_view_detect_free(instance->view_detect); - submenu_free(instance->submenu); - dialog_ex_free(instance->dialog_ex); - popup_free(instance->popup); - widget_free(instance->widget); - text_input_free(instance->text_input); - view_dispatcher_free(instance->view_dispatcher); - scene_manager_free(instance->scene_manager); - spi_mem_worker_free(instance->worker); - free(instance->chip_info); - found_chips_clear(instance->found_chips); - furi_record_close(RECORD_STORAGE); - furi_record_close(RECORD_DIALOGS); - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_GUI); - furi_string_free(instance->file_path); - furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_external); - furi_hal_power_disable_otg(); - free(instance); -} - -int32_t spi_mem_app(void* p) { - UNUSED(p); - SPIMemApp* instance = spi_mem_alloc(); - view_dispatcher_run(instance->view_dispatcher); - spi_mem_free(instance); - return 0; -} diff --git a/applications/external/spi_mem_manager/spi_mem_app.h b/applications/external/spi_mem_manager/spi_mem_app.h deleted file mode 100644 index 37ac927db..000000000 --- a/applications/external/spi_mem_manager/spi_mem_app.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -typedef struct SPIMemApp SPIMemApp; diff --git a/applications/external/spi_mem_manager/spi_mem_app_i.h b/applications/external/spi_mem_manager/spi_mem_app_i.h deleted file mode 100644 index 285ca66d2..000000000 --- a/applications/external/spi_mem_manager/spi_mem_app_i.h +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once - -#include -#include -#include -#include "spi_mem_app.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "scenes/spi_mem_scene.h" -#include "lib/spi/spi_mem_worker.h" -#include "spi_mem_manager_icons.h" -#include "views/spi_mem_view_progress.h" -#include "views/spi_mem_view_detect.h" - -#define TAG "SPIMem" -#define SPI_MEM_FILE_EXTENSION ".bin" -#define SPI_MEM_FILE_NAME_SIZE 100 -#define SPI_MEM_TEXT_BUFFER_SIZE 128 - -typedef enum { - SPIMemModeRead, - SPIMemModeWrite, - SPIMemModeCompare, - SPIMemModeErase, - SPIMemModeDelete, - SPIMemModeUnknown -} SPIMemMode; - -struct SPIMemApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - Submenu* submenu; - DialogEx* dialog_ex; - Popup* popup; - NotificationApp* notifications; - FuriString* file_path; - DialogsApp* dialogs; - Storage* storage; - File* file; - Widget* widget; - SPIMemWorker* worker; - SPIMemChip* chip_info; - found_chips_t found_chips; - uint32_t chip_vendor_enum; - SPIMemProgressView* view_progress; - SPIMemDetectView* view_detect; - TextInput* text_input; - SPIMemMode mode; - char text_buffer[SPI_MEM_TEXT_BUFFER_SIZE + 1]; -}; - -typedef enum { - SPIMemViewSubmenu, - SPIMemViewDialogEx, - SPIMemViewPopup, - SPIMemViewWidget, - SPIMemViewTextInput, - SPIMemViewProgress, - SPIMemViewDetect -} SPIMemView; - -typedef enum { - SPIMemCustomEventViewReadCancel, - SPIMemCustomEventViewVerifySkip, - SPIMemCustomEventTextEditResult, - SPIMemCustomEventPopupBack -} SPIMemCustomEvent; diff --git a/applications/external/spi_mem_manager/spi_mem_files.c b/applications/external/spi_mem_manager/spi_mem_files.c deleted file mode 100644 index 9b787bd7f..000000000 --- a/applications/external/spi_mem_manager/spi_mem_files.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "spi_mem_app_i.h" - -bool spi_mem_file_delete(SPIMemApp* app) { - return (storage_simply_remove(app->storage, furi_string_get_cstr(app->file_path))); -} - -bool spi_mem_file_select(SPIMemApp* app) { - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, SPI_MEM_FILE_EXTENSION, &I_Dip8_10px); - browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; - bool success = - dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); - return success; -} - -bool spi_mem_file_create_open(SPIMemApp* app) { - bool success = false; - app->file = storage_file_alloc(app->storage); - do { - if(furi_string_end_with(app->file_path, SPI_MEM_FILE_EXTENSION)) { - if(!spi_mem_file_delete(app)) break; - size_t filename_start = furi_string_search_rchar(app->file_path, '/'); - furi_string_left(app->file_path, filename_start); - } - furi_string_cat_printf(app->file_path, "/%s%s", app->text_buffer, SPI_MEM_FILE_EXTENSION); - if(!storage_file_open( - app->file, furi_string_get_cstr(app->file_path), FSAM_WRITE, FSOM_CREATE_NEW)) - break; - success = true; - } while(0); - if(!success) { //-V547 - dialog_message_show_storage_error(app->dialogs, "Cannot save\nfile"); - } - return success; -} - -bool spi_mem_file_open(SPIMemApp* app) { - app->file = storage_file_alloc(app->storage); - if(!storage_file_open( - app->file, furi_string_get_cstr(app->file_path), FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { - dialog_message_show_storage_error(app->dialogs, "Cannot save\nfile"); - return false; - } - return true; -} - -bool spi_mem_file_write_block(SPIMemApp* app, uint8_t* data, size_t size) { - if(storage_file_write(app->file, data, size) != size) return false; - return true; -} - -bool spi_mem_file_read_block(SPIMemApp* app, uint8_t* data, size_t size) { - if(storage_file_read(app->file, data, size) != size) return false; - return true; -} - -void spi_mem_file_close(SPIMemApp* app) { - storage_file_close(app->file); - storage_file_free(app->file); -} - -size_t spi_mem_file_get_size(SPIMemApp* app) { - FileInfo file_info; - if(storage_common_stat(app->storage, furi_string_get_cstr(app->file_path), &file_info) != - FSE_OK) - return 0; - return file_info.size; -} diff --git a/applications/external/spi_mem_manager/spi_mem_files.h b/applications/external/spi_mem_manager/spi_mem_files.h deleted file mode 100644 index 6a529d327..000000000 --- a/applications/external/spi_mem_manager/spi_mem_files.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "spi_mem_app.h" - -bool spi_mem_file_select(SPIMemApp* app); -bool spi_mem_file_create(SPIMemApp* app, const char* file_name); -bool spi_mem_file_delete(SPIMemApp* app); -bool spi_mem_file_create_open(SPIMemApp* app); -bool spi_mem_file_open(SPIMemApp* app); -bool spi_mem_file_write_block(SPIMemApp* app, uint8_t* data, size_t size); -bool spi_mem_file_read_block(SPIMemApp* app, uint8_t* data, size_t size); -void spi_mem_file_close(SPIMemApp* app); -void spi_mem_file_show_storage_error(SPIMemApp* app, const char* error_text); -size_t spi_mem_file_get_size(SPIMemApp* app); diff --git a/applications/external/spi_mem_manager/tools/README.md b/applications/external/spi_mem_manager/tools/README.md deleted file mode 100644 index 91080941f..000000000 --- a/applications/external/spi_mem_manager/tools/README.md +++ /dev/null @@ -1,7 +0,0 @@ -This utility can convert nofeletru's UsbAsp-flash's chiplist.xml to C array - -Usage: -```bash - ./chiplist_convert.py chiplist/chiplist.xml - mv spi_mem_chip_arr.c ../lib/spi/spi_mem_chip_arr.c -``` diff --git a/applications/external/spi_mem_manager/tools/chiplist/LICENSE b/applications/external/spi_mem_manager/tools/chiplist/LICENSE deleted file mode 100644 index 56364f150..000000000 --- a/applications/external/spi_mem_manager/tools/chiplist/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 nofeletru - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/applications/external/spi_mem_manager/tools/chiplist/chiplist.xml b/applications/external/spi_mem_manager/tools/chiplist/chiplist.xml deleted file mode 100644 index 91a654743..000000000 --- a/applications/external/spi_mem_manager/tools/chiplist/chiplist.xml +++ /dev/null @@ -1,984 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_25AA010A page="16" size="128" spicmd="95"/> - <_25AA020A page="16" size="256" spicmd="95"/> - <_25AA040 page="16" size="512" spicmd="95"/> - <_25AA040A page="16" size="512" spicmd="95"/> - <_25AA080 page="16" size="1024" spicmd="95"/> - <_25AA080A page="16" size="1024" spicmd="95"/> - <_25AA080B page="32" size="1024" spicmd="95"/> - <_25AA080C page="16" size="1024" spicmd="95"/> - <_25AA080D page="32" size="1024" spicmd="95"/> - <_25AA1024 page="256" size="131072" spicmd="95"/> - <_25AA128 page="64" size="16384" spicmd="95"/> - <_25AA160 page="16" size="2048" spicmd="95"/> - <_25AA160A page="16" size="2048" spicmd="95"/> - <_25AA160B page="32" size="2048" spicmd="95"/> - <_25AA256 page="64" size="32768" spicmd="95"/> - <_25AA320 page="32" size="4096" spicmd="95"/> - <_25AA512 page="128" size="65536" spicmd="95"/> - <_25AA640 page="32" size="8192" spicmd="95"/> - <_25C040 page="16" size="512" spicmd="95"/> - <_25C080 page="16" size="1024" spicmd="95"/> - <_25C160 page="16" size="2048" spicmd="95"/> - <_25C320 page="32" size="4096" spicmd="95"/> - <_25C640 page="32" size="8192" spicmd="95"/> - <_25LC010A page="16" size="128" spicmd="95"/> - <_25LC020A page="16" size="256" spicmd="95"/> - <_25LC040 page="16" size="512" spicmd="95"/> - <_25LC040A page="16" size="512" spicmd="95"/> - <_25LC080 page="16" size="1024" spicmd="95"/> - <_25LC080A page="16" size="1024" spicmd="95"/> - <_25LC080B page="32" size="1024" spicmd="95"/> - <_25LC080C page="16" size="1024" spicmd="95"/> - <_25LC080D page="32" size="1024" spicmd="95"/> - <_25LC1024 page="256" size="131072" spicmd="95"/> - <_25LC128 page="64" size="16384" spicmd="95"/> - <_25LC160 page="16" size="2048" spicmd="95"/> - <_25LC160A page="16" size="2048" spicmd="95"/> - <_25LC160B page="32" size="2048" spicmd="95"/> - <_25LC256 page="64" size="32768" spicmd="95"/> - <_25LC320 page="32" size="4096" spicmd="95"/> - <_25LC512 page="128" size="65536" spicmd="95"/> - <_25LC640 page="32" size="8192" spicmd="95"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <_24Cxxx> - - <_24C01 page="1" size="128" addrtype="1"/> - <_24C02 page="1" size="256" addrtype="1"/> - <_24C04 page="1" size="512" addrtype="2"/> - <_24C08 page="16" size="1024" addrtype="3"/> - <_24C16 page="16" size="2048" addrtype="4"/> - <_24C32 page="32" size="4096" addrtype="5"/> - <_24C64 page="32" size="8192" addrtype="5"/> - <_24C128 page="64" size="16384" addrtype="5"/> - <_24C256 page="64" size="32768" addrtype="5"/> - <_24C512 page="128" size="65536" addrtype="5"/> - <_24C1024 page="128" size="131072" addrtype="6"/> - - - - - - - - - - - - - diff --git a/applications/external/spi_mem_manager/tools/chiplist_convert.py b/applications/external/spi_mem_manager/tools/chiplist_convert.py deleted file mode 100755 index 8b623eb3e..000000000 --- a/applications/external/spi_mem_manager/tools/chiplist_convert.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import xml.etree.ElementTree as XML -import sys - - -def getArgs(): - parser = argparse.ArgumentParser( - description="chiplist.xml to C array converter", - ) - parser.add_argument("file", help="chiplist.xml file") - return parser.parse_args() - - -def getXML(file): - tree = XML.parse(file) - root = tree.getroot() - return root - - -def parseChip(cur, arr, vendor, vendorCodeArr): - chip = {} - chipAttr = cur.attrib - if "page" not in chipAttr: # chip without page size not supported - return - if "id" not in chipAttr: # I2C not supported yet - return - if len(chipAttr["id"]) < 6: # ID wihout capacity id not supported yet - return - chip["modelName"] = cur.tag - chip["vendorEnum"] = "SPIMemChipVendor" + vendor - chip["vendorID"] = "0x" + chipAttr["id"][0] + chipAttr["id"][1] - chip["typeID"] = chipAttr["id"][2] + chipAttr["id"][3] - chip["capacityID"] = chipAttr["id"][4] + chipAttr["id"][5] - chip["size"] = chipAttr["size"] - if chipAttr["page"] == "SSTW": - chip["writeMode"] = "SPIMemChipWriteModeAAIWord" - chip["pageSize"] = "1" - elif chipAttr["page"] == "SSTB": - chip["writeMode"] = "SPIMemChipWriteModeAAIByte" - chip["pageSize"] = "1" - else: - chip["writeMode"] = "SPIMemChipWriteModePage" - chip["pageSize"] = chipAttr["page"] - arr.append(chip) - vendorCodeArr[vendor].add(chip["vendorID"]) - - -def cleanEmptyVendors(vendors): - for cur in list(vendors): - if not vendors[cur]: - vendors.pop(cur) - - -def getVendors(xml, interface): - arr = {} - for cur in xml.find(interface): - arr[cur.tag] = set() - return arr - - -def parseXML(xml, interface, vendorCodeArr): - arr = [] - for vendor in xml.find(interface): - for cur in vendor: - parseChip(cur, arr, vendor.tag, vendorCodeArr) - return arr - - -def getVendorNameEnum(vendorID): - try: - return vendors[vendorID] - except: - print("Unknown vendor: " + vendorID) - sys.exit(1) - - -def generateCArr(arr, filename): - with open(filename, "w") as out: - print('#include "spi_mem_chip_i.h"', file=out) - print("const SPIMemChip SPIMemChips[] = {", file=out) - for cur in arr: - print(" {" + cur["vendorID"] + ",", file=out, end="") - print(" 0x" + cur["typeID"] + ",", file=out, end="") - print(" 0x" + cur["capacityID"] + ",", file=out, end="") - print(' "' + cur["modelName"] + '",', file=out, end="") - print(" " + cur["size"] + ",", file=out, end="") - print(" " + cur["pageSize"] + ",", file=out, end="") - print(" " + cur["vendorEnum"] + ",", file=out, end="") - if cur == arr[-1]: - print(" " + cur["writeMode"] + "}};", file=out) - else: - print(" " + cur["writeMode"] + "},", file=out) - -def main(): - filename = "spi_mem_chip_arr.c" - args = getArgs() - xml = getXML(args.file) - vendors = getVendors(xml, "SPI") - chipArr = parseXML(xml, "SPI", vendors) - cleanEmptyVendors(vendors) - for cur in vendors: - print(' {"' + cur + '", SPIMemChipVendor' + cur + "},") - generateCArr(chipArr, filename) - - -if __name__ == "__main__": - main() diff --git a/applications/external/spi_mem_manager/views/spi_mem_view_detect.c b/applications/external/spi_mem_manager/views/spi_mem_view_detect.c deleted file mode 100644 index eddf36e49..000000000 --- a/applications/external/spi_mem_manager/views/spi_mem_view_detect.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "spi_mem_view_detect.h" -#include "spi_mem_manager_icons.h" -#include - -struct SPIMemDetectView { - View* view; - IconAnimation* icon; - SPIMemDetectViewCallback callback; - void* cb_ctx; -}; - -typedef struct { - IconAnimation* icon; -} SPIMemDetectViewModel; - -View* spi_mem_view_detect_get_view(SPIMemDetectView* app) { - return app->view; -} - -static void spi_mem_view_detect_draw_callback(Canvas* canvas, void* context) { - SPIMemDetectViewModel* model = context; - canvas_set_font(canvas, FontPrimary); - canvas_draw_icon_animation(canvas, 0, 0, model->icon); - canvas_draw_str_aligned(canvas, 64, 26, AlignLeft, AlignCenter, "Detecting"); - canvas_draw_str_aligned(canvas, 64, 36, AlignLeft, AlignCenter, "SPI chip..."); -} - -static void spi_mem_view_detect_enter_callback(void* context) { - SPIMemDetectView* app = context; - with_view_model( - app->view, SPIMemDetectViewModel * model, { icon_animation_start(model->icon); }, false); -} - -static void spi_mem_view_detect_exit_callback(void* context) { - SPIMemDetectView* app = context; - with_view_model( - app->view, SPIMemDetectViewModel * model, { icon_animation_stop(model->icon); }, false); -} - -SPIMemDetectView* spi_mem_view_detect_alloc() { - SPIMemDetectView* app = malloc(sizeof(SPIMemDetectView)); - app->view = view_alloc(); - view_set_context(app->view, app); - view_allocate_model(app->view, ViewModelTypeLocking, sizeof(SPIMemDetectViewModel)); - with_view_model( - app->view, - SPIMemDetectViewModel * model, - { - model->icon = icon_animation_alloc(&A_ChipLooking_64x64); - view_tie_icon_animation(app->view, model->icon); - }, - false); - view_set_draw_callback(app->view, spi_mem_view_detect_draw_callback); - view_set_enter_callback(app->view, spi_mem_view_detect_enter_callback); - view_set_exit_callback(app->view, spi_mem_view_detect_exit_callback); - return app; -} - -void spi_mem_view_detect_free(SPIMemDetectView* app) { - with_view_model( - app->view, SPIMemDetectViewModel * model, { icon_animation_free(model->icon); }, false); - view_free(app->view); - free(app); -} diff --git a/applications/external/spi_mem_manager/views/spi_mem_view_detect.h b/applications/external/spi_mem_manager/views/spi_mem_view_detect.h deleted file mode 100644 index f95edb60d..000000000 --- a/applications/external/spi_mem_manager/views/spi_mem_view_detect.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include - -typedef struct SPIMemDetectView SPIMemDetectView; -typedef void (*SPIMemDetectViewCallback)(void* context); - -View* spi_mem_view_detect_get_view(SPIMemDetectView* app); -SPIMemDetectView* spi_mem_view_detect_alloc(); -void spi_mem_view_detect_free(SPIMemDetectView* app); diff --git a/applications/external/spi_mem_manager/views/spi_mem_view_progress.c b/applications/external/spi_mem_manager/views/spi_mem_view_progress.c deleted file mode 100644 index 790f97997..000000000 --- a/applications/external/spi_mem_manager/views/spi_mem_view_progress.c +++ /dev/null @@ -1,230 +0,0 @@ -#include "spi_mem_view_progress.h" -#include - -struct SPIMemProgressView { - View* view; - SPIMemProgressViewCallback callback; - void* cb_ctx; -}; - -typedef enum { - SPIMemProgressViewTypeRead, - SPIMemProgressViewTypeVerify, - SPIMemProgressViewTypeWrite, - SPIMemProgressViewTypeUnknown -} SPIMemProgressViewType; - -typedef struct { - size_t chip_size; - size_t file_size; - size_t blocks_written; - size_t block_size; - float progress; - SPIMemProgressViewType view_type; -} SPIMemProgressViewModel; - -View* spi_mem_view_progress_get_view(SPIMemProgressView* app) { - return app->view; -} - -static void spi_mem_view_progress_draw_progress(Canvas* canvas, float progress) { - FuriString* progress_str = furi_string_alloc(); - if(progress > 1.0) progress = 1.0; - furi_string_printf(progress_str, "%d %%", (int)(progress * 100)); - elements_progress_bar(canvas, 13, 35, 100, progress); - canvas_draw_str_aligned( - canvas, 64, 25, AlignCenter, AlignTop, furi_string_get_cstr(progress_str)); - furi_string_free(progress_str); -} - -static void - spi_mem_view_progress_read_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) { - canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Reading dump"); - spi_mem_view_progress_draw_progress(canvas, model->progress); - elements_button_left(canvas, "Cancel"); -} - -static void - spi_mem_view_progress_draw_size_warning(Canvas* canvas, SPIMemProgressViewModel* model) { - if(model->file_size > model->chip_size) { - canvas_draw_str_aligned(canvas, 64, 13, AlignCenter, AlignTop, "Size clamped to chip!"); - } - if(model->chip_size > model->file_size) { - canvas_draw_str_aligned(canvas, 64, 13, AlignCenter, AlignTop, "Size clamped to file!"); - } -} - -static void - spi_mem_view_progress_verify_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) { - canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Verifying dump"); - spi_mem_view_progress_draw_size_warning(canvas, model); - spi_mem_view_progress_draw_progress(canvas, model->progress); - elements_button_center(canvas, "Skip"); -} - -static void - spi_mem_view_progress_write_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) { - canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Writing dump"); - spi_mem_view_progress_draw_size_warning(canvas, model); - spi_mem_view_progress_draw_progress(canvas, model->progress); - elements_button_left(canvas, "Cancel"); -} - -static void spi_mem_view_progress_draw_callback(Canvas* canvas, void* context) { - SPIMemProgressViewModel* model = context; - SPIMemProgressViewType view_type = model->view_type; - if(view_type == SPIMemProgressViewTypeRead) { - spi_mem_view_progress_read_draw_callback(canvas, model); - } else if(view_type == SPIMemProgressViewTypeVerify) { - spi_mem_view_progress_verify_draw_callback(canvas, model); - } else if(view_type == SPIMemProgressViewTypeWrite) { - spi_mem_view_progress_write_draw_callback(canvas, model); - } -} - -static bool - spi_mem_view_progress_read_write_input_callback(InputEvent* event, SPIMemProgressView* app) { - bool success = false; - if(event->type == InputTypeShort && event->key == InputKeyLeft) { - if(app->callback) { - app->callback(app->cb_ctx); - } - success = true; - } - return success; -} - -static bool - spi_mem_view_progress_verify_input_callback(InputEvent* event, SPIMemProgressView* app) { - bool success = false; - if(event->type == InputTypeShort && event->key == InputKeyOk) { - if(app->callback) { - app->callback(app->cb_ctx); - } - success = true; - } - return success; -} - -static bool spi_mem_view_progress_input_callback(InputEvent* event, void* context) { - SPIMemProgressView* app = context; - bool success = false; - SPIMemProgressViewType view_type; - with_view_model( - app->view, SPIMemProgressViewModel * model, { view_type = model->view_type; }, true); - if(view_type == SPIMemProgressViewTypeRead) { - success = spi_mem_view_progress_read_write_input_callback(event, app); - } else if(view_type == SPIMemProgressViewTypeVerify) { - success = spi_mem_view_progress_verify_input_callback(event, app); - } else if(view_type == SPIMemProgressViewTypeWrite) { - success = spi_mem_view_progress_read_write_input_callback(event, app); - } - return success; -} - -SPIMemProgressView* spi_mem_view_progress_alloc() { - SPIMemProgressView* app = malloc(sizeof(SPIMemProgressView)); - app->view = view_alloc(); - view_allocate_model(app->view, ViewModelTypeLocking, sizeof(SPIMemProgressViewModel)); - view_set_context(app->view, app); - view_set_draw_callback(app->view, spi_mem_view_progress_draw_callback); - view_set_input_callback(app->view, spi_mem_view_progress_input_callback); - spi_mem_view_progress_reset(app); - return app; -} - -void spi_mem_view_progress_free(SPIMemProgressView* app) { - view_free(app->view); - free(app); -} - -void spi_mem_view_progress_set_read_callback( - SPIMemProgressView* app, - SPIMemProgressViewCallback callback, - void* cb_ctx) { - app->callback = callback; - app->cb_ctx = cb_ctx; - with_view_model( - app->view, - SPIMemProgressViewModel * model, - { model->view_type = SPIMemProgressViewTypeRead; }, - true); -} - -void spi_mem_view_progress_set_verify_callback( - SPIMemProgressView* app, - SPIMemProgressViewCallback callback, - void* cb_ctx) { - app->callback = callback; - app->cb_ctx = cb_ctx; - with_view_model( - app->view, - SPIMemProgressViewModel * model, - { model->view_type = SPIMemProgressViewTypeVerify; }, - true); -} - -void spi_mem_view_progress_set_write_callback( - SPIMemProgressView* app, - SPIMemProgressViewCallback callback, - void* cb_ctx) { - app->callback = callback; - app->cb_ctx = cb_ctx; - with_view_model( - app->view, - SPIMemProgressViewModel * model, - { model->view_type = SPIMemProgressViewTypeWrite; }, - true); -} - -void spi_mem_view_progress_set_chip_size(SPIMemProgressView* app, size_t chip_size) { - with_view_model( - app->view, SPIMemProgressViewModel * model, { model->chip_size = chip_size; }, true); -} - -void spi_mem_view_progress_set_file_size(SPIMemProgressView* app, size_t file_size) { - with_view_model( - app->view, SPIMemProgressViewModel * model, { model->file_size = file_size; }, true); -} - -void spi_mem_view_progress_set_block_size(SPIMemProgressView* app, size_t block_size) { - with_view_model( - app->view, SPIMemProgressViewModel * model, { model->block_size = block_size; }, true); -} - -static size_t spi_mem_view_progress_set_total_size(SPIMemProgressViewModel* model) { - size_t total_size = model->chip_size; - if((model->chip_size > model->file_size) && model->view_type != SPIMemProgressViewTypeRead) { - total_size = model->file_size; - } - return total_size; -} - -void spi_mem_view_progress_inc_progress(SPIMemProgressView* app) { - with_view_model( - app->view, - SPIMemProgressViewModel * model, - { - size_t total_size = spi_mem_view_progress_set_total_size(model); - if(total_size == 0) total_size = 1; - model->blocks_written++; - model->progress = - ((float)model->block_size * (float)model->blocks_written) / ((float)total_size); - }, - true); -} - -void spi_mem_view_progress_reset(SPIMemProgressView* app) { - with_view_model( - app->view, - SPIMemProgressViewModel * model, - { - model->blocks_written = 0; - model->block_size = 0; - model->chip_size = 0; - model->file_size = 0; - model->progress = 0; - model->view_type = SPIMemProgressViewTypeUnknown; - }, - true); -} diff --git a/applications/external/spi_mem_manager/views/spi_mem_view_progress.h b/applications/external/spi_mem_manager/views/spi_mem_view_progress.h deleted file mode 100644 index 6a8645b6c..000000000 --- a/applications/external/spi_mem_manager/views/spi_mem_view_progress.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include - -typedef struct SPIMemProgressView SPIMemProgressView; -typedef void (*SPIMemProgressViewCallback)(void* context); - -View* spi_mem_view_progress_get_view(SPIMemProgressView* app); -SPIMemProgressView* spi_mem_view_progress_alloc(); -void spi_mem_view_progress_free(SPIMemProgressView* app); -void spi_mem_view_progress_set_read_callback( - SPIMemProgressView* app, - SPIMemProgressViewCallback callback, - void* cb_ctx); -void spi_mem_view_progress_set_verify_callback( - SPIMemProgressView* app, - SPIMemProgressViewCallback callback, - void* cb_ctx); -void spi_mem_view_progress_set_write_callback( - SPIMemProgressView* app, - SPIMemProgressViewCallback callback, - void* cb_ctx); -void spi_mem_view_progress_set_chip_size(SPIMemProgressView* app, size_t chip_size); -void spi_mem_view_progress_set_file_size(SPIMemProgressView* app, size_t file_size); -void spi_mem_view_progress_set_block_size(SPIMemProgressView* app, size_t block_size); -void spi_mem_view_progress_inc_progress(SPIMemProgressView* app); -void spi_mem_view_progress_reset(SPIMemProgressView* app); diff --git a/applications/external/weather_station/application.fam b/applications/external/weather_station/application.fam deleted file mode 100644 index 8dcaa1259..000000000 --- a/applications/external/weather_station/application.fam +++ /dev/null @@ -1,13 +0,0 @@ -App( - appid="weather_station", - name="Weather Station", - apptype=FlipperAppType.EXTERNAL, - targets=["f7"], - entry_point="weather_station_app", - requires=["gui"], - stack_size=4 * 1024, - order=50, - fap_icon="weather_station_10px.png", - fap_category="Sub-GHz", - fap_icon_assets="images", -) diff --git a/applications/external/weather_station/helpers/weather_station_event.h b/applications/external/weather_station/helpers/weather_station_event.h deleted file mode 100644 index b0486183d..000000000 --- a/applications/external/weather_station/helpers/weather_station_event.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -typedef enum { - //WSCustomEvent - WSCustomEventStartId = 100, - - WSCustomEventSceneSettingLock, - - WSCustomEventViewReceiverOK, - WSCustomEventViewReceiverConfig, - WSCustomEventViewReceiverBack, - WSCustomEventViewReceiverOffDisplay, - WSCustomEventViewReceiverUnlock, -} WSCustomEvent; diff --git a/applications/external/weather_station/helpers/weather_station_types.h b/applications/external/weather_station/helpers/weather_station_types.h deleted file mode 100644 index 111465978..000000000 --- a/applications/external/weather_station/helpers/weather_station_types.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include - -#define WS_VERSION_APP "0.8" -#define WS_DEVELOPED "SkorP" -#define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" - -#define WS_KEY_FILE_VERSION 1 -#define WS_KEY_FILE_TYPE "Flipper Weather Station Key File" - -/** WSRxKeyState state */ -typedef enum { - WSRxKeyStateIDLE, - WSRxKeyStateBack, - WSRxKeyStateStart, - WSRxKeyStateAddKey, -} WSRxKeyState; - -/** WSHopperState state */ -typedef enum { - WSHopperStateOFF, - WSHopperStateRunnig, - WSHopperStatePause, - WSHopperStateRSSITimeOut, -} WSHopperState; - -/** WSLock */ -typedef enum { - WSLockOff, - WSLockOn, -} WSLock; - -typedef enum { - WeatherStationViewVariableItemList, - WeatherStationViewSubmenu, - WeatherStationViewReceiver, - WeatherStationViewReceiverInfo, - WeatherStationViewWidget, -} WeatherStationView; - -/** WeatherStationTxRx state */ -typedef enum { - WSTxRxStateIDLE, - WSTxRxStateRx, - WSTxRxStateTx, - WSTxRxStateSleep, -} WSTxRxState; diff --git a/applications/external/weather_station/images/Humid_10x15.png b/applications/external/weather_station/images/Humid_10x15.png deleted file mode 100644 index 34b074e5f75150853ec976d7521e1663f0f30f1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3624 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 diff --git a/applications/external/weather_station/images/Humid_8x13.png b/applications/external/weather_station/images/Humid_8x13.png deleted file mode 100644 index 6d8c71b000699e148e9480f7ddf9795b33e2fe7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3618 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 diff --git a/applications/external/weather_station/images/Lock_7x8.png b/applications/external/weather_station/images/Lock_7x8.png deleted file mode 100644 index f7c9ca2c702f1b93d7d06bd12ae708655c79d7c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3597 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 a9|QyVQ~@!E@+ey_4zMz}H7hmoyzn0`d`!~- diff --git a/applications/external/weather_station/images/Pin_back_arrow_10x8.png b/applications/external/weather_station/images/Pin_back_arrow_10x8.png deleted file mode 100644 index 3bafabd144864b575144c75b592e5eaf53974566..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3606 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 TFC+3}9cE|kU{!4CefEC<&8td2 diff --git a/applications/external/weather_station/images/Quest_7x8.png b/applications/external/weather_station/images/Quest_7x8.png deleted file mode 100644 index 6825247fbeaf98b4d0d9f8aa15d4f2c5f45dcf3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3675 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 N!3~Mzlm%~0{|8*@s)qmo diff --git a/applications/external/weather_station/images/Therm_7x16.png b/applications/external/weather_station/images/Therm_7x16.png deleted file mode 100644 index 7c55500b7e7ea9112c92ba966643e3684887eb92..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3611 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 diff --git a/applications/external/weather_station/images/Timer_11x11.png b/applications/external/weather_station/images/Timer_11x11.png deleted file mode 100644 index 21ad47f4b2bf69d81929187ca63d7a3d1b111ea4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3616 zcmaJ@XH*mE8Xg4cO{Iv48v+6%BqTHw6G}pr4ncxg2uTPLQwY&e6zN4-5Rq;WRC-ZC zqX;NXKoJlSm97FJy1-qEu+nbWUH9G}*E?rszVCa_`#f)Z=A21zcC?lf-y;qHfRwF` zg)4uR*m^_-`R`m-oE!j%TT-!DXIm^5#AGpisb|Ol5H!ejqu|`870}D0ix|83@N0Gq zS9wv8E9P>zT#AOas+jDNc-8y?d6&i=mX<=w?RoKnNlD>}@-8}(m&D(ROsL*WinBZ`Y&|Cg*>XtusZajEvGF867t?m|S5S2`~(RVQnmn^~T+wnfCt)=zD1jH;tT%8HX zidK_U1J~6AfR!*5>L9p5sI}##_fI~mN5D@+SPQMZZ+f|CU$D3Ps#vto@TX+!wTBX$Ybt%<7F(Yhytdr9 z%g%r#i|oV&cmX&8bM?Tp{k@x{k7GKkf+k~zz}?d(0--6o#V3e@-|RGH@$80=%K$K6 z%V>P9B`O&17xkf=vpHwFZk@Lu2=}$U8UO$%Ez}{n7uBY1q5xo#7omOETzRo^w@!ob z-p1|2jS_3#M$s7cmL`lWMw}GBm*st+JQAZ7+j&<-+Z+1YOvRwV#V|}+!oL8*- zd(eqS`BSgT{A31`O|Wfx4WD<5=(n8FgS0kd?j6z*OC@&P1D8vdweGolv|O+@VTss% zk0Z1*!m>fkNQi?05%!te;O+5_?`(=ed({ng42l_x2}Zj#X@XOW?e1$l-tkAvZXY-- z4sWBQ_GV}DE~sp1JhsJHeP;p|u32+so9(^ZxZa(;R=sprwP~G_90Qv@YN^i$N&ZzL zh-*5agY7XB+==E1{R!m>)p;**u8?G?9=TCOinA5of=oivyfCTGIU-EU>PjuhwP zb{Hlf!&Kz+T<^HV74I@Qn~msZ<%`MGyCz5k+gk|8LvEgJEpBa zXM7f1btDehSM{Kea)Q8lF4GYJ8;P*C*3YoTDj}HjhBeMPA_vWwqv?L#a)jy)|QSG{L&DT_9JTqYBI@?ifN~}z1;#y}jl`}=$!g|YE&(#QN^R^?J$2F}f$z9vIQ*HxpBSqpx3Jz%GQYEC+ zzd$*^)`IhtUNoDT`{ZPJu05k@G`N21``!!Cb=*4bd(o1$Bwn~$QeAjRvTHZ$nPC6} zr2=gm`rQS4qS*{vKu9BGe27k|=|SDFK;dEg!}e{RFFS8`zR5DoLBrm{r*fup-sX%w^gb4JOovy@dqlRanmAVIIm@e~ z#~ed=7U12Fov5~|;8yH^Q(IA6w4v!*CgY67Dc;x8xIMRq_kOdvVRtt0LA6Gzxf0Vh6$^e%C8s&krV ziihsZ8qHE?eD1s7m=ofT?h!+6 zlTyfO)S&TWgU6<=5MR%i{dg|k_Ke+L1Vp>ih<@hD*xJlO+(+(5iSbayOlbQFW^jI2 z(_&1KLJ4H24l>=$KHl-rwSSq*Y8NXc?w{Yq*`FjH+@#V(0YiI?dg9+~kO*9F44pMO{s~5`ZaHbx7q=zED2- zp6e(l$5d@RqhEdq-Ipfv+`sxt`F2lTaUQ1dGwztyTWygl3faT=X=lOV-@R0bp{Pu&fM}^B#k1p}FY5h)R zGaeb0Vf7jz4*n4*8(%~=J`nK#D&a0Z8FS(5@Y|UaPI##2*aO1%Sgx{(e8Qzlxgo_2 z`HSzghJz-R;}|cVW({AvUsBdmL+bYJ^_~7Ss+;R2onD&pDMOkrH86NzYV7F!nWb-* zL(q&)t)bc|9=7JzQ`Dn6a?$gy&cmj-+qgyCcbw5|@5lqf+ZB4xta51GH-q2$hrH^R z*G-;38FCkJcj))+C$HMBRxg`YCX`OEq_5IWR5;QCX4(XM1=mH?q^UiYi?qH(Ut zZw`L7mTvpy$p&|hqbp@3<^JpS){kmTi{OdrWwEj4eNxE5bBUqlA4K|oIj2HVfu6=> z&u3fZxMi<;`FK5cdTG-0=F4cvn)T2xGS>}Ip20^JaL=iO(~*6tl=<#NZW{MO803#( z@1dK#&?#cq*l8KY++$hxhhEFg%TtHz4tE`&f5e`z8k*eY@yH|l4)PT33;PRdBel<| zt@e6tc4f_R|C-s5`Uj!D%hSra#$6+e^})X@Y`*EwMW9FO7eW}z&z6_Q6h^{Wn(JL1 zwF4Z@*@`-+x>Jj0Gv))>k+^8y%I33ed2X{;zMldNO%rsiEW8zyD@y(90H3Bn3EVjWRNY5Kq0%538dhF=VTNB2x?JrcsAa_9!X@- zAcQR+NDz(5M*{%LG>Azc`jgopA2NkXM}y~TpMpVD5*qAb=%DAo#FG7}HX$ssTZki$ z7~)Svkie!UAXE^NPe3EH37{a_8G0Zx2o3&|7s=mmnW13NpDt{FH2ANi@D9!(EQ3V` z8AD*YL_Iw{kTC*6CK2F1`o09B4hXIXhe2Wd+gKN7jD+hWVF=LQ7nmQAMe;?uT3G!Z zj(F%oW8C)!~b`s(f;ucWV@38 zlkfi|4#WjB$xv5vAmc2H$e*3B+Eyqg63ZeJ*bEkq!8r4ykyyNgz}z2?;keH?o2o85mhw>A_%@76_|DRv0*c z42KTs8DaH}e_$;b#IrOqo&5t#`VZFdr`Rn)(3t$l7GxIn9GPUrV$eW;R*j_oJQw&+ z`ToX|ex8f|Pq9#bGSIEr{@1L3nD_$P+WsS6{^1|_lj(fTv-skkVdSmxKMY}Kdz|Iw z<|cpZ-qaVyUk=(@nB#&5eY}OX3Cju#ic0MV`6N0=>%^ya#yelSoh4wa5-C(7JR~CP Y4M;=-T9{t958 z2#qC*WXVoKV+mP%$Mbu7e(xV|@42pXzW4V&pU>yMzxREg>pE8*?5qU^WCQ>J5VS#9 zpg8MJ&J6Mcqn^zcKy?O)nxYMMjNADIC77ua?(V;KVX20HiY%aC)gwEo2w(aB@jcrV37&d zYhS(w0GQ)p&?9J%j5oL*k^ydj(xrYtv~l=XRHcKmD*#Rch9IJoySNfjK$E&tlQ__{ z7kK3O)LQ^Z0RRFc%nSnD7X)U0*ckBvJ;llWQb14szG4s%#|2~@v_8OX@)GcLzJOBY zu6qsSF-;)qymh5qk#5hmthpnr`GDYfbfU0{ClHxorrH94^|=A_{bH>=U?fkTMrZ9% zu?Ho(0>K5;u~J*pk9TT|SERm|30asM8c`T|O?YgEkvb&e!#@VePR~*lLrn4@+jawh z%xcH0Eq&v}$%(Py37<&<`$t3mR=^w?Vx%xXxK(wXn->tVYiIX*jE{HoP#U=&1=R)= zp8|Sa0KdUickMp@ypsa&Lsw%N`Wq(ub8kB|8OrSw*tKg`$?JBt#%Qe3FYRISP;A69 z=j~Qs=p1l1(~_R>S!@m03f+`HNixM3usL*90h=?uX|75OOZmp1p$CX-i5=DOn2^nCC;o9%6=tR zRVT%b*g8Oa!B;_g=vb^ z4$r;0ulH76=I1qS0*PT1U@?2V;(H)%AgPRaUI+%Eb0e}4JQX8;0@Bb#E#xjX^G|X| zC@!c`#SP+4o2(`FHG#FRZCtCe)=atZ#^N2cWmbjXzL zhetloFX}k{HHZd;UyH{^c4!LuT>p$Yef^51=T)?fa-$@69Ifk;po^759|@L_t;@x* zK?k^FBgJMwXD*4nCR|KRv_>P*=J%9l6w5>_L9YB!mo#7h1xdbVU#1i)x>`^7f;~<| zTQQZtE9_UuRXX#RkeEj@;($=|jWIg`1*JqSn_V^mh(3f`p<|&@rwBe9sXU!XZ2mF^ zdJ@S5rze#s3Mbm%SZ{taRxS=}h#5ih=N~{7ridQX#Tk$D-npe^mXUY=L~C*GN6`Hk z*sYT`#Jpe!sNiKKU; zsjyU+)QHr{`%cb*&cABJkzQHH*LL6Jz1SW2J@}U z21Cyw9nAyp`!Icyd~znvwsHx*eLOU0@HzWfn?jpl+c`BJHDk5M-Toy$B@rb@dP93_ zdc9_;vy!vZz3d=Lj!BMc&Jv6WTM6Q?)T=yE8C}^I)c(!r19qA*#lQ4!NoZ=I!+MGM zqhLwu8@rp`A%8?e2c(xMP0-ZG&b1_BzXsgIS9Hu>8osxOMN`-Y#6IK)S42I=~LNJ_JP*Y(xlqY>|r*~#2a*F z2jpUEK3DZ^#6{n+%x*Xqs~6jt)|(c_;!CqlTVdXGF>+zJEV+DQ+H{|uR-GnxyAm8^ zU9)y)!LnG-@0Dbg)CXq~2gOIk6ApDAT5=@yYR+uT2+U;8?3guJ#w;r>6PMfNTK0*` zbswc24WrV6T7n6bs_DXEoj1kx#c!ruePw-b2j(p5O5Hu4$P!HtPM2~d7F{bM-3n!; zj>~+n?0oiNsUYiRR)5K7;>Up&ctiMubzAi;*=F}QaJK1>xfS%t*_P3qqO79Vi;0ua zGr?!v&a7AOw||;Lkc{6zL?9}Cp<9oRSy4y&? zY&XB4n>;m{Tqm_4yNcEB_f^g8ka!2mkvJ*4rqQB|+~2(?{&G8LP$YtUcNIC+@*EU1 zWKD>vkjG1BNUes8A3CgcU;W#OGDq53+KOs7bIfhsw>o}4q4@fXqkaC*slmQXe*%ht zoyn?*thirsfqvzu<$Ss*P3!>w?A5XQo_hGz(LnA=LZ){1Sf*1N4O=?ipZ`K?Vycam z8)E3D>y{X%AAM6a{fY5-6xhrGy4QZZh-51#ws0vc+TOAzKQ8~otfOUh1vf3>}NHDlV zdmj~*WWh1U1o540@|AZhV~VSRi+vJ=XkLXr$Rrq_Y}PXQH?nHQG3v5 z>)Wd0u8Wdk)rpTBDjq%Usi3>f4?$`zUrH**I!cA8Yr3Nq*+C!w4GX zyx`C1Ux-IVb>6vSu5!^;C$%`GnMEr7aqil`XtzWC zm*QK?THm$u=wftdPqjQ}_AT7jD_9QAIq%ML*(`ZbUh`SGx4U*A*D?ws4XY{{PXr;!Q$4{K|m@Dovb zar+T4%6L{Jxi@PzGvpcNv8oV2JZq(uH?Y1}lZ(0X4&X+HNrV$L4PFQUa zQ>}oQ2ftm-{(8M2NA8TAbxrxN2)5=ZHmFfI!8JE8=OBE3b?jpDXpwhOZjPNX{9{Hx zV+Fa95#WBpz1r8jJ=a)@_8nR7vC_QwWir8iu8Q&lvf|aJRDQe!UJAF4pll8!9-bmk z<5pO+u7;(wAGXs+JJ=u2uld(?1%CSZN!|SxqniD8Mz)-!Jg~1qsdDLO@bauwh`@Jb zzk6r`{ozJU@8-9iYr@~omu)@9)e(n&de(Wizi|_03-Mpc-AeiO;mUBQb&GYEqLpG? zLXNz=te{Nwf_Gc;aM6<@vG#WnF25Mlfe$7JH%Hcwx1%?D=60>dw%3+2iWjNu2gMIz zjf#!(Rc#FT{N0U`w!Uz71-o*vv06Uk;D*VT!(zu8wz25F{fg0K*wzMg<B>T`pFjO31>P_~-fo+HwUmOaD@n)QD#u)+tk22l~O+(uvVOTOz9kY#5 zrxPh0HUJnJ(;gG*|VH|tg4TXUJhR_1wkpCowwsioTlc_kcp1Ot_ zRzpJ%e8fQA8{>t+dU>gWwKTLep&B|+O&v824Vbn8Oh*U&&jsOxqk8+mP!?AI1mo=B z5I-7?0)s+BLPFF-wAIN}U#O;mfdN!Q3#z51#zCkBGDtKGU5yl|_*=mO7l@_eDKtEp z1m0G}c#(r>a0n;W|D1tH`B#<{_)ncU6@$_-6sV@U#`c+h18r^pe<+doFFKHh!u>bj z|5G^7i9x|ZQMf>I5EaYmoR8vmC<@G+io?*zR3|c-@Vkr-eq(`ynw+1+toQ;L46TR2V)7#tA6A(24DVXY@MO2ZIUc4X;fENVFW;}?ba)5x1 MrJY5ondim-0h+K&wEzGB diff --git a/applications/external/weather_station/images/WarningDolphin_45x42.png b/applications/external/weather_station/images/WarningDolphin_45x42.png deleted file mode 100644 index d766ffbb444db1739f2ccd030e506e8bada11ee8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1139 zcmaJ=TWAz#6rQ4%BpO*okt7zz3Bkm=bK9L=XV}qhW_HceY#KHzP4Z$UGyf(-b}pUy z<8ESW=_MtCk6tj|`k)VrCbo(SMH0n`eW+KIU`wG5O`$IeMg)Vzf7adDhv+af=lqBB zo%5Z`zqhqzdu2s+1%_dji6%LPq#u2o%9fzN?;7Dlq6)^^VVjkKImH23RI|DPo-mXi zkOGP}@Wrnnf?-SQ^>jOIPc{pxWsr*JL*@+|p)oA7EpIDoAAoo_=+RA)c=F3Qf$N$` ze9k55q%DD7y=l+^ZG$aob+Aw6HDcRVJdzhs00Te;&l_3O74jlch$|r7GgAa!aDjay z@rG1;vK5ys2jF3n@vAgV<6)izn!k6Qhd>D(EhD7l zcrhJ1i9|1iwm?z2T#n2INXzM=7@p@Tnx$CQk39VDfC-hn-*jtB5oF-1j&4KUGI1}W z(rxuakw9eMRAJc3%8 zlBq3$QTyJX$a6$&1ldyi4Pe5AEE32Sg@vDzY~7qR?1u@oXh zd9(fBtV<@eK%Tm=yy&p7{=h^#@1W)WFFSe!U5pP~o71uR`FW)7xc*=d5_b}EG@XBZ z@<7MRp-;+|o}SzJvMyfx>37Y4etBizf%0H*zaIF?2ObZt?$D;{o|esJ z*PgAOIO^*8tL%z2)|}k$DP5kN5}8qo*wNa8y?R2=%wO{gCD(`sHt}TzWQuK zMDIJap(nb2=7*ns*oDcRBbC23yy`kvKYV`ovU`a=EmxNv-90;;5r6+l8hQTp^u`Hn XX6*+%8gHC`h)Tl}u@-r>vFqE{F~5F& diff --git a/applications/external/weather_station/images/station_icon.png b/applications/external/weather_station/images/station_icon.png deleted file mode 100644 index b839eeb7aebfcf8aee9b2ba9570a3d9cf1d8af12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3607 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 PjRq`CY_X+i_pAQ_NTe=6 diff --git a/applications/external/weather_station/protocols/acurite_592txr.c b/applications/external/weather_station/protocols/acurite_592txr.c deleted file mode 100644 index 874f6dd33..000000000 --- a/applications/external/weather_station/protocols/acurite_592txr.c +++ /dev/null @@ -1,298 +0,0 @@ -#include "acurite_592txr.h" - -#define TAG "WSProtocolAcurite_592TXR" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/acurite.c - * - * Acurite 592TXR Temperature Humidity sensor decoder - * Message Type 0x04, 7 bytes - * | Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | - * | --------- | --------- | --------- | --------- | --------- | --------- | --------- | - * | CCII IIII | IIII IIII | pB00 0100 | pHHH HHHH | p??T TTTT | pTTT TTTT | KKKK KKKK | - * - C: Channel 00: C, 10: B, 11: A, (01 is invalid) - * - I: Device ID (14 bits) - * - B: Battery, 1 is battery OK, 0 is battery low - * - M: Message type (6 bits), 0x04 - * - T: Temperature Celsius (11 - 14 bits?), + 1000 * 10 - * - H: Relative Humidity (%) (7 bits) - * - K: Checksum (8 bits) - * - p: Parity bit - * Notes: - * - Temperature - * - Encoded as Celsius + 1000 * 10 - * - only 11 bits needed for specified range -40 C to 70 C (-40 F - 158 F) - * - However 14 bits available for temperature, giving possible range of -100 C to 1538.4 C - * - @todo - check if high 3 bits ever used for anything else - * - */ - -static const SubGhzBlockConst ws_protocol_acurite_592txr_const = { - .te_short = 200, - .te_long = 400, - .te_delta = 90, - .min_count_bit_for_found = 56, -}; - -struct WSProtocolDecoderAcurite_592TXR { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - - uint16_t header_count; -}; - -struct WSProtocolEncoderAcurite_592TXR { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - Acurite_592TXRDecoderStepReset = 0, - Acurite_592TXRDecoderStepCheckPreambule, - Acurite_592TXRDecoderStepSaveDuration, - Acurite_592TXRDecoderStepCheckDuration, -} Acurite_592TXRDecoderStep; - -const SubGhzProtocolDecoder ws_protocol_acurite_592txr_decoder = { - .alloc = ws_protocol_decoder_acurite_592txr_alloc, - .free = ws_protocol_decoder_acurite_592txr_free, - - .feed = ws_protocol_decoder_acurite_592txr_feed, - .reset = ws_protocol_decoder_acurite_592txr_reset, - - .get_hash_data = ws_protocol_decoder_acurite_592txr_get_hash_data, - .serialize = ws_protocol_decoder_acurite_592txr_serialize, - .deserialize = ws_protocol_decoder_acurite_592txr_deserialize, - .get_string = ws_protocol_decoder_acurite_592txr_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_acurite_592txr_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_acurite_592txr = { - .name = WS_PROTOCOL_ACURITE_592TXR_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_acurite_592txr_decoder, - .encoder = &ws_protocol_acurite_592txr_encoder, -}; - -void* ws_protocol_decoder_acurite_592txr_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderAcurite_592TXR* instance = malloc(sizeof(WSProtocolDecoderAcurite_592TXR)); - instance->base.protocol = &ws_protocol_acurite_592txr; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_acurite_592txr_free(void* context) { - furi_assert(context); - WSProtocolDecoderAcurite_592TXR* instance = context; - free(instance); -} - -void ws_protocol_decoder_acurite_592txr_reset(void* context) { - furi_assert(context); - WSProtocolDecoderAcurite_592TXR* instance = context; - instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; -} - -static bool ws_protocol_acurite_592txr_check_crc(WSProtocolDecoderAcurite_592TXR* instance) { - uint8_t msg[] = { - instance->decoder.decode_data >> 48, - instance->decoder.decode_data >> 40, - instance->decoder.decode_data >> 32, - instance->decoder.decode_data >> 24, - instance->decoder.decode_data >> 16, - instance->decoder.decode_data >> 8}; - - if((subghz_protocol_blocks_add_bytes(msg, 6) == - (uint8_t)(instance->decoder.decode_data & 0xFF)) && - (!subghz_protocol_blocks_parity_bytes(&msg[2], 4))) { - return true; - } else { - return false; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_acurite_592txr_remote_controller(WSBlockGeneric* instance) { - uint8_t channel[] = {3, 0, 2, 1}; - uint8_t channel_raw = ((instance->data >> 54) & 0x03); - instance->channel = channel[channel_raw]; - instance->id = (instance->data >> 40) & 0x3FFF; - instance->battery_low = !((instance->data >> 38) & 1); - instance->humidity = (instance->data >> 24) & 0x7F; - - uint16_t temp_raw = ((instance->data >> 9) & 0xF80) | ((instance->data >> 8) & 0x7F); - instance->temp = ((float)(temp_raw)-1000) / 10.0f; - - instance->btn = WS_NO_BTN; -} - -void ws_protocol_decoder_acurite_592txr_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderAcurite_592TXR* instance = context; - - switch(instance->decoder.parser_step) { - case Acurite_592TXRDecoderStepReset: - if((level) && (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short * 3) < - ws_protocol_acurite_592txr_const.te_delta * 2)) { - instance->decoder.parser_step = Acurite_592TXRDecoderStepCheckPreambule; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - - case Acurite_592TXRDecoderStepCheckPreambule: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF( - instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_short * 3) < - ws_protocol_acurite_592txr_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short * 3) < - ws_protocol_acurite_592txr_const.te_delta * 2)) { - //Found preambule - instance->header_count++; - } else if((instance->header_count > 2) && (instance->header_count < 5)) { - if((DURATION_DIFF( - instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_short) < - ws_protocol_acurite_592txr_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_long) < - ws_protocol_acurite_592txr_const.te_delta)) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_long) < - ws_protocol_acurite_592txr_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short) < - ws_protocol_acurite_592txr_const.te_delta)) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; - } - } else { - instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; - } - } - break; - - case Acurite_592TXRDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = Acurite_592TXRDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; - } - break; - - case Acurite_592TXRDecoderStepCheckDuration: - if(!level) { - if(duration >= ((uint32_t)ws_protocol_acurite_592txr_const.te_short * 5)) { - if((instance->decoder.decode_count_bit == - ws_protocol_acurite_592txr_const.min_count_bit_for_found) && - ws_protocol_acurite_592txr_check_crc(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_acurite_592txr_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; - break; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_short) < - ws_protocol_acurite_592txr_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_long) < - ws_protocol_acurite_592txr_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_acurite_592txr_const.te_long) < - ws_protocol_acurite_592txr_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_acurite_592txr_const.te_short) < - ws_protocol_acurite_592txr_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Acurite_592TXRDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; - } - } else { - instance->decoder.parser_step = Acurite_592TXRDecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderAcurite_592TXR* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_acurite_592txr_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderAcurite_592TXR* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderAcurite_592TXR* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - ws_protocol_acurite_592txr_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderAcurite_592TXR* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/acurite_592txr.h b/applications/external/weather_station/protocols/acurite_592txr.h deleted file mode 100644 index 1071d1cf7..000000000 --- a/applications/external/weather_station/protocols/acurite_592txr.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_ACURITE_592TXR_NAME "Acurite 592TXR" - -typedef struct WSProtocolDecoderAcurite_592TXR WSProtocolDecoderAcurite_592TXR; -typedef struct WSProtocolEncoderAcurite_592TXR WSProtocolEncoderAcurite_592TXR; - -extern const SubGhzProtocolDecoder ws_protocol_acurite_592txr_decoder; -extern const SubGhzProtocolEncoder ws_protocol_acurite_592txr_encoder; -extern const SubGhzProtocol ws_protocol_acurite_592txr; - -/** - * Allocate WSProtocolDecoderAcurite_592TXR. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderAcurite_592TXR* pointer to a WSProtocolDecoderAcurite_592TXR instance - */ -void* ws_protocol_decoder_acurite_592txr_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderAcurite_592TXR. - * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance - */ -void ws_protocol_decoder_acurite_592txr_free(void* context); - -/** - * Reset decoder WSProtocolDecoderAcurite_592TXR. - * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance - */ -void ws_protocol_decoder_acurite_592txr_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_acurite_592txr_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_acurite_592txr_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderAcurite_592TXR. - * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_acurite_592txr_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderAcurite_592TXR. - * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_acurite_592txr_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderAcurite_592TXR instance - * @param output Resulting text - */ -void ws_protocol_decoder_acurite_592txr_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/acurite_606tx.c b/applications/external/weather_station/protocols/acurite_606tx.c deleted file mode 100644 index e0d405c66..000000000 --- a/applications/external/weather_station/protocols/acurite_606tx.c +++ /dev/null @@ -1,239 +0,0 @@ -#include "acurite_606tx.h" - -#define TAG "WSProtocolAcurite_606TX" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L1644 - * - * 0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 - * iiii iiii | buuu tttt | tttt tttt | cccc cccc - * - i: identification; changes on battery switch - * - c: lfsr_digest8; - * - u: unknown; - * - b: battery low; flag to indicate low battery voltage - * - t: Temperature; in °C - * - */ - -static const SubGhzBlockConst ws_protocol_acurite_606tx_const = { - .te_short = 500, - .te_long = 2000, - .te_delta = 150, - .min_count_bit_for_found = 32, -}; - -struct WSProtocolDecoderAcurite_606TX { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; -}; - -struct WSProtocolEncoderAcurite_606TX { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - Acurite_606TXDecoderStepReset = 0, - Acurite_606TXDecoderStepSaveDuration, - Acurite_606TXDecoderStepCheckDuration, -} Acurite_606TXDecoderStep; - -const SubGhzProtocolDecoder ws_protocol_acurite_606tx_decoder = { - .alloc = ws_protocol_decoder_acurite_606tx_alloc, - .free = ws_protocol_decoder_acurite_606tx_free, - - .feed = ws_protocol_decoder_acurite_606tx_feed, - .reset = ws_protocol_decoder_acurite_606tx_reset, - - .get_hash_data = ws_protocol_decoder_acurite_606tx_get_hash_data, - .serialize = ws_protocol_decoder_acurite_606tx_serialize, - .deserialize = ws_protocol_decoder_acurite_606tx_deserialize, - .get_string = ws_protocol_decoder_acurite_606tx_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_acurite_606tx_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_acurite_606tx = { - .name = WS_PROTOCOL_ACURITE_606TX_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_acurite_606tx_decoder, - .encoder = &ws_protocol_acurite_606tx_encoder, -}; - -void* ws_protocol_decoder_acurite_606tx_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderAcurite_606TX* instance = malloc(sizeof(WSProtocolDecoderAcurite_606TX)); - instance->base.protocol = &ws_protocol_acurite_606tx; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_acurite_606tx_free(void* context) { - furi_assert(context); - WSProtocolDecoderAcurite_606TX* instance = context; - free(instance); -} - -void ws_protocol_decoder_acurite_606tx_reset(void* context) { - furi_assert(context); - WSProtocolDecoderAcurite_606TX* instance = context; - instance->decoder.parser_step = Acurite_606TXDecoderStepReset; -} - -static bool ws_protocol_acurite_606tx_check(WSProtocolDecoderAcurite_606TX* instance) { - if(!instance->decoder.decode_data) return false; - uint8_t msg[] = { - instance->decoder.decode_data >> 24, - instance->decoder.decode_data >> 16, - instance->decoder.decode_data >> 8}; - - uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1); - return (crc == (instance->decoder.decode_data & 0xFF)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_acurite_606tx_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 24) & 0xFF; - instance->battery_low = (instance->data >> 23) & 1; - - instance->channel = WS_NO_CHANNEL; - - if(!((instance->data >> 19) & 1)) { - instance->temp = (float)((instance->data >> 8) & 0x07FF) / 10.0f; - } else { - instance->temp = (float)((~(instance->data >> 8) & 0x07FF) + 1) / -10.0f; - } - instance->btn = WS_NO_BTN; - instance->humidity = WS_NO_HUMIDITY; -} - -void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderAcurite_606TX* instance = context; - - switch(instance->decoder.parser_step) { - case Acurite_606TXDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_short * 17) < - ws_protocol_acurite_606tx_const.te_delta * 8)) { - //Found syncPrefix - instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } - break; - - case Acurite_606TXDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = Acurite_606TXDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = Acurite_606TXDecoderStepReset; - } - break; - - case Acurite_606TXDecoderStepCheckDuration: - if(!level) { - if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta) { - if((DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_short) < - ws_protocol_acurite_606tx_const.te_delta) || - (duration > ws_protocol_acurite_606tx_const.te_long * 3)) { - //Found syncPostfix - instance->decoder.parser_step = Acurite_606TXDecoderStepReset; - if((instance->decoder.decode_count_bit == - ws_protocol_acurite_606tx_const.min_count_bit_for_found) && - ws_protocol_acurite_606tx_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_acurite_606tx_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else if( - DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long) < - ws_protocol_acurite_606tx_const.te_delta * 2) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; - } else if( - DURATION_DIFF(duration, ws_protocol_acurite_606tx_const.te_long * 2) < - ws_protocol_acurite_606tx_const.te_delta * 4) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Acurite_606TXDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Acurite_606TXDecoderStepReset; - } - } else { - instance->decoder.parser_step = Acurite_606TXDecoderStepReset; - } - } else { - instance->decoder.parser_step = Acurite_606TXDecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderAcurite_606TX* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_acurite_606tx_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderAcurite_606TX* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderAcurite_606TX* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - ws_protocol_acurite_606tx_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderAcurite_606TX* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/acurite_606tx.h b/applications/external/weather_station/protocols/acurite_606tx.h deleted file mode 100644 index 15b6d1eb5..000000000 --- a/applications/external/weather_station/protocols/acurite_606tx.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_ACURITE_606TX_NAME "Acurite-606TX" - -typedef struct WSProtocolDecoderAcurite_606TX WSProtocolDecoderAcurite_606TX; -typedef struct WSProtocolEncoderAcurite_606TX WSProtocolEncoderAcurite_606TX; - -extern const SubGhzProtocolDecoder ws_protocol_acurite_606tx_decoder; -extern const SubGhzProtocolEncoder ws_protocol_acurite_606tx_encoder; -extern const SubGhzProtocol ws_protocol_acurite_606tx; - -/** - * Allocate WSProtocolDecoderAcurite_606TX. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderAcurite_606TX* pointer to a WSProtocolDecoderAcurite_606TX instance - */ -void* ws_protocol_decoder_acurite_606tx_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderAcurite_606TX. - * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance - */ -void ws_protocol_decoder_acurite_606tx_free(void* context); - -/** - * Reset decoder WSProtocolDecoderAcurite_606TX. - * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance - */ -void ws_protocol_decoder_acurite_606tx_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_acurite_606tx_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_acurite_606tx_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderAcurite_606TX. - * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_acurite_606tx_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderAcurite_606TX. - * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_acurite_606tx_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderAcurite_606TX instance - * @param output Resulting text - */ -void ws_protocol_decoder_acurite_606tx_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/acurite_609txc.c b/applications/external/weather_station/protocols/acurite_609txc.c deleted file mode 100644 index 853b78446..000000000 --- a/applications/external/weather_station/protocols/acurite_609txc.c +++ /dev/null @@ -1,239 +0,0 @@ -#include "acurite_609txc.h" - -#define TAG "WSProtocolAcurite_609TXC" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/5bef4e43133ac4c0e2d18d36f87c52b4f9458453/src/devices/acurite.c#L216 - * - * 0000 1111 | 0011 0000 | 0101 1100 | 0000 0000 | 1110 0111 - * iiii iiii | buuu tttt | tttt tttt | hhhh hhhh | cccc cccc - * - i: identification; changes on battery switch - * - c: checksum (sum of previous by bytes) - * - u: unknown - * - b: battery low; flag to indicate low battery voltage - * - t: temperature; in °C * 10, 12 bit with complement - * - h: humidity - * - */ - -static const SubGhzBlockConst ws_protocol_acurite_609txc_const = { - .te_short = 500, - .te_long = 1000, - .te_delta = 150, - .min_count_bit_for_found = 40, -}; - -struct WSProtocolDecoderAcurite_609TXC { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; -}; - -struct WSProtocolEncoderAcurite_609TXC { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - Acurite_609TXCDecoderStepReset = 0, - Acurite_609TXCDecoderStepSaveDuration, - Acurite_609TXCDecoderStepCheckDuration, -} Acurite_609TXCDecoderStep; - -const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder = { - .alloc = ws_protocol_decoder_acurite_609txc_alloc, - .free = ws_protocol_decoder_acurite_609txc_free, - - .feed = ws_protocol_decoder_acurite_609txc_feed, - .reset = ws_protocol_decoder_acurite_609txc_reset, - - .get_hash_data = ws_protocol_decoder_acurite_609txc_get_hash_data, - .serialize = ws_protocol_decoder_acurite_609txc_serialize, - .deserialize = ws_protocol_decoder_acurite_609txc_deserialize, - .get_string = ws_protocol_decoder_acurite_609txc_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_acurite_609txc = { - .name = WS_PROTOCOL_ACURITE_609TXC_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_acurite_609txc_decoder, - .encoder = &ws_protocol_acurite_609txc_encoder, -}; - -void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderAcurite_609TXC* instance = malloc(sizeof(WSProtocolDecoderAcurite_609TXC)); - instance->base.protocol = &ws_protocol_acurite_609txc; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_acurite_609txc_free(void* context) { - furi_assert(context); - WSProtocolDecoderAcurite_609TXC* instance = context; - free(instance); -} - -void ws_protocol_decoder_acurite_609txc_reset(void* context) { - furi_assert(context); - WSProtocolDecoderAcurite_609TXC* instance = context; - instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; -} - -static bool ws_protocol_acurite_609txc_check(WSProtocolDecoderAcurite_609TXC* instance) { - if(!instance->decoder.decode_data) return false; - uint8_t crc = (uint8_t)(instance->decoder.decode_data >> 32) + - (uint8_t)(instance->decoder.decode_data >> 24) + - (uint8_t)(instance->decoder.decode_data >> 16) + - (uint8_t)(instance->decoder.decode_data >> 8); - return (crc == (instance->decoder.decode_data & 0xFF)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_acurite_609txc_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 32) & 0xFF; - instance->battery_low = (instance->data >> 31) & 1; - - instance->channel = WS_NO_CHANNEL; - - // Temperature in Celsius is encoded as a 12 bit integer value - // multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2) - // negative values are recovered by sign extend from int16_t. - int16_t temp_raw = - (int16_t)(((instance->data >> 12) & 0xf000) | ((instance->data >> 16) << 4)); - instance->temp = (temp_raw >> 4) * 0.1f; - instance->humidity = (instance->data >> 8) & 0xff; - instance->btn = WS_NO_BTN; -} - -void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderAcurite_609TXC* instance = context; - - switch(instance->decoder.parser_step) { - case Acurite_609TXCDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short * 17) < - ws_protocol_acurite_609txc_const.te_delta * 8)) { - //Found syncPrefix - instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } - break; - - case Acurite_609TXCDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = Acurite_609TXCDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; - } - break; - - case Acurite_609TXCDecoderStepCheckDuration: - if(!level) { - if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_acurite_609txc_const.te_short) < - ws_protocol_acurite_609txc_const.te_delta) { - if((DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_short) < - ws_protocol_acurite_609txc_const.te_delta) || - (duration > ws_protocol_acurite_609txc_const.te_long * 3)) { - //Found syncPostfix - instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; - if((instance->decoder.decode_count_bit == - ws_protocol_acurite_609txc_const.min_count_bit_for_found) && - ws_protocol_acurite_609txc_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_acurite_609txc_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else if( - DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long) < - ws_protocol_acurite_609txc_const.te_delta * 2) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; - } else if( - DURATION_DIFF(duration, ws_protocol_acurite_609txc_const.te_long * 2) < - ws_protocol_acurite_609txc_const.te_delta * 4) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Acurite_609TXCDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; - } - } else { - instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; - } - } else { - instance->decoder.parser_step = Acurite_609TXCDecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderAcurite_609TXC* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_acurite_609txc_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderAcurite_609TXC* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderAcurite_609TXC* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - ws_protocol_acurite_609txc_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderAcurite_609TXC* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 40), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/acurite_609txc.h b/applications/external/weather_station/protocols/acurite_609txc.h deleted file mode 100644 index 3e3b9cee8..000000000 --- a/applications/external/weather_station/protocols/acurite_609txc.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_ACURITE_609TXC_NAME "Acurite-609TXC" - -typedef struct WSProtocolDecoderAcurite_609TXC WSProtocolDecoderAcurite_609TXC; -typedef struct WSProtocolEncoderAcurite_609TXC WSProtocolEncoderAcurite_609TXC; - -extern const SubGhzProtocolDecoder ws_protocol_acurite_609txc_decoder; -extern const SubGhzProtocolEncoder ws_protocol_acurite_609txc_encoder; -extern const SubGhzProtocol ws_protocol_acurite_609txc; - -/** - * Allocate WSProtocolDecoderAcurite_609TXC. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderAcurite_609TXC* pointer to a WSProtocolDecoderAcurite_609TXC instance - */ -void* ws_protocol_decoder_acurite_609txc_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderAcurite_609TXC. - * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance - */ -void ws_protocol_decoder_acurite_609txc_free(void* context); - -/** - * Reset decoder WSProtocolDecoderAcurite_609TXC. - * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance - */ -void ws_protocol_decoder_acurite_609txc_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_acurite_609txc_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_acurite_609txc_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderAcurite_609TXC. - * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_acurite_609txc_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderAcurite_609TXC. - * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_acurite_609txc_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderAcurite_609TXC instance - * @param output Resulting text - */ -void ws_protocol_decoder_acurite_609txc_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/ambient_weather.c b/applications/external/weather_station/protocols/ambient_weather.c deleted file mode 100644 index 588a7f82a..000000000 --- a/applications/external/weather_station/protocols/ambient_weather.c +++ /dev/null @@ -1,268 +0,0 @@ -#include "ambient_weather.h" -#include - -#define TAG "WSProtocolAmbient_Weather" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/ambient_weather.c - * - * Decode Ambient Weather F007TH, F012TH, TF 30.3208.02, SwitchDoc F016TH. - * Devices supported: - * - Ambient Weather F007TH Thermo-Hygrometer. - * - Ambient Weather F012TH Indoor/Display Thermo-Hygrometer. - * - TFA senders 30.3208.02 from the TFA "Klima-Monitor" 30.3054, - * - SwitchDoc Labs F016TH. - * This decoder handles the 433mhz/868mhz thermo-hygrometers. - * The 915mhz (WH*) family of devices use different modulation/encoding. - * Byte 0 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 - * xxxxMMMM IIIIIIII BCCCTTTT TTTTTTTT HHHHHHHH MMMMMMMM - * - x: Unknown 0x04 on F007TH/F012TH - * - M: Model Number?, 0x05 on F007TH/F012TH/SwitchDocLabs F016TH - * - I: ID byte (8 bits), volatie, changes at power up, - * - B: Battery Low - * - C: Channel (3 bits 1-8) - F007TH set by Dip switch, F012TH soft setting - * - T: Temperature 12 bits - Fahrenheit * 10 + 400 - * - H: Humidity (8 bits) - * - M: Message integrity check LFSR Digest-8, gen 0x98, key 0x3e, init 0x64 - * - * three repeats without gap - * full preamble is 0x00145 (the last bits might not be fixed, e.g. 0x00146) - * and on decoding also 0xffd45 - */ - -#define AMBIENT_WEATHER_PACKET_HEADER_1 0xFFD440000000000 //0xffd45 .. 0xffd46 -#define AMBIENT_WEATHER_PACKET_HEADER_2 0x001440000000000 //0x00145 .. 0x00146 -#define AMBIENT_WEATHER_PACKET_HEADER_MASK 0xFFFFC0000000000 - -static const SubGhzBlockConst ws_protocol_ambient_weather_const = { - .te_short = 500, - .te_long = 1000, - .te_delta = 120, - .min_count_bit_for_found = 48, -}; - -struct WSProtocolDecoderAmbient_Weather { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - ManchesterState manchester_saved_state; - uint16_t header_count; -}; - -struct WSProtocolEncoderAmbient_Weather { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -const SubGhzProtocolDecoder ws_protocol_ambient_weather_decoder = { - .alloc = ws_protocol_decoder_ambient_weather_alloc, - .free = ws_protocol_decoder_ambient_weather_free, - - .feed = ws_protocol_decoder_ambient_weather_feed, - .reset = ws_protocol_decoder_ambient_weather_reset, - - .get_hash_data = ws_protocol_decoder_ambient_weather_get_hash_data, - .serialize = ws_protocol_decoder_ambient_weather_serialize, - .deserialize = ws_protocol_decoder_ambient_weather_deserialize, - .get_string = ws_protocol_decoder_ambient_weather_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_ambient_weather_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_ambient_weather = { - .name = WS_PROTOCOL_AMBIENT_WEATHER_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_ambient_weather_decoder, - .encoder = &ws_protocol_ambient_weather_encoder, -}; - -void* ws_protocol_decoder_ambient_weather_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderAmbient_Weather* instance = malloc(sizeof(WSProtocolDecoderAmbient_Weather)); - instance->base.protocol = &ws_protocol_ambient_weather; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_ambient_weather_free(void* context) { - furi_assert(context); - WSProtocolDecoderAmbient_Weather* instance = context; - free(instance); -} - -void ws_protocol_decoder_ambient_weather_reset(void* context) { - furi_assert(context); - WSProtocolDecoderAmbient_Weather* instance = context; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); -} - -static bool ws_protocol_ambient_weather_check_crc(WSProtocolDecoderAmbient_Weather* instance) { - uint8_t msg[] = { - instance->decoder.decode_data >> 40, - instance->decoder.decode_data >> 32, - instance->decoder.decode_data >> 24, - instance->decoder.decode_data >> 16, - instance->decoder.decode_data >> 8}; - - uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 5, 0x98, 0x3e) ^ 0x64; - return (crc == (uint8_t)(instance->decoder.decode_data & 0xFF)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_ambient_weather_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 32) & 0xFF; - instance->battery_low = (instance->data >> 31) & 1; - instance->channel = ((instance->data >> 28) & 0x07) + 1; - instance->temp = - locale_fahrenheit_to_celsius(((float)((instance->data >> 16) & 0x0FFF) - 400.0f) / 10.0f); - instance->humidity = (instance->data >> 8) & 0xFF; - instance->btn = WS_NO_BTN; - - // ToDo maybe it won't be needed - /* - Sanity checks to reduce false positives and other bad data - Packets with Bad data often pass the MIC check. - - humidity > 100 (such as 255) and - - temperatures > 140 F (such as 369.5 F and 348.8 F - Specs in the F007TH and F012TH manuals state the range is: - - Temperature: -40 to 140 F - - Humidity: 10 to 99% - @todo - sanity check b[0] "model number" - - 0x45 - F007TH and F012TH - - 0x?5 - SwitchDocLabs F016TH temperature sensor (based on comment b[0] & 0x0f == 5) - - ? - TFA 30.3208.02 - if (instance->humidity < 0 || instance->humidity > 100) { - ERROR; - } - - if (instance->temp < -40.0 || instance->temp > 140.0) { - ERROR; - } - */ -} - -void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderAmbient_Weather* instance = context; - - ManchesterEvent event = ManchesterEventReset; - if(!level) { - if(DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_short) < - ws_protocol_ambient_weather_const.te_delta) { - event = ManchesterEventShortLow; - } else if( - DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_long) < - ws_protocol_ambient_weather_const.te_delta * 2) { - event = ManchesterEventLongLow; - } - } else { - if(DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_short) < - ws_protocol_ambient_weather_const.te_delta) { - event = ManchesterEventShortHigh; - } else if( - DURATION_DIFF(duration, ws_protocol_ambient_weather_const.te_long) < - ws_protocol_ambient_weather_const.te_delta * 2) { - event = ManchesterEventLongHigh; - } - } - if(event != ManchesterEventReset) { - bool data; - bool data_ok = manchester_advance( - instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); - - if(data_ok) { - instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; - } - - if(((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) == - AMBIENT_WEATHER_PACKET_HEADER_1) || - ((instance->decoder.decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) == - AMBIENT_WEATHER_PACKET_HEADER_2)) { - if(ws_protocol_ambient_weather_check_crc(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = - ws_protocol_ambient_weather_const.min_count_bit_for_found; - ws_protocol_ambient_weather_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } - } - } else { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - } -} - -uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderAmbient_Weather* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_ambient_weather_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderAmbient_Weather* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderAmbient_Weather* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - ws_protocol_ambient_weather_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderAmbient_Weather* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/ambient_weather.h b/applications/external/weather_station/protocols/ambient_weather.h deleted file mode 100644 index 1694403cd..000000000 --- a/applications/external/weather_station/protocols/ambient_weather.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_AMBIENT_WEATHER_NAME "Ambient_Weather" - -typedef struct WSProtocolDecoderAmbient_Weather WSProtocolDecoderAmbient_Weather; -typedef struct WSProtocolEncoderAmbient_Weather WSProtocolEncoderAmbient_Weather; - -extern const SubGhzProtocolDecoder ws_protocol_ambient_weather_decoder; -extern const SubGhzProtocolEncoder ws_protocol_ambient_weather_encoder; -extern const SubGhzProtocol ws_protocol_ambient_weather; - -/** - * Allocate WSProtocolDecoderAmbient_Weather. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderAmbient_Weather* pointer to a WSProtocolDecoderAmbient_Weather instance - */ -void* ws_protocol_decoder_ambient_weather_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderAmbient_Weather. - * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance - */ -void ws_protocol_decoder_ambient_weather_free(void* context); - -/** - * Reset decoder WSProtocolDecoderAmbient_Weather. - * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance - */ -void ws_protocol_decoder_ambient_weather_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_ambient_weather_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_ambient_weather_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderAmbient_Weather. - * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_ambient_weather_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderAmbient_Weather. - * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_ambient_weather_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderAmbient_Weather instance - * @param output Resulting text - */ -void ws_protocol_decoder_ambient_weather_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/auriol_hg0601a.c b/applications/external/weather_station/protocols/auriol_hg0601a.c deleted file mode 100644 index 813fe1526..000000000 --- a/applications/external/weather_station/protocols/auriol_hg0601a.c +++ /dev/null @@ -1,248 +0,0 @@ -#include "auriol_hg0601a.h" - -#define TAG "WSProtocolAuriol_TH" - -/* - * -Auriol HG06061A-DCF-TX sensor. - -Data layout: - DDDDDDDD-B0-NN-TT-TTTTTTTTTT-CCCC-HHHHHHHH -Exmpl.: 11110100-10-01-00-0001001100-1111-01011101 - -- D: id, 8 bit -- B: where B is the battery status: 1=OK, 0=LOW, 1 bit -- 0: just zero :) -- N: NN is the channel: 00=CH1, 01=CH2, 11=CH3, 2bit -- T: temperature, 12 bit: 2's complement, scaled by 10 -- C: 4 bit: seems to be 0xf constantly, a separator between temp and humidity -- H: humidity sensor, humidity is 8 bits - - * The sensor sends 37 bits 10 times, - * the packets are ppm modulated (distance coding) with a pulse of ~500 us - * followed by a short gap of ~1000 us for a 0 bit or a long ~2000 us gap for a - * 1 bit, the sync gap is ~4000 us. - * - */ - -#define AURIOL_TH_CONST_DATA 0b1110 - -static const SubGhzBlockConst ws_protocol_auriol_th_const = { - .te_short = 500, - .te_long = 2000, - .te_delta = 150, - .min_count_bit_for_found = 37, -}; - -struct WSProtocolDecoderAuriol_TH { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; -}; - -struct WSProtocolEncoderAuriol_TH { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - auriol_THDecoderStepReset = 0, - auriol_THDecoderStepSaveDuration, - auriol_THDecoderStepCheckDuration, -} auriol_THDecoderStep; - -const SubGhzProtocolDecoder ws_protocol_auriol_th_decoder = { - .alloc = ws_protocol_decoder_auriol_th_alloc, - .free = ws_protocol_decoder_auriol_th_free, - - .feed = ws_protocol_decoder_auriol_th_feed, - .reset = ws_protocol_decoder_auriol_th_reset, - - .get_hash_data = ws_protocol_decoder_auriol_th_get_hash_data, - .serialize = ws_protocol_decoder_auriol_th_serialize, - .deserialize = ws_protocol_decoder_auriol_th_deserialize, - .get_string = ws_protocol_decoder_auriol_th_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_auriol_th_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_auriol_th = { - .name = WS_PROTOCOL_AURIOL_TH_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_auriol_th_decoder, - .encoder = &ws_protocol_auriol_th_encoder, -}; - -void* ws_protocol_decoder_auriol_th_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderAuriol_TH* instance = malloc(sizeof(WSProtocolDecoderAuriol_TH)); - instance->base.protocol = &ws_protocol_auriol_th; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_auriol_th_free(void* context) { - furi_assert(context); - WSProtocolDecoderAuriol_TH* instance = context; - free(instance); -} - -void ws_protocol_decoder_auriol_th_reset(void* context) { - furi_assert(context); - WSProtocolDecoderAuriol_TH* instance = context; - instance->decoder.parser_step = auriol_THDecoderStepReset; -} - -static bool ws_protocol_auriol_th_check(WSProtocolDecoderAuriol_TH* instance) { - uint8_t type = (instance->decoder.decode_data >> 8) & 0x0F; - - if((type == AURIOL_TH_CONST_DATA) && ((instance->decoder.decode_data >> 4) != 0xffffffff)) { - return true; - } else { - return false; - } - return true; -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_auriol_th_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 31) & 0xFF; - instance->battery_low = ((instance->data >> 30) & 1); - instance->channel = ((instance->data >> 25) & 0x03) + 1; - instance->btn = WS_NO_BTN; - if(!((instance->data >> 23) & 1)) { - instance->temp = (float)((instance->data >> 13) & 0x07FF) / 10.0f; - } else { - instance->temp = (float)((~(instance->data >> 13) & 0x07FF) + 1) / -10.0f; - } - - instance->humidity = (instance->data >> 1) & 0x7F; -} - -void ws_protocol_decoder_auriol_th_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderAuriol_TH* instance = context; - - switch(instance->decoder.parser_step) { - case auriol_THDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, ws_protocol_auriol_th_const.te_short * 8) < - ws_protocol_auriol_th_const.te_delta)) { - //Found sync - instance->decoder.parser_step = auriol_THDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } - break; - - case auriol_THDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = auriol_THDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = auriol_THDecoderStepReset; - } - break; - - case auriol_THDecoderStepCheckDuration: - if(!level) { - if(DURATION_DIFF(duration, ws_protocol_auriol_th_const.te_short * 8) < - ws_protocol_auriol_th_const.te_delta) { - //Found sync - instance->decoder.parser_step = auriol_THDecoderStepReset; - if((instance->decoder.decode_count_bit == - ws_protocol_auriol_th_const.min_count_bit_for_found) && - ws_protocol_auriol_th_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_auriol_th_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - instance->decoder.parser_step = auriol_THDecoderStepCheckDuration; - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - - break; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_auriol_th_const.te_short) < - ws_protocol_auriol_th_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_auriol_th_const.te_short * 2) < - ws_protocol_auriol_th_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = auriol_THDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_auriol_th_const.te_short) < - ws_protocol_auriol_th_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_auriol_th_const.te_short * 4) < - ws_protocol_auriol_th_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = auriol_THDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = auriol_THDecoderStepReset; - } - } else { - instance->decoder.parser_step = auriol_THDecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_auriol_th_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderAuriol_TH* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_auriol_th_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderAuriol_TH* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_auriol_th_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderAuriol_TH* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_auriol_th_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_auriol_th_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderAuriol_TH* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/auriol_hg0601a.h b/applications/external/weather_station/protocols/auriol_hg0601a.h deleted file mode 100644 index 155bb07fc..000000000 --- a/applications/external/weather_station/protocols/auriol_hg0601a.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_AURIOL_TH_NAME "Auriol HG06061" //HG06061A-DCF-TX - -typedef struct WSProtocolDecoderAuriol_TH WSProtocolDecoderAuriol_TH; -typedef struct WSProtocolEncoderAuriol_TH WSProtocolEncoderAuriol_TH; - -extern const SubGhzProtocolDecoder ws_protocol_auriol_th_decoder; -extern const SubGhzProtocolEncoder ws_protocol_auriol_th_encoder; -extern const SubGhzProtocol ws_protocol_auriol_th; - -/** - * Allocate WSProtocolDecoderAuriol_TH. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderAuriol_TH* pointer to a WSProtocolDecoderAuriol_TH instance - */ -void* ws_protocol_decoder_auriol_th_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderAuriol_TH. - * @param context Pointer to a WSProtocolDecoderAuriol_TH instance - */ -void ws_protocol_decoder_auriol_th_free(void* context); - -/** - * Reset decoder WSProtocolDecoderAuriol_TH. - * @param context Pointer to a WSProtocolDecoderAuriol_TH instance - */ -void ws_protocol_decoder_auriol_th_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderAuriol_TH instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_auriol_th_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderAuriol_TH instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_auriol_th_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderAuriol_TH. - * @param context Pointer to a WSProtocolDecoderAuriol_TH instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_auriol_th_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderAuriol_TH. - * @param context Pointer to a WSProtocolDecoderAuriol_TH instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_auriol_th_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderAuriol_TH instance - * @param output Resulting text - */ -void ws_protocol_decoder_auriol_th_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/gt_wt_02.c b/applications/external/weather_station/protocols/gt_wt_02.c deleted file mode 100644 index d333164b4..000000000 --- a/applications/external/weather_station/protocols/gt_wt_02.c +++ /dev/null @@ -1,255 +0,0 @@ -#include "gt_wt_02.h" - -#define TAG "WSProtocolGT_WT02" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/gt_wt_02.c - * - * GT-WT-02 sensor on 433.92MHz. - * Example and frame description provided by https://github.com/ludwich66 - * [01] {37} 34 00 ed 47 60 : 00110100 00000000 11101101 01000111 01100000 - * code, BatOK,not-man-send, Channel1, +23,7C, 35% - * [01] {37} 34 8f 87 15 90 : 00110100 10001111 10000111 00010101 10010000 - * code, BatOK,not-man-send, Channel1,-12,1C, 10% - * Humidity: - * - the working range is 20-90 % - * - if "LL" in display view it sends 10 % - * - if "HH" in display view it sends 110% - * SENSOR: GT-WT-02 (ALDI Globaltronics..) - * TYP IIIIIIII BMCCTTTT TTTTTTTT HHHHHHHX XXXXX - * TYPE Description: - * - I = Random Device Code, changes with battery reset - * - B = Battery 0=OK 1=LOW - * - M = Manual Send Button Pressed 0=not pressed 1=pressed - * - C = Channel 00=CH1, 01=CH2, 10=CH3 - * - T = Temperature, 12 Bit 2's complement, scaled by 10 - * - H = Humidity = 7 Bit bin2dez 00-99, Display LL=10%, Display HH=110% (Range 20-90%) - * - X = Checksum, sum modulo 64 - * A Lidl AURIO (from 12/2018) with PCB marking YJ-T12 V02 has two extra bits in front. - * -*/ - -static const SubGhzBlockConst ws_protocol_gt_wt_02_const = { - .te_short = 500, - .te_long = 2000, - .te_delta = 150, - .min_count_bit_for_found = 37, -}; - -struct WSProtocolDecoderGT_WT02 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; -}; - -struct WSProtocolEncoderGT_WT02 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - GT_WT02DecoderStepReset = 0, - GT_WT02DecoderStepSaveDuration, - GT_WT02DecoderStepCheckDuration, -} GT_WT02DecoderStep; - -const SubGhzProtocolDecoder ws_protocol_gt_wt_02_decoder = { - .alloc = ws_protocol_decoder_gt_wt_02_alloc, - .free = ws_protocol_decoder_gt_wt_02_free, - - .feed = ws_protocol_decoder_gt_wt_02_feed, - .reset = ws_protocol_decoder_gt_wt_02_reset, - - .get_hash_data = ws_protocol_decoder_gt_wt_02_get_hash_data, - .serialize = ws_protocol_decoder_gt_wt_02_serialize, - .deserialize = ws_protocol_decoder_gt_wt_02_deserialize, - .get_string = ws_protocol_decoder_gt_wt_02_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_gt_wt_02_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_gt_wt_02 = { - .name = WS_PROTOCOL_GT_WT_02_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_gt_wt_02_decoder, - .encoder = &ws_protocol_gt_wt_02_encoder, -}; - -void* ws_protocol_decoder_gt_wt_02_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderGT_WT02* instance = malloc(sizeof(WSProtocolDecoderGT_WT02)); - instance->base.protocol = &ws_protocol_gt_wt_02; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_gt_wt_02_free(void* context) { - furi_assert(context); - WSProtocolDecoderGT_WT02* instance = context; - free(instance); -} - -void ws_protocol_decoder_gt_wt_02_reset(void* context) { - furi_assert(context); - WSProtocolDecoderGT_WT02* instance = context; - instance->decoder.parser_step = GT_WT02DecoderStepReset; -} - -static bool ws_protocol_gt_wt_02_check(WSProtocolDecoderGT_WT02* instance) { - if(!instance->decoder.decode_data) return false; - uint8_t sum = (instance->decoder.decode_data >> 5) & 0xe; - uint64_t temp_data = instance->decoder.decode_data >> 9; - for(uint8_t i = 0; i < 7; i++) { - sum += (temp_data >> (i * 4)) & 0xF; - } - return ((uint8_t)(instance->decoder.decode_data & 0x3F) == (sum & 0x3F)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_gt_wt_02_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 29) & 0xFF; - instance->battery_low = (instance->data >> 28) & 1; - instance->btn = (instance->data >> 27) & 1; - instance->channel = ((instance->data >> 25) & 0x3) + 1; - - if(!((instance->data >> 24) & 1)) { - instance->temp = (float)((instance->data >> 13) & 0x07FF) / 10.0f; - } else { - instance->temp = (float)((~(instance->data >> 13) & 0x07FF) + 1) / -10.0f; - } - - instance->humidity = (instance->data >> 6) & 0x7F; - if(instance->humidity <= 10) // actually the sensors sends 10 below working range of 20% - instance->humidity = 0; - else if(instance->humidity > 90) // actually the sensors sends 110 above working range of 90% - instance->humidity = 100; -} - -void ws_protocol_decoder_gt_wt_02_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderGT_WT02* instance = context; - - switch(instance->decoder.parser_step) { - case GT_WT02DecoderStepReset: - if((!level) && (DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_short * 18) < - ws_protocol_gt_wt_02_const.te_delta * 8)) { - //Found syncPrefix - instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } - break; - - case GT_WT02DecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = GT_WT02DecoderStepCheckDuration; - } else { - instance->decoder.parser_step = GT_WT02DecoderStepReset; - } - break; - - case GT_WT02DecoderStepCheckDuration: - if(!level) { - if(DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_02_const.te_short) < - ws_protocol_gt_wt_02_const.te_delta) { - if(DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_short * 18) < - ws_protocol_gt_wt_02_const.te_delta * 8) { - //Found syncPostfix - instance->decoder.parser_step = GT_WT02DecoderStepReset; - if((instance->decoder.decode_count_bit == - ws_protocol_gt_wt_02_const.min_count_bit_for_found) && - ws_protocol_gt_wt_02_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_gt_wt_02_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } else if(instance->decoder.decode_count_bit == 1) { - instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else if( - DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_long) < - ws_protocol_gt_wt_02_const.te_delta * 2) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; - } else if( - DURATION_DIFF(duration, ws_protocol_gt_wt_02_const.te_long * 2) < - ws_protocol_gt_wt_02_const.te_delta * 4) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = GT_WT02DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = GT_WT02DecoderStepReset; - } - } else { - instance->decoder.parser_step = GT_WT02DecoderStepReset; - } - } else { - instance->decoder.parser_step = GT_WT02DecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderGT_WT02* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_gt_wt_02_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderGT_WT02* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderGT_WT02* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_gt_wt_02_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_gt_wt_02_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderGT_WT02* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/gt_wt_02.h b/applications/external/weather_station/protocols/gt_wt_02.h deleted file mode 100644 index e13576d21..000000000 --- a/applications/external/weather_station/protocols/gt_wt_02.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_GT_WT_02_NAME "GT-WT02" - -typedef struct WSProtocolDecoderGT_WT02 WSProtocolDecoderGT_WT02; -typedef struct WSProtocolEncoderGT_WT02 WSProtocolEncoderGT_WT02; - -extern const SubGhzProtocolDecoder ws_protocol_gt_wt_02_decoder; -extern const SubGhzProtocolEncoder ws_protocol_gt_wt_02_encoder; -extern const SubGhzProtocol ws_protocol_gt_wt_02; - -/** - * Allocate WSProtocolDecoderGT_WT02. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderGT_WT02* pointer to a WSProtocolDecoderGT_WT02 instance - */ -void* ws_protocol_decoder_gt_wt_02_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderGT_WT02. - * @param context Pointer to a WSProtocolDecoderGT_WT02 instance - */ -void ws_protocol_decoder_gt_wt_02_free(void* context); - -/** - * Reset decoder WSProtocolDecoderGT_WT02. - * @param context Pointer to a WSProtocolDecoderGT_WT02 instance - */ -void ws_protocol_decoder_gt_wt_02_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderGT_WT02 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_gt_wt_02_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderGT_WT02 instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_gt_wt_02_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderGT_WT02. - * @param context Pointer to a WSProtocolDecoderGT_WT02 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_gt_wt_02_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderGT_WT02. - * @param context Pointer to a WSProtocolDecoderGT_WT02 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_gt_wt_02_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderGT_WT02 instance - * @param output Resulting text - */ -void ws_protocol_decoder_gt_wt_02_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/gt_wt_03.c b/applications/external/weather_station/protocols/gt_wt_03.c deleted file mode 100644 index 30430b839..000000000 --- a/applications/external/weather_station/protocols/gt_wt_03.c +++ /dev/null @@ -1,330 +0,0 @@ -#include "gt_wt_03.h" - -#define TAG "WSProtocolGT_WT03" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/gt_wt_03.c - * - * - * Globaltronics GT-WT-03 sensor on 433.92MHz. - * The 01-set sensor has 60 ms packet gap with 10 repeats. - * The 02-set sensor has no packet gap with 23 repeats. - * Example: - * {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes ] - * {41} 17 cf be fa 6a 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-Yes Batt-Changed ] - * {41} 17 cf fe fa ea 80 [ S1 C1 26,1 C 78.9 F 48% Bat-Good Manual-No Batt-Changed ] - * {41} 01 cf 6f 11 b2 80 [ S2 C2 23,8 C 74.8 F 48% Bat-LOW Manual-No ] - * {41} 01 c8 d0 2b 76 80 [ S2 C3 -4,4 C 24.1 F 55% Bat-Good Manual-No Batt-Changed ] - * Format string: - * ID:8h HUM:8d B:b M:b C:2d TEMP:12d CHK:8h 1x - * Data layout: - * TYP IIIIIIII HHHHHHHH BMCCTTTT TTTTTTTT XXXXXXXX - * - I: Random Device Code: changes with battery reset - * - H: Humidity: 8 Bit 00-99, Display LL=10%, Display HH=110% (Range 20-95%) - * - B: Battery: 0=OK 1=LOW - * - M: Manual Send Button Pressed: 0=not pressed, 1=pressed - * - C: Channel: 00=CH1, 01=CH2, 10=CH3 - * - T: Temperature: 12 Bit 2's complement, scaled by 10, range-50.0 C (-50.1 shown as Lo) to +70.0 C (+70.1 C is shown as Hi) - * - X: Checksum, xor shifting key per byte - * Humidity: - * - the working range is 20-95 % - * - if "LL" in display view it sends 10 % - * - if "HH" in display view it sends 110% - * Checksum: - * Per byte xor the key for each 1-bit, shift per bit. Key list per bit, starting at MSB: - * - 0x00 [07] - * - 0x80 [06] - * - 0x40 [05] - * - 0x20 [04] - * - 0x10 [03] - * - 0x88 [02] - * - 0xc4 [01] - * - 0x62 [00] - * Note: this can also be seen as lower byte of a Galois/Fibonacci LFSR-16, gen 0x00, init 0x3100 (or 0x62 if reversed) resetting at every byte. - * Battery voltages: - * - U=<2,65V +- ~5% Battery indicator - * - U=>2.10C +- 5% plausible readings - * - U=2,00V +- ~5% Temperature offset -5°C Humidity offset unknown - * - U=<1,95V +- ~5% does not initialize anymore - * - U=1,90V +- 5% temperature offset -15°C - * - U=1,80V +- 5% Display is showing refresh pattern - * - U=1.75V +- ~5% TX causes cut out - * - */ - -static const SubGhzBlockConst ws_protocol_gt_wt_03_const = { - .te_short = 285, - .te_long = 570, - .te_delta = 120, - .min_count_bit_for_found = 41, -}; - -struct WSProtocolDecoderGT_WT03 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - - uint16_t header_count; -}; - -struct WSProtocolEncoderGT_WT03 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - GT_WT03DecoderStepReset = 0, - GT_WT03DecoderStepCheckPreambule, - GT_WT03DecoderStepSaveDuration, - GT_WT03DecoderStepCheckDuration, -} GT_WT03DecoderStep; - -const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder = { - .alloc = ws_protocol_decoder_gt_wt_03_alloc, - .free = ws_protocol_decoder_gt_wt_03_free, - - .feed = ws_protocol_decoder_gt_wt_03_feed, - .reset = ws_protocol_decoder_gt_wt_03_reset, - - .get_hash_data = ws_protocol_decoder_gt_wt_03_get_hash_data, - .serialize = ws_protocol_decoder_gt_wt_03_serialize, - .deserialize = ws_protocol_decoder_gt_wt_03_deserialize, - .get_string = ws_protocol_decoder_gt_wt_03_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_gt_wt_03 = { - .name = WS_PROTOCOL_GT_WT_03_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_gt_wt_03_decoder, - .encoder = &ws_protocol_gt_wt_03_encoder, -}; - -void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderGT_WT03* instance = malloc(sizeof(WSProtocolDecoderGT_WT03)); - instance->base.protocol = &ws_protocol_gt_wt_03; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_gt_wt_03_free(void* context) { - furi_assert(context); - WSProtocolDecoderGT_WT03* instance = context; - free(instance); -} - -void ws_protocol_decoder_gt_wt_03_reset(void* context) { - furi_assert(context); - WSProtocolDecoderGT_WT03* instance = context; - instance->decoder.parser_step = GT_WT03DecoderStepReset; -} - -static bool ws_protocol_gt_wt_03_check_crc(WSProtocolDecoderGT_WT03* instance) { - uint8_t msg[] = { - instance->decoder.decode_data >> 33, - instance->decoder.decode_data >> 25, - instance->decoder.decode_data >> 17, - instance->decoder.decode_data >> 9}; - - uint8_t sum = 0; - for(unsigned k = 0; k < sizeof(msg); ++k) { - uint8_t data = msg[k]; - uint16_t key = 0x3100; - for(int i = 7; i >= 0; --i) { - // XOR key into sum if data bit is set - if((data >> i) & 1) sum ^= key & 0xff; - // roll the key right - key = (key >> 1); - } - } - return ((sum ^ (uint8_t)((instance->decoder.decode_data >> 1) & 0xFF)) == 0x2D); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_gt_wt_03_remote_controller(WSBlockGeneric* instance) { - instance->id = instance->data >> 33; - instance->humidity = (instance->data >> 25) & 0xFF; - - if(instance->humidity <= 10) { // actually the sensors sends 10 below working range of 20% - instance->humidity = 0; - } else if(instance->humidity > 95) { // actually the sensors sends 110 above working range of 90% - instance->humidity = 100; - } - - instance->battery_low = (instance->data >> 24) & 1; - instance->btn = (instance->data >> 23) & 1; - instance->channel = ((instance->data >> 21) & 0x03) + 1; - - if(!((instance->data >> 20) & 1)) { - instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f; - } else { - instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f; - } -} - -void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderGT_WT03* instance = context; - - switch(instance->decoder.parser_step) { - case GT_WT03DecoderStepReset: - if((level) && (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < - ws_protocol_gt_wt_03_const.te_delta * 2)) { - instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - - case GT_WT03DecoderStepCheckPreambule: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) < - ws_protocol_gt_wt_03_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < - ws_protocol_gt_wt_03_const.te_delta * 2)) { - //Found preambule - instance->header_count++; - } else if(instance->header_count == 4) { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) < - ws_protocol_gt_wt_03_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) < - ws_protocol_gt_wt_03_const.te_delta)) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) < - ws_protocol_gt_wt_03_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) < - ws_protocol_gt_wt_03_const.te_delta)) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = GT_WT03DecoderStepReset; - } - } else { - instance->decoder.parser_step = GT_WT03DecoderStepReset; - } - } - break; - - case GT_WT03DecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = GT_WT03DecoderStepCheckDuration; - } else { - instance->decoder.parser_step = GT_WT03DecoderStepReset; - } - break; - - case GT_WT03DecoderStepCheckDuration: - if(!level) { - if(((DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short * 3) < - ws_protocol_gt_wt_03_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short * 3) < - ws_protocol_gt_wt_03_const.te_delta * 2))) { - if((instance->decoder.decode_count_bit == - ws_protocol_gt_wt_03_const.min_count_bit_for_found) && - ws_protocol_gt_wt_03_check_crc(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_gt_wt_03_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 1; - instance->decoder.parser_step = GT_WT03DecoderStepCheckPreambule; - break; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_short) < - ws_protocol_gt_wt_03_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_long) < - ws_protocol_gt_wt_03_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_gt_wt_03_const.te_long) < - ws_protocol_gt_wt_03_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_gt_wt_03_const.te_short) < - ws_protocol_gt_wt_03_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = GT_WT03DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = GT_WT03DecoderStepReset; - } - } else { - instance->decoder.parser_step = GT_WT03DecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderGT_WT03* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_gt_wt_03_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderGT_WT03* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderGT_WT03* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_gt_wt_03_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderGT_WT03* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/gt_wt_03.h b/applications/external/weather_station/protocols/gt_wt_03.h deleted file mode 100644 index d566bb399..000000000 --- a/applications/external/weather_station/protocols/gt_wt_03.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_GT_WT_03_NAME "GT-WT03" - -typedef struct WSProtocolDecoderGT_WT03 WSProtocolDecoderGT_WT03; -typedef struct WSProtocolEncoderGT_WT03 WSProtocolEncoderGT_WT03; - -extern const SubGhzProtocolDecoder ws_protocol_gt_wt_03_decoder; -extern const SubGhzProtocolEncoder ws_protocol_gt_wt_03_encoder; -extern const SubGhzProtocol ws_protocol_gt_wt_03; - -/** - * Allocate WSProtocolDecoderGT_WT03. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderGT_WT03* pointer to a WSProtocolDecoderGT_WT03 instance - */ -void* ws_protocol_decoder_gt_wt_03_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderGT_WT03. - * @param context Pointer to a WSProtocolDecoderGT_WT03 instance - */ -void ws_protocol_decoder_gt_wt_03_free(void* context); - -/** - * Reset decoder WSProtocolDecoderGT_WT03. - * @param context Pointer to a WSProtocolDecoderGT_WT03 instance - */ -void ws_protocol_decoder_gt_wt_03_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderGT_WT03 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_gt_wt_03_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderGT_WT03 instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_gt_wt_03_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderGT_WT03. - * @param context Pointer to a WSProtocolDecoderGT_WT03 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_gt_wt_03_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderGT_WT03. - * @param context Pointer to a WSProtocolDecoderGT_WT03 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_gt_wt_03_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderGT_WT03 instance - * @param output Resulting text - */ -void ws_protocol_decoder_gt_wt_03_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/infactory.c b/applications/external/weather_station/protocols/infactory.c deleted file mode 100644 index 4b0e6602a..000000000 --- a/applications/external/weather_station/protocols/infactory.c +++ /dev/null @@ -1,286 +0,0 @@ -#include "infactory.h" - -#define TAG "WSProtocolInfactory" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/infactory.c - * - * Analysis using Genuino (see http://gitlab.com/hp-uno, e.g. uno_log_433): - * Observed On-Off-Key (OOK) data pattern: - * preamble syncPrefix data...(40 bit) syncPostfix - * HHLL HHLL HHLL HHLL HLLLLLLLLLLLLLLLL (HLLLL HLLLLLLLL HLLLL HLLLLLLLL ....) HLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL - * Breakdown: - * - four preamble pairs '1'/'0' each with a length of ca. 1000us - * - syncPre, syncPost, data0, data1 have a '1' start pulse of ca. 500us - * - syncPre pulse before dataPtr has a '0' pulse length of ca. 8000us - * - data0 (0-bits) have then a '0' pulse length of ca. 2000us - * - data1 (1-bits) have then a '0' pulse length of ca. 4000us - * - syncPost after dataPtr has a '0' pulse length of ca. 16000us - * This analysis is the reason for the new r_device definitions below. - * NB: pulse_slicer_ppm does not use .gap_limit if .tolerance is set. - * - * Outdoor sensor, transmits temperature and humidity data - * - inFactory NC-3982-913/NX-5817-902, Pearl (for FWS-686 station) - * - nor-tec 73383 (weather station + sensor), Schou Company AS, Denmark - * - DAY 73365 (weather station + sensor), Schou Company AS, Denmark - * Known brand names: inFactory, nor-tec, GreenBlue, DAY. Manufacturer in China. - * Transmissions includes an id. Every 60 seconds the sensor transmits 6 packets: - * 0000 1111 | 0011 0000 | 0101 1100 | 1110 0111 | 0110 0001 - * iiii iiii | cccc ub?? | tttt tttt | tttt hhhh | hhhh ??nn - * - i: identification; changes on battery switch - * - c: CRC-4; CCITT checksum, see below for computation specifics - * - u: unknown; (sometimes set at power-on, but not always) - * - b: battery low; flag to indicate low battery voltage - * - h: Humidity; BCD-encoded, each nibble is one digit, 'A0' means 100%rH - * - t: Temperature; in °F as binary number with one decimal place + 90 °F offset - * - n: Channel; Channel number 1 - 3 - * - */ - -static const SubGhzBlockConst ws_protocol_infactory_const = { - .te_short = 500, - .te_long = 2000, - .te_delta = 150, - .min_count_bit_for_found = 40, -}; - -struct WSProtocolDecoderInfactory { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - - uint16_t header_count; -}; - -struct WSProtocolEncoderInfactory { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - InfactoryDecoderStepReset = 0, - InfactoryDecoderStepCheckPreambule, - InfactoryDecoderStepSaveDuration, - InfactoryDecoderStepCheckDuration, -} InfactoryDecoderStep; - -const SubGhzProtocolDecoder ws_protocol_infactory_decoder = { - .alloc = ws_protocol_decoder_infactory_alloc, - .free = ws_protocol_decoder_infactory_free, - - .feed = ws_protocol_decoder_infactory_feed, - .reset = ws_protocol_decoder_infactory_reset, - - .get_hash_data = ws_protocol_decoder_infactory_get_hash_data, - .serialize = ws_protocol_decoder_infactory_serialize, - .deserialize = ws_protocol_decoder_infactory_deserialize, - .get_string = ws_protocol_decoder_infactory_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_infactory_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_infactory = { - .name = WS_PROTOCOL_INFACTORY_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_infactory_decoder, - .encoder = &ws_protocol_infactory_encoder, -}; - -void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderInfactory* instance = malloc(sizeof(WSProtocolDecoderInfactory)); - instance->base.protocol = &ws_protocol_infactory; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_infactory_free(void* context) { - furi_assert(context); - WSProtocolDecoderInfactory* instance = context; - free(instance); -} - -void ws_protocol_decoder_infactory_reset(void* context) { - furi_assert(context); - WSProtocolDecoderInfactory* instance = context; - instance->decoder.parser_step = InfactoryDecoderStepReset; -} - -static bool ws_protocol_infactory_check_crc(WSProtocolDecoderInfactory* instance) { - uint8_t msg[] = { - instance->decoder.decode_data >> 32, - (((instance->decoder.decode_data >> 24) & 0x0F) | (instance->decoder.decode_data & 0x0F) - << 4), - instance->decoder.decode_data >> 16, - instance->decoder.decode_data >> 8, - instance->decoder.decode_data}; - - uint8_t crc = - subghz_protocol_blocks_crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704 - crc ^= msg[4] >> 4; // last nibble is only XORed - return (crc == ((instance->decoder.decode_data >> 28) & 0x0F)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_infactory_remote_controller(WSBlockGeneric* instance) { - instance->id = instance->data >> 32; - instance->battery_low = (instance->data >> 26) & 1; - instance->btn = WS_NO_BTN; - instance->temp = - locale_fahrenheit_to_celsius(((float)((instance->data >> 12) & 0x0FFF) - 900.0f) / 10.0f); - instance->humidity = - (((instance->data >> 8) & 0x0F) * 10) + ((instance->data >> 4) & 0x0F); // BCD, 'A0'=100%rH - instance->channel = instance->data & 0x03; -} - -void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderInfactory* instance = context; - - switch(instance->decoder.parser_step) { - case InfactoryDecoderStepReset: - if((level) && (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) < - ws_protocol_infactory_const.te_delta * 2)) { - instance->decoder.parser_step = InfactoryDecoderStepCheckPreambule; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - - case InfactoryDecoderStepCheckPreambule: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short * 2) < - ws_protocol_infactory_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 2) < - ws_protocol_infactory_const.te_delta * 2)) { - //Found preambule - instance->header_count++; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < - ws_protocol_infactory_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_infactory_const.te_short * 16) < - ws_protocol_infactory_const.te_delta * 8)) { - //Found syncPrefix - if(instance->header_count > 3) { - instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } - } else { - instance->decoder.parser_step = InfactoryDecoderStepReset; - } - } - break; - - case InfactoryDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = InfactoryDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = InfactoryDecoderStepReset; - } - break; - - case InfactoryDecoderStepCheckDuration: - if(!level) { - if(duration >= ((uint32_t)ws_protocol_infactory_const.te_short * 30)) { - //Found syncPostfix - if((instance->decoder.decode_count_bit == - ws_protocol_infactory_const.min_count_bit_for_found) && - ws_protocol_infactory_check_crc(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_infactory_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = InfactoryDecoderStepReset; - break; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < - ws_protocol_infactory_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_infactory_const.te_long) < - ws_protocol_infactory_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_infactory_const.te_short) < - ws_protocol_infactory_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_infactory_const.te_long * 2) < - ws_protocol_infactory_const.te_delta * 4)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = InfactoryDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = InfactoryDecoderStepReset; - } - } else { - instance->decoder.parser_step = InfactoryDecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderInfactory* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_infactory_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderInfactory* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderInfactory* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_infactory_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderInfactory* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/infactory.h b/applications/external/weather_station/protocols/infactory.h deleted file mode 100644 index 9431a067e..000000000 --- a/applications/external/weather_station/protocols/infactory.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_INFACTORY_NAME "inFactory-TH" - -typedef struct WSProtocolDecoderInfactory WSProtocolDecoderInfactory; -typedef struct WSProtocolEncoderInfactory WSProtocolEncoderInfactory; - -extern const SubGhzProtocolDecoder ws_protocol_infactory_decoder; -extern const SubGhzProtocolEncoder ws_protocol_infactory_encoder; -extern const SubGhzProtocol ws_protocol_infactory; - -/** - * Allocate WSProtocolDecoderInfactory. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderInfactory* pointer to a WSProtocolDecoderInfactory instance - */ -void* ws_protocol_decoder_infactory_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderInfactory. - * @param context Pointer to a WSProtocolDecoderInfactory instance - */ -void ws_protocol_decoder_infactory_free(void* context); - -/** - * Reset decoder WSProtocolDecoderInfactory. - * @param context Pointer to a WSProtocolDecoderInfactory instance - */ -void ws_protocol_decoder_infactory_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderInfactory instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_infactory_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderInfactory instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_infactory_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderInfactory. - * @param context Pointer to a WSProtocolDecoderInfactory instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_infactory_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderInfactory. - * @param context Pointer to a WSProtocolDecoderInfactory instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_infactory_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderInfactory instance - * @param output Resulting text - */ -void ws_protocol_decoder_infactory_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/lacrosse_tx.c b/applications/external/weather_station/protocols/lacrosse_tx.c deleted file mode 100644 index f4b850d76..000000000 --- a/applications/external/weather_station/protocols/lacrosse_tx.c +++ /dev/null @@ -1,319 +0,0 @@ -#include "lacrosse_tx.h" - -#define TAG "WSProtocolLaCrosse_TX" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse.c - * - * - * LaCrosse TX 433 Mhz Temperature and Humidity Sensors. - * - Tested: TX-7U and TX-6U (Temperature only) - * - Not Tested but should work: TX-3, TX-4 - * - also TFA Dostmann 30.3120.90 sensor (for e.g. 35.1018.06 (WS-9015) station) - * - also TFA Dostmann 30.3121 sensor - * Protocol Documentation: http://www.f6fbb.org/domo/sensors/tx3_th.php - * Message is 44 bits, 11 x 4 bit nybbles: - * [00] [cnt = 10] [type] [addr] [addr + parity] [v1] [v2] [v3] [iv1] [iv2] [check] - * Notes: - * - Zero Pulses are longer (1,400 uS High, 1,000 uS Low) = 2,400 uS - * - One Pulses are shorter ( 550 uS High, 1,000 uS Low) = 1,600 uS - * - Sensor id changes when the battery is changed - * - Primary Value are BCD with one decimal place: vvv = 12.3 - * - Secondary value is integer only intval = 12, seems to be a repeat of primary - * This may actually be an additional data check because the 4 bit checksum - * and parity bit is pretty week at detecting errors. - * - Temperature is in Celsius with 50.0 added (to handle negative values) - * - Humidity values appear to be integer precision, decimal always 0. - * - There is a 4 bit checksum and a parity bit covering the three digit value - * - Parity check for TX-3 and TX-4 might be different. - * - Msg sent with one repeat after 30 mS - * - Temperature and humidity are sent as separate messages - * - Frequency for each sensor may be could be off by as much as 50-75 khz - * - LaCrosse Sensors in other frequency ranges (915 Mhz) use FSK not OOK - * so they can't be decoded by rtl_433 currently. - * - Temperature and Humidity are sent in different messages bursts. -*/ - -#define LACROSSE_TX_GAP 1000 -#define LACROSSE_TX_BIT_SIZE 44 -#define LACROSSE_TX_SUNC_PATTERN 0x0A000000000 -#define LACROSSE_TX_SUNC_MASK 0x0F000000000 -#define LACROSSE_TX_MSG_TYPE_TEMP 0x00 -#define LACROSSE_TX_MSG_TYPE_HUM 0x0E - -static const SubGhzBlockConst ws_protocol_lacrosse_tx_const = { - .te_short = 550, - .te_long = 1300, - .te_delta = 120, - .min_count_bit_for_found = 40, -}; - -struct WSProtocolDecoderLaCrosse_TX { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - - uint16_t header_count; -}; - -struct WSProtocolEncoderLaCrosse_TX { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - LaCrosse_TXDecoderStepReset = 0, - LaCrosse_TXDecoderStepCheckPreambule, - LaCrosse_TXDecoderStepSaveDuration, - LaCrosse_TXDecoderStepCheckDuration, -} LaCrosse_TXDecoderStep; - -const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder = { - .alloc = ws_protocol_decoder_lacrosse_tx_alloc, - .free = ws_protocol_decoder_lacrosse_tx_free, - - .feed = ws_protocol_decoder_lacrosse_tx_feed, - .reset = ws_protocol_decoder_lacrosse_tx_reset, - - .get_hash_data = ws_protocol_decoder_lacrosse_tx_get_hash_data, - .serialize = ws_protocol_decoder_lacrosse_tx_serialize, - .deserialize = ws_protocol_decoder_lacrosse_tx_deserialize, - .get_string = ws_protocol_decoder_lacrosse_tx_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_lacrosse_tx = { - .name = WS_PROTOCOL_LACROSSE_TX_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_lacrosse_tx_decoder, - .encoder = &ws_protocol_lacrosse_tx_encoder, -}; - -void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderLaCrosse_TX* instance = malloc(sizeof(WSProtocolDecoderLaCrosse_TX)); - instance->base.protocol = &ws_protocol_lacrosse_tx; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_lacrosse_tx_free(void* context) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX* instance = context; - free(instance); -} - -void ws_protocol_decoder_lacrosse_tx_reset(void* context) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX* instance = context; - instance->header_count = 0; - instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; -} - -static bool ws_protocol_lacrosse_tx_check_crc(WSProtocolDecoderLaCrosse_TX* instance) { - if(!instance->decoder.decode_data) return false; - uint8_t msg[] = { - (instance->decoder.decode_data >> 36) & 0x0F, - (instance->decoder.decode_data >> 32) & 0x0F, - (instance->decoder.decode_data >> 28) & 0x0F, - (instance->decoder.decode_data >> 24) & 0x0F, - (instance->decoder.decode_data >> 20) & 0x0F, - (instance->decoder.decode_data >> 16) & 0x0F, - (instance->decoder.decode_data >> 12) & 0x0F, - (instance->decoder.decode_data >> 8) & 0x0F, - (instance->decoder.decode_data >> 4) & 0x0F}; - - uint8_t crc = subghz_protocol_blocks_add_bytes(msg, 9); - return ((crc & 0x0F) == ((instance->decoder.decode_data) & 0x0F)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_lacrosse_tx_remote_controller(WSBlockGeneric* instance) { - uint8_t msg_type = (instance->data >> 32) & 0x0F; - instance->id = (((instance->data >> 28) & 0x0F) << 3) | (((instance->data >> 24) & 0x0F) >> 1); - - float msg_value = (float)((instance->data >> 20) & 0x0F) * 10.0f + - (float)((instance->data >> 16) & 0x0F) + - (float)((instance->data >> 12) & 0x0F) * 0.1f; - - if(msg_type == LACROSSE_TX_MSG_TYPE_TEMP) { //-V1051 - instance->temp = msg_value - 50.0f; - instance->humidity = WS_NO_HUMIDITY; - } else if(msg_type == LACROSSE_TX_MSG_TYPE_HUM) { - //ToDo for verification, records are needed with sensors maintaining temperature and temperature for this standard - instance->humidity = (uint8_t)msg_value; - } else { - furi_crash("WS: WSProtocolLaCrosse_TX incorrect msg_type."); - } - - instance->btn = WS_NO_BTN; - instance->battery_low = WS_NO_BATT; - instance->channel = WS_NO_CHANNEL; -} - -void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX* instance = context; - - switch(instance->decoder.parser_step) { - case LaCrosse_TXDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, LACROSSE_TX_GAP) < - ws_protocol_lacrosse_tx_const.te_delta * 2)) { - instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckPreambule; - instance->header_count = 0; - } - break; - - case LaCrosse_TXDecoderStepCheckPreambule: - - if(level) { - if((DURATION_DIFF(duration, ws_protocol_lacrosse_tx_const.te_short) < - ws_protocol_lacrosse_tx_const.te_delta) && - (instance->header_count > 1)) { - instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.te_last = duration; - } else if(duration > (ws_protocol_lacrosse_tx_const.te_long * 2)) { - instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; - } - } else { - if(DURATION_DIFF(duration, LACROSSE_TX_GAP) < - ws_protocol_lacrosse_tx_const.te_delta * 2) { - instance->decoder.te_last = duration; - instance->header_count++; - } else { - instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; - } - } - - break; - - case LaCrosse_TXDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; - } - break; - - case LaCrosse_TXDecoderStepCheckDuration: - - if(!level) { - if(duration > LACROSSE_TX_GAP * 3) { - if(DURATION_DIFF( - instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) < - ws_protocol_lacrosse_tx_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; - } else if( - DURATION_DIFF( - instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) < - ws_protocol_lacrosse_tx_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; - } - if((instance->decoder.decode_data & LACROSSE_TX_SUNC_MASK) == - LACROSSE_TX_SUNC_PATTERN) { - if(ws_protocol_lacrosse_tx_check_crc(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = LACROSSE_TX_BIT_SIZE; - ws_protocol_lacrosse_tx_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - } - - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 0; - instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; - break; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) < - ws_protocol_lacrosse_tx_const.te_delta) && - (DURATION_DIFF(duration, LACROSSE_TX_GAP) < - ws_protocol_lacrosse_tx_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) < - ws_protocol_lacrosse_tx_const.te_delta) && - (DURATION_DIFF(duration, LACROSSE_TX_GAP) < - ws_protocol_lacrosse_tx_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; - } - - } else { - instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; - } - - break; - } -} - -uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_lacrosse_tx_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/lacrosse_tx.h b/applications/external/weather_station/protocols/lacrosse_tx.h deleted file mode 100644 index 151282b3a..000000000 --- a/applications/external/weather_station/protocols/lacrosse_tx.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_LACROSSE_TX_NAME "LaCrosse_TX" - -typedef struct WSProtocolDecoderLaCrosse_TX WSProtocolDecoderLaCrosse_TX; -typedef struct WSProtocolEncoderLaCrosse_TX WSProtocolEncoderLaCrosse_TX; - -extern const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder; -extern const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder; -extern const SubGhzProtocol ws_protocol_lacrosse_tx; - -/** - * Allocate WSProtocolDecoderLaCrosse_TX. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderLaCrosse_TX* pointer to a WSProtocolDecoderLaCrosse_TX instance - */ -void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderLaCrosse_TX. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance - */ -void ws_protocol_decoder_lacrosse_tx_free(void* context); - -/** - * Reset decoder WSProtocolDecoderLaCrosse_TX. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance - */ -void ws_protocol_decoder_lacrosse_tx_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderLaCrosse_TX. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderLaCrosse_TX. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance - * @param output Resulting text - */ -void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c deleted file mode 100644 index f2fddd40c..000000000 --- a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.c +++ /dev/null @@ -1,294 +0,0 @@ -#include "lacrosse_tx141thbv2.h" - -#define TAG "WSProtocolLaCrosse_TX141THBv2" - -#define LACROSSE_TX141TH_BV2_BIT_COUNT 41 - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse_tx141x.c - * - * iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | u - 41 bit - * or - * iiii iiii | bkcc tttt | tttt tttt | hhhh hhhh | cccc cccc | -40 bit - * - i: identification; changes on battery switch - * - c: lfsr_digest8_reflect; - * - u: unknown; - * - b: battery low; flag to indicate low battery voltage - * - h: Humidity; - * - t: Temperature; in °F as binary number with one decimal place + 50 °F offset - * - n: Channel; Channel number 1 - 3 - */ - -static const SubGhzBlockConst ws_protocol_lacrosse_tx141thbv2_const = { - .te_short = 208, - .te_long = 417, - .te_delta = 120, - .min_count_bit_for_found = 40, -}; - -struct WSProtocolDecoderLaCrosse_TX141THBv2 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - - uint16_t header_count; -}; - -struct WSProtocolEncoderLaCrosse_TX141THBv2 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - LaCrosse_TX141THBv2DecoderStepReset = 0, - LaCrosse_TX141THBv2DecoderStepCheckPreambule, - LaCrosse_TX141THBv2DecoderStepSaveDuration, - LaCrosse_TX141THBv2DecoderStepCheckDuration, -} LaCrosse_TX141THBv2DecoderStep; - -const SubGhzProtocolDecoder ws_protocol_lacrosse_tx141thbv2_decoder = { - .alloc = ws_protocol_decoder_lacrosse_tx141thbv2_alloc, - .free = ws_protocol_decoder_lacrosse_tx141thbv2_free, - - .feed = ws_protocol_decoder_lacrosse_tx141thbv2_feed, - .reset = ws_protocol_decoder_lacrosse_tx141thbv2_reset, - - .get_hash_data = ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data, - .serialize = ws_protocol_decoder_lacrosse_tx141thbv2_serialize, - .deserialize = ws_protocol_decoder_lacrosse_tx141thbv2_deserialize, - .get_string = ws_protocol_decoder_lacrosse_tx141thbv2_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_lacrosse_tx141thbv2_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_lacrosse_tx141thbv2 = { - .name = WS_PROTOCOL_LACROSSE_TX141THBV2_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_lacrosse_tx141thbv2_decoder, - .encoder = &ws_protocol_lacrosse_tx141thbv2_encoder, -}; - -void* ws_protocol_decoder_lacrosse_tx141thbv2_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderLaCrosse_TX141THBv2* instance = - malloc(sizeof(WSProtocolDecoderLaCrosse_TX141THBv2)); - instance->base.protocol = &ws_protocol_lacrosse_tx141thbv2; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_lacrosse_tx141thbv2_free(void* context) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - free(instance); -} - -void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; -} - -static bool - ws_protocol_lacrosse_tx141thbv2_check_crc(WSProtocolDecoderLaCrosse_TX141THBv2* instance) { - if(!instance->decoder.decode_data) return false; - uint64_t data = instance->decoder.decode_data; - if(instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) { - data >>= 1; - } - uint8_t msg[] = {data >> 32, data >> 24, data >> 16, data >> 8}; - - uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4); - return (crc == (data & 0xFF)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_lacrosse_tx141thbv2_remote_controller(WSBlockGeneric* instance) { - uint64_t data = instance->data; - if(instance->data_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT) { - data >>= 1; - } - instance->id = data >> 32; - instance->battery_low = (data >> 31) & 1; - instance->btn = (data >> 30) & 1; - instance->channel = ((data >> 28) & 0x03) + 1; - instance->temp = ((float)((data >> 16) & 0x0FFF) - 500.0f) / 10.0f; - instance->humidity = (data >> 8) & 0xFF; -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static bool ws_protocol_decoder_lacrosse_tx141thbv2_add_bit( - WSProtocolDecoderLaCrosse_TX141THBv2* instance, - uint32_t te_last, - uint32_t te_current) { - furi_assert(instance); - bool ret = false; - if(DURATION_DIFF( - te_last + te_current, - ws_protocol_lacrosse_tx141thbv2_const.te_short + - ws_protocol_lacrosse_tx141thbv2_const.te_long) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) { - if(te_last > te_current) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } else { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - } - ret = true; - } - - return ret; -} -void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - - switch(instance->decoder.parser_step) { - case LaCrosse_TX141THBv2DecoderStepReset: - if((level) && - (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - - case LaCrosse_TX141THBv2DecoderStepCheckPreambule: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF( - instance->decoder.te_last, - ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2)) { - //Found preambule - instance->header_count++; - } else if(instance->header_count == 4) { - if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit( - instance, instance->decoder.te_last, duration)) { - instance->decoder.decode_data = instance->decoder.decode_data & 1; - instance->decoder.decode_count_bit = 1; - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; - } - } else { - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; - } - } - break; - - case LaCrosse_TX141THBv2DecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckDuration; - } else { - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; - } - break; - - case LaCrosse_TX141THBv2DecoderStepCheckDuration: - if(!level) { - if(((DURATION_DIFF( - instance->decoder.te_last, - ws_protocol_lacrosse_tx141thbv2_const.te_short * 3) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_lacrosse_tx141thbv2_const.te_short * 4) < - ws_protocol_lacrosse_tx141thbv2_const.te_delta * 2))) { - if((instance->decoder.decode_count_bit == - ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found) || - (instance->decoder.decode_count_bit == LACROSSE_TX141TH_BV2_BIT_COUNT)) { - if(ws_protocol_lacrosse_tx141thbv2_check_crc(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_lacrosse_tx141thbv2_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 1; - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepCheckPreambule; - break; - } - } else if(ws_protocol_decoder_lacrosse_tx141thbv2_add_bit( - instance, instance->decoder.te_last, duration)) { - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; - } - } else { - instance->decoder.parser_step = LaCrosse_TX141THBv2DecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - ws_protocol_lacrosse_tx141thbv2_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderLaCrosse_TX141THBv2* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.h b/applications/external/weather_station/protocols/lacrosse_tx141thbv2.h deleted file mode 100644 index 036db0548..000000000 --- a/applications/external/weather_station/protocols/lacrosse_tx141thbv2.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_LACROSSE_TX141THBV2_NAME "TX141THBv2" - -typedef struct WSProtocolDecoderLaCrosse_TX141THBv2 WSProtocolDecoderLaCrosse_TX141THBv2; -typedef struct WSProtocolEncoderLaCrosse_TX141THBv2 WSProtocolEncoderLaCrosse_TX141THBv2; - -extern const SubGhzProtocolDecoder ws_protocol_lacrosse_tx141thbv2_decoder; -extern const SubGhzProtocolEncoder ws_protocol_lacrosse_tx141thbv2_encoder; -extern const SubGhzProtocol ws_protocol_lacrosse_tx141thbv2; - -/** - * Allocate WSProtocolDecoderLaCrosse_TX141THBv2. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderLaCrosse_TX141THBv2* pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance - */ -void* ws_protocol_decoder_lacrosse_tx141thbv2_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderLaCrosse_TX141THBv2. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance - */ -void ws_protocol_decoder_lacrosse_tx141thbv2_free(void* context); - -/** - * Reset decoder WSProtocolDecoderLaCrosse_TX141THBv2. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance - */ -void ws_protocol_decoder_lacrosse_tx141thbv2_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_lacrosse_tx141thbv2_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_lacrosse_tx141thbv2_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderLaCrosse_TX141THBv2. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderLaCrosse_TX141THBv2. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_lacrosse_tx141thbv2_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderLaCrosse_TX141THBv2 instance - * @param output Resulting text - */ -void ws_protocol_decoder_lacrosse_tx141thbv2_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/nexus_th.c b/applications/external/weather_station/protocols/nexus_th.c deleted file mode 100644 index 14ba8b273..000000000 --- a/applications/external/weather_station/protocols/nexus_th.c +++ /dev/null @@ -1,254 +0,0 @@ -#include "nexus_th.h" - -#define TAG "WSProtocolNexus_TH" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/nexus.c - * - * Nexus sensor protocol with ID, temperature and optional humidity - * also FreeTec (Pearl) NC-7345 sensors for FreeTec Weatherstation NC-7344, - * also infactory/FreeTec (Pearl) NX-3980 sensors for infactory/FreeTec NX-3974 station, - * also Solight TE82S sensors for Solight TE76/TE82/TE83/TE84 stations, - * also TFA 30.3209.02 temperature/humidity sensor. - * The sensor sends 36 bits 12 times, - * the packets are ppm modulated (distance coding) with a pulse of ~500 us - * followed by a short gap of ~1000 us for a 0 bit or a long ~2000 us gap for a - * 1 bit, the sync gap is ~4000 us. - * The data is grouped in 9 nibbles: - * [id0] [id1] [flags] [temp0] [temp1] [temp2] [const] [humi0] [humi1] - * - The 8-bit id changes when the battery is changed in the sensor. - * - flags are 4 bits B 0 C C, where B is the battery status: 1=OK, 0=LOW - * - and CC is the channel: 0=CH1, 1=CH2, 2=CH3 - * - temp is 12 bit signed scaled by 10 - * - const is always 1111 (0x0F) - * - humidity is 8 bits - * The sensors can be bought at Clas Ohlsen (Nexus) and Pearl (infactory/FreeTec). - * - */ - -#define NEXUS_TH_CONST_DATA 0b1111 - -static const SubGhzBlockConst ws_protocol_nexus_th_const = { - .te_short = 500, - .te_long = 2000, - .te_delta = 150, - .min_count_bit_for_found = 36, -}; - -struct WSProtocolDecoderNexus_TH { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; -}; - -struct WSProtocolEncoderNexus_TH { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - Nexus_THDecoderStepReset = 0, - Nexus_THDecoderStepSaveDuration, - Nexus_THDecoderStepCheckDuration, -} Nexus_THDecoderStep; - -const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder = { - .alloc = ws_protocol_decoder_nexus_th_alloc, - .free = ws_protocol_decoder_nexus_th_free, - - .feed = ws_protocol_decoder_nexus_th_feed, - .reset = ws_protocol_decoder_nexus_th_reset, - - .get_hash_data = ws_protocol_decoder_nexus_th_get_hash_data, - .serialize = ws_protocol_decoder_nexus_th_serialize, - .deserialize = ws_protocol_decoder_nexus_th_deserialize, - .get_string = ws_protocol_decoder_nexus_th_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_nexus_th = { - .name = WS_PROTOCOL_NEXUS_TH_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_nexus_th_decoder, - .encoder = &ws_protocol_nexus_th_encoder, -}; - -void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderNexus_TH* instance = malloc(sizeof(WSProtocolDecoderNexus_TH)); - instance->base.protocol = &ws_protocol_nexus_th; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_nexus_th_free(void* context) { - furi_assert(context); - WSProtocolDecoderNexus_TH* instance = context; - free(instance); -} - -void ws_protocol_decoder_nexus_th_reset(void* context) { - furi_assert(context); - WSProtocolDecoderNexus_TH* instance = context; - instance->decoder.parser_step = Nexus_THDecoderStepReset; -} - -static bool ws_protocol_nexus_th_check(WSProtocolDecoderNexus_TH* instance) { - uint8_t type = (instance->decoder.decode_data >> 8) & 0x0F; - - if((type == NEXUS_TH_CONST_DATA) && ((instance->decoder.decode_data >> 4) != 0xffffffff)) { - return true; - } else { - return false; - } - return true; -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_nexus_th_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 28) & 0xFF; - instance->battery_low = !((instance->data >> 27) & 1); - instance->channel = ((instance->data >> 24) & 0x03) + 1; - instance->btn = WS_NO_BTN; - if(!((instance->data >> 23) & 1)) { - instance->temp = (float)((instance->data >> 12) & 0x07FF) / 10.0f; - } else { - instance->temp = (float)((~(instance->data >> 12) & 0x07FF) + 1) / -10.0f; - } - - instance->humidity = instance->data & 0xFF; - if(instance->humidity > 95) - instance->humidity = 95; - else if(instance->humidity < 20) - instance->humidity = 20; -} - -void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderNexus_TH* instance = context; - - switch(instance->decoder.parser_step) { - case Nexus_THDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) < - ws_protocol_nexus_th_const.te_delta * 4)) { - //Found sync - instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } - break; - - case Nexus_THDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = Nexus_THDecoderStepReset; - } - break; - - case Nexus_THDecoderStepCheckDuration: - if(!level) { - if(DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 8) < - ws_protocol_nexus_th_const.te_delta * 4) { - //Found sync - instance->decoder.parser_step = Nexus_THDecoderStepReset; - if((instance->decoder.decode_count_bit == - ws_protocol_nexus_th_const.min_count_bit_for_found) && - ws_protocol_nexus_th_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_nexus_th_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - instance->decoder.parser_step = Nexus_THDecoderStepCheckDuration; - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - - break; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) < - ws_protocol_nexus_th_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 2) < - ws_protocol_nexus_th_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_nexus_th_const.te_short) < - ws_protocol_nexus_th_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_nexus_th_const.te_short * 4) < - ws_protocol_nexus_th_const.te_delta * 4)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Nexus_THDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Nexus_THDecoderStepReset; - } - } else { - instance->decoder.parser_step = Nexus_THDecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderNexus_TH* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_nexus_th_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderNexus_TH* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderNexus_TH* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_nexus_th_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderNexus_TH* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/nexus_th.h b/applications/external/weather_station/protocols/nexus_th.h deleted file mode 100644 index 6c2715b85..000000000 --- a/applications/external/weather_station/protocols/nexus_th.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_NEXUS_TH_NAME "Nexus-TH" - -typedef struct WSProtocolDecoderNexus_TH WSProtocolDecoderNexus_TH; -typedef struct WSProtocolEncoderNexus_TH WSProtocolEncoderNexus_TH; - -extern const SubGhzProtocolDecoder ws_protocol_nexus_th_decoder; -extern const SubGhzProtocolEncoder ws_protocol_nexus_th_encoder; -extern const SubGhzProtocol ws_protocol_nexus_th; - -/** - * Allocate WSProtocolDecoderNexus_TH. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderNexus_TH* pointer to a WSProtocolDecoderNexus_TH instance - */ -void* ws_protocol_decoder_nexus_th_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderNexus_TH. - * @param context Pointer to a WSProtocolDecoderNexus_TH instance - */ -void ws_protocol_decoder_nexus_th_free(void* context); - -/** - * Reset decoder WSProtocolDecoderNexus_TH. - * @param context Pointer to a WSProtocolDecoderNexus_TH instance - */ -void ws_protocol_decoder_nexus_th_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderNexus_TH instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_nexus_th_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderNexus_TH instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_nexus_th_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderNexus_TH. - * @param context Pointer to a WSProtocolDecoderNexus_TH instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_nexus_th_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderNexus_TH. - * @param context Pointer to a WSProtocolDecoderNexus_TH instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_nexus_th_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderNexus_TH instance - * @param output Resulting text - */ -void ws_protocol_decoder_nexus_th_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/oregon2.c b/applications/external/weather_station/protocols/oregon2.c deleted file mode 100644 index 313748ccf..000000000 --- a/applications/external/weather_station/protocols/oregon2.c +++ /dev/null @@ -1,429 +0,0 @@ -#include "oregon2.h" - -#include -#include -#include -#include -#include "ws_generic.h" - -#include -#include - -#define TAG "WSProtocolOregon2" - -static const SubGhzBlockConst ws_oregon2_const = { - .te_long = 1000, - .te_short = 500, - .te_delta = 200, - .min_count_bit_for_found = 32, -}; - -#define OREGON2_PREAMBLE_BITS 19 -#define OREGON2_PREAMBLE_MASK 0b1111111111111111111 -#define OREGON2_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) -#define OREGON2_CHECKSUM_BITS 8 - -// 15 ones + 0101 (inverted A) -#define OREGON2_PREAMBLE 0b1111111111111110101 - -// bit indicating the low battery -#define OREGON2_FLAG_BAT_LOW 0x4 - -/// Documentation for Oregon Scientific protocols can be found here: -/// http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf -// Sensors ID -#define ID_THGR122N 0x1d20 -#define ID_THGR968 0x1d30 -#define ID_BTHR918 0x5d50 -#define ID_BHTR968 0x5d60 -#define ID_RGR968 0x2d10 -#define ID_THR228N 0xec40 -#define ID_THN132N 0xec40 // same as THR228N but different packet size -#define ID_RTGN318 0x0cc3 // warning: id is from 0x0cc3 and 0xfcc3 -#define ID_RTGN129 0x0cc3 // same as RTGN318 but different packet size -#define ID_THGR810 0xf824 // This might be ID_THGR81, but what's true is lost in (git) history -#define ID_THGR810a 0xf8b4 // unconfirmed version -#define ID_THN802 0xc844 -#define ID_PCR800 0x2914 -#define ID_PCR800a 0x2d14 // Different PCR800 ID - AU version I think -#define ID_WGR800 0x1984 -#define ID_WGR800a 0x1994 // unconfirmed version -#define ID_WGR968 0x3d00 -#define ID_UV800 0xd874 -#define ID_THN129 0xcc43 // THN129 Temp only -#define ID_RTHN129 0x0cd3 // RTHN129 Temp, clock sensors -#define ID_RTHN129_1 0x9cd3 -#define ID_RTHN129_2 0xacd3 -#define ID_RTHN129_3 0xbcd3 -#define ID_RTHN129_4 0xccd3 -#define ID_RTHN129_5 0xdcd3 -#define ID_BTHGN129 0x5d53 // Baro, Temp, Hygro sensor -#define ID_UVR128 0xec70 -#define ID_THGR328N 0xcc23 // Temp & Hygro sensor similar to THR228N with 5 channel instead of 3 -#define ID_RTGR328N_1 0xdcc3 // RTGR328N_[1-5] RFclock(date &time)&Temp&Hygro sensor -#define ID_RTGR328N_2 0xccc3 -#define ID_RTGR328N_3 0xbcc3 -#define ID_RTGR328N_4 0xacc3 -#define ID_RTGR328N_5 0x9cc3 -#define ID_RTGR328N_6 0x8ce3 // RTGR328N_6&7 RFclock(date &time)&Temp&Hygro sensor like THGR328N -#define ID_RTGR328N_7 0x8ae3 - -struct WSProtocolDecoderOregon2 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - ManchesterState manchester_state; - bool prev_bit; - bool have_bit; - - uint8_t var_bits; - uint32_t var_data; -}; - -typedef struct WSProtocolDecoderOregon2 WSProtocolDecoderOregon2; - -typedef enum { - Oregon2DecoderStepReset = 0, - Oregon2DecoderStepFoundPreamble, - Oregon2DecoderStepVarData, -} Oregon2DecoderStep; - -void* ws_protocol_decoder_oregon2_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderOregon2* instance = malloc(sizeof(WSProtocolDecoderOregon2)); - instance->base.protocol = &ws_protocol_oregon2; - instance->generic.protocol_name = instance->base.protocol->name; - instance->generic.humidity = WS_NO_HUMIDITY; - instance->generic.temp = WS_NO_TEMPERATURE; - instance->generic.btn = WS_NO_BTN; - instance->generic.channel = WS_NO_CHANNEL; - instance->generic.battery_low = WS_NO_BATT; - instance->generic.id = WS_NO_ID; - return instance; -} - -void ws_protocol_decoder_oregon2_free(void* context) { - furi_assert(context); - WSProtocolDecoderOregon2* instance = context; - free(instance); -} - -void ws_protocol_decoder_oregon2_reset(void* context) { - furi_assert(context); - WSProtocolDecoderOregon2* instance = context; - instance->decoder.parser_step = Oregon2DecoderStepReset; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); - instance->have_bit = false; - instance->var_data = 0; - instance->var_bits = 0; -} - -static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) { - bool is_long = false; - - if(DURATION_DIFF(duration, ws_oregon2_const.te_long) < ws_oregon2_const.te_delta) { - is_long = true; - } else if(DURATION_DIFF(duration, ws_oregon2_const.te_short) < ws_oregon2_const.te_delta) { - is_long = false; - } else { - return ManchesterEventReset; - } - - if(level) - return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh; - else - return is_long ? ManchesterEventLongLow : ManchesterEventShortLow; -} - -// From sensor id code return amount of bits in variable section -// https://temofeev.ru/info/articles/o-dekodirovanii-protokola-pogodnykh-datchikov-oregon-scientific -static uint8_t oregon2_sensor_id_var_bits(uint16_t sensor_id) { - switch(sensor_id) { - case ID_THR228N: - case ID_RTHN129_1: - case ID_RTHN129_2: - case ID_RTHN129_3: - case ID_RTHN129_4: - case ID_RTHN129_5: - return 16; - case ID_THGR122N: - return 24; - default: - return 0; - } -} - -static void ws_oregon2_decode_const_data(WSBlockGeneric* ws_block) { - ws_block->id = OREGON2_SENSOR_ID(ws_block->data); - - uint8_t ch_bits = (ws_block->data >> 12) & 0xF; - ws_block->channel = 1; - while(ch_bits > 1) { - ws_block->channel++; - ch_bits >>= 1; - } - - ws_block->battery_low = (ws_block->data & OREGON2_FLAG_BAT_LOW) ? 1 : 0; -} - -uint16_t bcd_decode_short(uint32_t data) { - return (data & 0xF) * 10 + ((data >> 4) & 0xF); -} - -static float ws_oregon2_decode_temp(uint32_t data) { - int32_t temp_val; - temp_val = bcd_decode_short(data >> 4); - temp_val *= 10; - temp_val += (data >> 12) & 0xF; - if(data & 0xF) temp_val = -temp_val; - return (float)temp_val / 10.0; -} - -static void ws_oregon2_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { - switch(sensor_id) { - case ID_THR228N: - case ID_RTHN129_1: - case ID_RTHN129_2: - case ID_RTHN129_3: - case ID_RTHN129_4: - case ID_RTHN129_5: - ws_b->temp = ws_oregon2_decode_temp(data); - ws_b->humidity = WS_NO_HUMIDITY; - return; - case ID_THGR122N: - ws_b->humidity = bcd_decode_short(data); - ws_b->temp = ws_oregon2_decode_temp(data >> 8); - return; - default: - break; - } -} - -void ws_protocol_decoder_oregon2_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderOregon2* instance = context; - // oregon v2.1 signal is inverted - ManchesterEvent event = level_and_duration_to_event(!level, duration); - bool data; - - // low-level bit sequence decoding - if(event == ManchesterEventReset) { - instance->decoder.parser_step = Oregon2DecoderStepReset; - instance->have_bit = false; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - } - if(manchester_advance(instance->manchester_state, event, &instance->manchester_state, &data)) { - if(instance->have_bit) { - if(!instance->prev_bit && data) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } else if(instance->prev_bit && !data) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - } else { - ws_protocol_decoder_oregon2_reset(context); - } - instance->have_bit = false; - } else { - instance->prev_bit = data; - instance->have_bit = true; - } - } - - switch(instance->decoder.parser_step) { - case Oregon2DecoderStepReset: - // waiting for fixed oregon2 preamble - if(instance->decoder.decode_count_bit >= OREGON2_PREAMBLE_BITS && - ((instance->decoder.decode_data & OREGON2_PREAMBLE_MASK) == OREGON2_PREAMBLE)) { - instance->decoder.parser_step = Oregon2DecoderStepFoundPreamble; - instance->decoder.decode_count_bit = 0; - instance->decoder.decode_data = 0UL; - } - break; - case Oregon2DecoderStepFoundPreamble: - // waiting for fixed oregon2 data - if(instance->decoder.decode_count_bit == 32) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - - // reverse nibbles in decoded data - instance->generic.data = (instance->generic.data & 0x55555555) << 1 | - (instance->generic.data & 0xAAAAAAAA) >> 1; - instance->generic.data = (instance->generic.data & 0x33333333) << 2 | - (instance->generic.data & 0xCCCCCCCC) >> 2; - - ws_oregon2_decode_const_data(&instance->generic); - instance->var_bits = - oregon2_sensor_id_var_bits(OREGON2_SENSOR_ID(instance->generic.data)); - - if(!instance->var_bits) { - // sensor is not supported, stop decoding, but showing the decoded fixed part - instance->decoder.parser_step = Oregon2DecoderStepReset; - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } else { - instance->decoder.parser_step = Oregon2DecoderStepVarData; - } - } - break; - case Oregon2DecoderStepVarData: - // waiting for variable (sensor-specific data) - if(instance->decoder.decode_count_bit == instance->var_bits + OREGON2_CHECKSUM_BITS) { - instance->var_data = instance->decoder.decode_data & 0xFFFFFFFF; - - // reverse nibbles in var data - instance->var_data = (instance->var_data & 0x55555555) << 1 | - (instance->var_data & 0xAAAAAAAA) >> 1; - instance->var_data = (instance->var_data & 0x33333333) << 2 | - (instance->var_data & 0xCCCCCCCC) >> 2; - - ws_oregon2_decode_var_data( - &instance->generic, - OREGON2_SENSOR_ID(instance->generic.data), - instance->var_data >> OREGON2_CHECKSUM_BITS); - - instance->decoder.parser_step = Oregon2DecoderStepReset; - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - break; - } -} - -uint8_t ws_protocol_decoder_oregon2_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderOregon2* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_oregon2_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderOregon2* instance = context; - SubGhzProtocolStatus ret = SubGhzProtocolStatusError; - ret = ws_block_generic_serialize(&instance->generic, flipper_format, preset); - if(ret != SubGhzProtocolStatusOk) return ret; - uint32_t temp = instance->var_bits; - if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { - FURI_LOG_E(TAG, "Error adding VarBits"); - return SubGhzProtocolStatusErrorParserOthers; - } - if(!flipper_format_write_hex( - flipper_format, - "VarData", - (const uint8_t*)&instance->var_data, - sizeof(instance->var_data))) { - FURI_LOG_E(TAG, "Error adding VarData"); - return SubGhzProtocolStatusErrorParserOthers; - } - return ret; -} - -SubGhzProtocolStatus - ws_protocol_decoder_oregon2_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderOregon2* instance = context; - uint32_t temp_data; - SubGhzProtocolStatus ret = SubGhzProtocolStatusError; - do { - ret = ws_block_generic_deserialize(&instance->generic, flipper_format); - if(ret != SubGhzProtocolStatusOk) { - break; - } - if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { - FURI_LOG_E(TAG, "Missing VarLen"); - ret = SubGhzProtocolStatusErrorParserOthers; - break; - } - instance->var_bits = (uint8_t)temp_data; - if(!flipper_format_read_hex( - flipper_format, - "VarData", - (uint8_t*)&instance->var_data, - sizeof(instance->var_data))) { //-V1051 - FURI_LOG_E(TAG, "Missing VarData"); - ret = SubGhzProtocolStatusErrorParserOthers; - break; - } - if(instance->generic.data_count_bit != ws_oregon2_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); - ret = SubGhzProtocolStatusErrorValueBitCount; - break; - } - } while(false); - return ret; -} - -static void oregon2_append_check_sum(uint32_t fix_data, uint32_t var_data, FuriString* output) { - uint8_t sum = fix_data & 0xF; - uint8_t ref_sum = var_data & 0xFF; - var_data >>= 8; - - for(uint8_t i = 1; i < 8; i++) { - fix_data >>= 4; - var_data >>= 4; - sum += (fix_data & 0xF) + (var_data & 0xF); - } - - // swap calculated sum nibbles - sum = (((sum >> 4) & 0xF) | (sum << 4)) & 0xFF; - if(sum == ref_sum) - furi_string_cat_printf(output, "Sum ok: 0x%hhX", ref_sum); - else - furi_string_cat_printf(output, "Sum err: 0x%hhX vs 0x%hhX", ref_sum, sum); -} - -void ws_protocol_decoder_oregon2_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderOregon2* instance = context; - furi_string_cat_printf( - output, - "%s\r\n" - "ID: 0x%04lX, ch: %d, bat: %d, rc: 0x%02lX\r\n", - instance->generic.protocol_name, - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (uint32_t)(instance->generic.data >> 4) & 0xFF); - - if(instance->var_bits > 0) { - furi_string_cat_printf( - output, - "Temp:%d.%d C Hum:%d%%", - (int16_t)instance->generic.temp, - abs( - ((int16_t)(instance->generic.temp * 10) - - (((int16_t)instance->generic.temp) * 10))), - instance->generic.humidity); - oregon2_append_check_sum((uint32_t)instance->generic.data, instance->var_data, output); - } -} - -const SubGhzProtocolDecoder ws_protocol_oregon2_decoder = { - .alloc = ws_protocol_decoder_oregon2_alloc, - .free = ws_protocol_decoder_oregon2_free, - - .feed = ws_protocol_decoder_oregon2_feed, - .reset = ws_protocol_decoder_oregon2_reset, - - .get_hash_data = ws_protocol_decoder_oregon2_get_hash_data, - .serialize = ws_protocol_decoder_oregon2_serialize, - .deserialize = ws_protocol_decoder_oregon2_deserialize, - .get_string = ws_protocol_decoder_oregon2_get_string, -}; - -const SubGhzProtocol ws_protocol_oregon2 = { - .name = WS_PROTOCOL_OREGON2_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_oregon2_decoder, -}; diff --git a/applications/external/weather_station/protocols/oregon2.h b/applications/external/weather_station/protocols/oregon2.h deleted file mode 100644 index cfe938e6d..000000000 --- a/applications/external/weather_station/protocols/oregon2.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -#define WS_PROTOCOL_OREGON2_NAME "Oregon2" -extern const SubGhzProtocol ws_protocol_oregon2; diff --git a/applications/external/weather_station/protocols/oregon3.c b/applications/external/weather_station/protocols/oregon3.c deleted file mode 100644 index bd35c2fd5..000000000 --- a/applications/external/weather_station/protocols/oregon3.c +++ /dev/null @@ -1,365 +0,0 @@ -#include "oregon3.h" - -#include -#include -#include -#include -#include "ws_generic.h" - -#include -#include - -#define TAG "WSProtocolOregon3" - -static const SubGhzBlockConst ws_oregon3_const = { - .te_long = 1100, - .te_short = 500, - .te_delta = 300, - .min_count_bit_for_found = 32, -}; - -#define OREGON3_PREAMBLE_BITS 28 -#define OREGON3_PREAMBLE_MASK 0b1111111111111111111111111111 -// 24 ones + 0101 (inverted A) -#define OREGON3_PREAMBLE 0b1111111111111111111111110101 - -// Fixed part contains: -// - Sensor type: 16 bits -// - Channel: 4 bits -// - ID (changes when batteries are changed): 8 bits -// - Battery status: 4 bits -#define OREGON3_FIXED_PART_BITS (16 + 4 + 8 + 4) -#define OREGON3_SENSOR_ID(d) (((d) >> 16) & 0xFFFF) -#define OREGON3_CHECKSUM_BITS 8 - -// bit indicating the low battery -#define OREGON3_FLAG_BAT_LOW 0x4 - -/// Documentation for Oregon Scientific protocols can be found here: -/// https://www.osengr.org/Articles/OS-RF-Protocols-IV.pdf -// Sensors ID -#define ID_THGR221 0xf824 - -struct WSProtocolDecoderOregon3 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - ManchesterState manchester_state; - bool prev_bit; - - uint8_t var_bits; - uint64_t var_data; -}; - -typedef struct WSProtocolDecoderOregon3 WSProtocolDecoderOregon3; - -typedef enum { - Oregon3DecoderStepReset = 0, - Oregon3DecoderStepFoundPreamble, - Oregon3DecoderStepVarData, -} Oregon3DecoderStep; - -void* ws_protocol_decoder_oregon3_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderOregon3* instance = malloc(sizeof(WSProtocolDecoderOregon3)); - instance->base.protocol = &ws_protocol_oregon3; - instance->generic.protocol_name = instance->base.protocol->name; - instance->generic.humidity = WS_NO_HUMIDITY; - instance->generic.temp = WS_NO_TEMPERATURE; - instance->generic.btn = WS_NO_BTN; - instance->generic.channel = WS_NO_CHANNEL; - instance->generic.battery_low = WS_NO_BATT; - instance->generic.id = WS_NO_ID; - instance->prev_bit = false; - return instance; -} - -void ws_protocol_decoder_oregon3_free(void* context) { - furi_assert(context); - WSProtocolDecoderOregon3* instance = context; - free(instance); -} - -void ws_protocol_decoder_oregon3_reset(void* context) { - furi_assert(context); - WSProtocolDecoderOregon3* instance = context; - instance->decoder.parser_step = Oregon3DecoderStepReset; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL); - instance->prev_bit = false; - instance->var_data = 0; - instance->var_bits = 0; -} - -static ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) { - bool is_long = false; - - if(DURATION_DIFF(duration, ws_oregon3_const.te_long) < ws_oregon3_const.te_delta) { - is_long = true; - } else if(DURATION_DIFF(duration, ws_oregon3_const.te_short) < ws_oregon3_const.te_delta) { - is_long = false; - } else { - return ManchesterEventReset; - } - - if(level) - return is_long ? ManchesterEventLongHigh : ManchesterEventShortHigh; - else - return is_long ? ManchesterEventLongLow : ManchesterEventShortLow; -} - -// From sensor id code return amount of bits in variable section -// https://temofeev.ru/info/articles/o-dekodirovanii-protokola-pogodnykh-datchikov-oregon-scientific -static uint8_t oregon3_sensor_id_var_bits(uint16_t sensor_id) { - switch(sensor_id) { - case ID_THGR221: - // nibbles: temp + hum + '0' - return (4 + 2 + 1) * 4; - default: - FURI_LOG_D(TAG, "Unsupported sensor id 0x%x", sensor_id); - return 0; - } -} - -static void ws_oregon3_decode_const_data(WSBlockGeneric* ws_block) { - ws_block->id = OREGON3_SENSOR_ID(ws_block->data); - ws_block->channel = (ws_block->data >> 12) & 0xF; - ws_block->battery_low = (ws_block->data & OREGON3_FLAG_BAT_LOW) ? 1 : 0; -} - -static uint16_t ws_oregon3_bcd_decode_short(uint32_t data) { - return (data & 0xF) * 10 + ((data >> 4) & 0xF); -} - -static float ws_oregon3_decode_temp(uint32_t data) { - int32_t temp_val; - temp_val = ws_oregon3_bcd_decode_short(data >> 4); - temp_val *= 10; - temp_val += (data >> 12) & 0xF; - if(data & 0xF) temp_val = -temp_val; - return (float)temp_val / 10.0; -} - -static void ws_oregon3_decode_var_data(WSBlockGeneric* ws_b, uint16_t sensor_id, uint32_t data) { - switch(sensor_id) { - case ID_THGR221: - default: - ws_b->humidity = ws_oregon3_bcd_decode_short(data >> 4); - ws_b->temp = ws_oregon3_decode_temp(data >> 12); - break; - } -} - -void ws_protocol_decoder_oregon3_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderOregon3* instance = context; - // Oregon v3.0 protocol is inverted - ManchesterEvent event = level_and_duration_to_event(!level, duration); - - // low-level bit sequence decoding - if(event == ManchesterEventReset) { - instance->decoder.parser_step = Oregon3DecoderStepReset; - instance->prev_bit = false; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - } - if(manchester_advance( - instance->manchester_state, event, &instance->manchester_state, &instance->prev_bit)) { - subghz_protocol_blocks_add_bit(&instance->decoder, instance->prev_bit); - } - - switch(instance->decoder.parser_step) { - case Oregon3DecoderStepReset: - // waiting for fixed oregon3 preamble - if(instance->decoder.decode_count_bit >= OREGON3_PREAMBLE_BITS && - ((instance->decoder.decode_data & OREGON3_PREAMBLE_MASK) == OREGON3_PREAMBLE)) { - instance->decoder.parser_step = Oregon3DecoderStepFoundPreamble; - instance->decoder.decode_count_bit = 0; - instance->decoder.decode_data = 0UL; - } - break; - case Oregon3DecoderStepFoundPreamble: - // waiting for fixed oregon3 data - if(instance->decoder.decode_count_bit == OREGON3_FIXED_PART_BITS) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - instance->decoder.decode_data = 0UL; - instance->decoder.decode_count_bit = 0; - - // reverse nibbles in decoded data as oregon v3.0 is LSB first - instance->generic.data = (instance->generic.data & 0x55555555) << 1 | - (instance->generic.data & 0xAAAAAAAA) >> 1; - instance->generic.data = (instance->generic.data & 0x33333333) << 2 | - (instance->generic.data & 0xCCCCCCCC) >> 2; - - ws_oregon3_decode_const_data(&instance->generic); - instance->var_bits = - oregon3_sensor_id_var_bits(OREGON3_SENSOR_ID(instance->generic.data)); - - if(!instance->var_bits) { - // sensor is not supported, stop decoding - instance->decoder.parser_step = Oregon3DecoderStepReset; - } else { - instance->decoder.parser_step = Oregon3DecoderStepVarData; - } - } - break; - case Oregon3DecoderStepVarData: - // waiting for variable (sensor-specific data) - if(instance->decoder.decode_count_bit == instance->var_bits + OREGON3_CHECKSUM_BITS) { - instance->var_data = instance->decoder.decode_data & 0xFFFFFFFFFFFFFFFF; - - // reverse nibbles in var data - instance->var_data = (instance->var_data & 0x5555555555555555) << 1 | - (instance->var_data & 0xAAAAAAAAAAAAAAAA) >> 1; - instance->var_data = (instance->var_data & 0x3333333333333333) << 2 | - (instance->var_data & 0xCCCCCCCCCCCCCCCC) >> 2; - - ws_oregon3_decode_var_data( - &instance->generic, - OREGON3_SENSOR_ID(instance->generic.data), - instance->var_data >> OREGON3_CHECKSUM_BITS); - - instance->decoder.parser_step = Oregon3DecoderStepReset; - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - break; - } -} - -uint8_t ws_protocol_decoder_oregon3_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderOregon3* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_oregon3_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderOregon3* instance = context; - SubGhzProtocolStatus ret = SubGhzProtocolStatusError; - ret = ws_block_generic_serialize(&instance->generic, flipper_format, preset); - if(ret != SubGhzProtocolStatusOk) return ret; - uint32_t temp = instance->var_bits; - if(!flipper_format_write_uint32(flipper_format, "VarBits", &temp, 1)) { - FURI_LOG_E(TAG, "Error adding VarBits"); - return SubGhzProtocolStatusErrorParserOthers; - } - if(!flipper_format_write_hex( - flipper_format, - "VarData", - (const uint8_t*)&instance->var_data, - sizeof(instance->var_data))) { - FURI_LOG_E(TAG, "Error adding VarData"); - return SubGhzProtocolStatusErrorParserOthers; - } - return ret; -} - -SubGhzProtocolStatus - ws_protocol_decoder_oregon3_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderOregon3* instance = context; - uint32_t temp_data; - SubGhzProtocolStatus ret = SubGhzProtocolStatusError; - do { - ret = ws_block_generic_deserialize(&instance->generic, flipper_format); - if(ret != SubGhzProtocolStatusOk) { - break; - } - if(!flipper_format_read_uint32(flipper_format, "VarBits", &temp_data, 1)) { - FURI_LOG_E(TAG, "Missing VarLen"); - ret = SubGhzProtocolStatusErrorParserOthers; - break; - } - instance->var_bits = (uint8_t)temp_data; - if(!flipper_format_read_hex( - flipper_format, - "VarData", - (uint8_t*)&instance->var_data, - sizeof(instance->var_data))) { //-V1051 - FURI_LOG_E(TAG, "Missing VarData"); - ret = SubGhzProtocolStatusErrorParserOthers; - break; - } - if(instance->generic.data_count_bit != ws_oregon3_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key: %d", instance->generic.data_count_bit); - ret = SubGhzProtocolStatusErrorValueBitCount; - break; - } - } while(false); - return ret; -} - -static void oregon3_append_check_sum(uint32_t fix_data, uint64_t var_data, FuriString* output) { - uint8_t sum = fix_data & 0xF; - uint8_t ref_sum = var_data & 0xFF; - var_data >>= 4; - - for(uint8_t i = 1; i < 8; i++) { - fix_data >>= 4; - var_data >>= 4; - sum += (fix_data & 0xF) + (var_data & 0xF); - } - - // swap calculated sum nibbles - sum = (((sum >> 4) & 0xF) | (sum << 4)) & 0xFF; - if(sum == ref_sum) - furi_string_cat_printf(output, "Sum ok: 0x%hhX", ref_sum); - else - furi_string_cat_printf(output, "Sum err: 0x%hhX vs 0x%hhX", ref_sum, sum); -} - -void ws_protocol_decoder_oregon3_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderOregon3* instance = context; - furi_string_cat_printf( - output, - "%s\r\n" - "ID: 0x%04lX, ch: %d, bat: %d, rc: 0x%02lX\r\n", - instance->generic.protocol_name, - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (uint32_t)(instance->generic.data >> 4) & 0xFF); - - if(instance->var_bits > 0) { - furi_string_cat_printf( - output, - "Temp:%d.%d C Hum:%d%%", - (int16_t)instance->generic.temp, - abs( - ((int16_t)(instance->generic.temp * 10) - - (((int16_t)instance->generic.temp) * 10))), - instance->generic.humidity); - oregon3_append_check_sum((uint32_t)instance->generic.data, instance->var_data, output); - } -} - -const SubGhzProtocolDecoder ws_protocol_oregon3_decoder = { - .alloc = ws_protocol_decoder_oregon3_alloc, - .free = ws_protocol_decoder_oregon3_free, - - .feed = ws_protocol_decoder_oregon3_feed, - .reset = ws_protocol_decoder_oregon3_reset, - - .get_hash_data = ws_protocol_decoder_oregon3_get_hash_data, - .serialize = ws_protocol_decoder_oregon3_serialize, - .deserialize = ws_protocol_decoder_oregon3_deserialize, - .get_string = ws_protocol_decoder_oregon3_get_string, -}; - -const SubGhzProtocol ws_protocol_oregon3 = { - .name = WS_PROTOCOL_OREGON3_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_oregon3_decoder, -}; diff --git a/applications/external/weather_station/protocols/oregon3.h b/applications/external/weather_station/protocols/oregon3.h deleted file mode 100644 index ec51ddb00..000000000 --- a/applications/external/weather_station/protocols/oregon3.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include - -#define WS_PROTOCOL_OREGON3_NAME "Oregon3" -extern const SubGhzProtocol ws_protocol_oregon3; diff --git a/applications/external/weather_station/protocols/oregon_v1.c b/applications/external/weather_station/protocols/oregon_v1.c deleted file mode 100644 index 03215bbf5..000000000 --- a/applications/external/weather_station/protocols/oregon_v1.c +++ /dev/null @@ -1,321 +0,0 @@ -#include "oregon_v1.h" -#include - -#define TAG "WSProtocolOregon_V1" - -/* - * Help - * https://github.dev/merbanan/rtl_433/blob/bb1be7f186ac0fdb7dc5d77693847d96fb95281e/src/devices/oregon_scientific_v1.c - * - * OSv1 protocol. - * - * MC with nominal bit width of 2930 us. - * Pulses are somewhat longer than nominal half-bit width, 1748 us / 3216 us, - * Gaps are somewhat shorter than nominal half-bit width, 1176 us / 2640 us. - * After 12 preamble bits there is 4200 us gap, 5780 us pulse, 5200 us gap. - * And next 32 bit data - * - * Care must be taken with the gap after the sync pulse since it - * is outside of the normal clocking. Because of this a data stream - * beginning with a 0 will have data in this gap. - * - * - * Data is in reverse order of bits - * RevBit(data32bit)=> tib23atad - * - * tib23atad => xxxxxxxx | busuTTTT | ttttzzzz | ccuuiiii - * - * - i: ID - * - x: CRC; - * - u: unknown; - * - b: battery low; flag to indicate low battery voltage - * - s: temperature sign - * - T: BCD, Temperature; in C * 10 - * - t: BCD, Temperature; in C * 1 - * - z: BCD, Temperature; in C * 0.1 - * - c: Channel 00=CH1, 01=CH2, 10=CH3 - * - */ - -#define OREGON_V1_HEADER_OK 0xFF - -static const SubGhzBlockConst ws_protocol_oregon_v1_const = { - .te_short = 1465, - .te_long = 2930, - .te_delta = 350, - .min_count_bit_for_found = 32, -}; - -struct WSProtocolDecoderOregon_V1 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - ManchesterState manchester_state; - uint16_t header_count; - uint8_t first_bit; -}; - -struct WSProtocolEncoderOregon_V1 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - Oregon_V1DecoderStepReset = 0, - Oregon_V1DecoderStepFoundPreamble, - Oregon_V1DecoderStepParse, -} Oregon_V1DecoderStep; - -const SubGhzProtocolDecoder ws_protocol_oregon_v1_decoder = { - .alloc = ws_protocol_decoder_oregon_v1_alloc, - .free = ws_protocol_decoder_oregon_v1_free, - - .feed = ws_protocol_decoder_oregon_v1_feed, - .reset = ws_protocol_decoder_oregon_v1_reset, - - .get_hash_data = ws_protocol_decoder_oregon_v1_get_hash_data, - .serialize = ws_protocol_decoder_oregon_v1_serialize, - .deserialize = ws_protocol_decoder_oregon_v1_deserialize, - .get_string = ws_protocol_decoder_oregon_v1_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_oregon_v1_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_oregon_v1 = { - .name = WS_PROTOCOL_OREGON_V1_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_oregon_v1_decoder, - .encoder = &ws_protocol_oregon_v1_encoder, -}; - -void* ws_protocol_decoder_oregon_v1_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderOregon_V1* instance = malloc(sizeof(WSProtocolDecoderOregon_V1)); - instance->base.protocol = &ws_protocol_oregon_v1; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_oregon_v1_free(void* context) { - furi_assert(context); - WSProtocolDecoderOregon_V1* instance = context; - free(instance); -} - -void ws_protocol_decoder_oregon_v1_reset(void* context) { - furi_assert(context); - WSProtocolDecoderOregon_V1* instance = context; - instance->decoder.parser_step = Oregon_V1DecoderStepReset; -} - -static bool ws_protocol_oregon_v1_check(WSProtocolDecoderOregon_V1* instance) { - if(!instance->decoder.decode_data) return false; - uint64_t data = subghz_protocol_blocks_reverse_key(instance->decoder.decode_data, 32); - uint16_t crc = (data & 0xff) + ((data >> 8) & 0xff) + ((data >> 16) & 0xff); - crc = (crc & 0xff) + ((crc >> 8) & 0xff); - return (crc == ((data >> 24) & 0xFF)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_oregon_v1_remote_controller(WSBlockGeneric* instance) { - uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 32); - - instance->id = data & 0xFF; - instance->channel = ((data >> 6) & 0x03) + 1; - - float temp_raw = - ((data >> 8) & 0x0F) * 0.1f + ((data >> 12) & 0x0F) + ((data >> 16) & 0x0F) * 10.0f; - if(!((data >> 21) & 1)) { - instance->temp = temp_raw; - } else { - instance->temp = -temp_raw; - } - - instance->battery_low = !((instance->data >> 23) & 1ULL); - - instance->btn = WS_NO_BTN; - instance->humidity = WS_NO_HUMIDITY; -} - -void ws_protocol_decoder_oregon_v1_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderOregon_V1* instance = context; - ManchesterEvent event = ManchesterEventReset; - switch(instance->decoder.parser_step) { - case Oregon_V1DecoderStepReset: - if((level) && (DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < - ws_protocol_oregon_v1_const.te_delta)) { - instance->decoder.parser_step = Oregon_V1DecoderStepFoundPreamble; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - case Oregon_V1DecoderStepFoundPreamble: - if(level) { - //keep high levels, if they suit our durations - if((DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < - ws_protocol_oregon_v1_const.te_delta) || - (DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short * 4) < - ws_protocol_oregon_v1_const.te_delta)) { - instance->decoder.te_last = duration; - } else { - instance->decoder.parser_step = Oregon_V1DecoderStepReset; - } - } else if( - //checking low levels - (DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < - ws_protocol_oregon_v1_const.te_delta) && - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_oregon_v1_const.te_short) < - ws_protocol_oregon_v1_const.te_delta)) { - // Found header - instance->header_count++; - } else if( - (DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short * 3) < - ws_protocol_oregon_v1_const.te_delta) && - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_oregon_v1_const.te_short) < - ws_protocol_oregon_v1_const.te_delta)) { - // check header - if(instance->header_count > 7) { - instance->header_count = OREGON_V1_HEADER_OK; - } - } else if( - (instance->header_count == OREGON_V1_HEADER_OK) && - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_oregon_v1_const.te_short * 4) < - ws_protocol_oregon_v1_const.te_delta)) { - //found all the necessary patterns - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 1; - manchester_advance( - instance->manchester_state, - ManchesterEventReset, - &instance->manchester_state, - NULL); - instance->decoder.parser_step = Oregon_V1DecoderStepParse; - if(duration < ws_protocol_oregon_v1_const.te_short * 4) { - instance->first_bit = 1; - } else { - instance->first_bit = 0; - } - } else { - instance->decoder.parser_step = Oregon_V1DecoderStepReset; - } - break; - case Oregon_V1DecoderStepParse: - if(level) { - if(DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < - ws_protocol_oregon_v1_const.te_delta) { - event = ManchesterEventShortHigh; - } else if( - DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_long) < - ws_protocol_oregon_v1_const.te_delta) { - event = ManchesterEventLongHigh; - } else { - instance->decoder.parser_step = Oregon_V1DecoderStepReset; - } - } else { - if(DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_short) < - ws_protocol_oregon_v1_const.te_delta) { - event = ManchesterEventShortLow; - } else if( - DURATION_DIFF(duration, ws_protocol_oregon_v1_const.te_long) < - ws_protocol_oregon_v1_const.te_delta) { - event = ManchesterEventLongLow; - } else if(duration >= ((uint32_t)ws_protocol_oregon_v1_const.te_long * 2)) { - if(instance->decoder.decode_count_bit == - ws_protocol_oregon_v1_const.min_count_bit_for_found) { - if(instance->first_bit) { - instance->decoder.decode_data = ~instance->decoder.decode_data | (1 << 31); - } - if(ws_protocol_oregon_v1_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_oregon_v1_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_state, - ManchesterEventReset, - &instance->manchester_state, - NULL); - } else { - instance->decoder.parser_step = Oregon_V1DecoderStepReset; - } - } - if(event != ManchesterEventReset) { - bool data; - bool data_ok = manchester_advance( - instance->manchester_state, event, &instance->manchester_state, &data); - - if(data_ok) { - instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; - instance->decoder.decode_count_bit++; - } - } - - break; - } -} - -uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderOregon_V1* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_oregon_v1_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderOregon_V1* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderOregon_V1* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_oregon_v1_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_oregon_v1_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderOregon_V1* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/oregon_v1.h b/applications/external/weather_station/protocols/oregon_v1.h deleted file mode 100644 index 48937601d..000000000 --- a/applications/external/weather_station/protocols/oregon_v1.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_OREGON_V1_NAME "Oregon-v1" - -typedef struct WSProtocolDecoderOregon_V1 WSProtocolDecoderOregon_V1; -typedef struct WSProtocolEncoderOregon_V1 WSProtocolEncoderOregon_V1; - -extern const SubGhzProtocolDecoder ws_protocol_oregon_v1_decoder; -extern const SubGhzProtocolEncoder ws_protocol_oregon_v1_encoder; -extern const SubGhzProtocol ws_protocol_oregon_v1; - -/** - * Allocate WSProtocolDecoderOregon_V1. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderOregon_V1* pointer to a WSProtocolDecoderOregon_V1 instance - */ -void* ws_protocol_decoder_oregon_v1_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderOregon_V1. - * @param context Pointer to a WSProtocolDecoderOregon_V1 instance - */ -void ws_protocol_decoder_oregon_v1_free(void* context); - -/** - * Reset decoder WSProtocolDecoderOregon_V1. - * @param context Pointer to a WSProtocolDecoderOregon_V1 instance - */ -void ws_protocol_decoder_oregon_v1_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderOregon_V1 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_oregon_v1_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderOregon_V1 instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_oregon_v1_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderOregon_V1. - * @param context Pointer to a WSProtocolDecoderOregon_V1 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_oregon_v1_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderOregon_V1. - * @param context Pointer to a WSProtocolDecoderOregon_V1 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_oregon_v1_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderOregon_V1 instance - * @param output Resulting text - */ -void ws_protocol_decoder_oregon_v1_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/protocol_items.c b/applications/external/weather_station/protocols/protocol_items.c deleted file mode 100644 index 93dc25488..000000000 --- a/applications/external/weather_station/protocols/protocol_items.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "protocol_items.h" - -const SubGhzProtocol* weather_station_protocol_registry_items[] = { - &ws_protocol_infactory, - &ws_protocol_thermopro_tx4, - &ws_protocol_nexus_th, - &ws_protocol_gt_wt_02, - &ws_protocol_gt_wt_03, - &ws_protocol_acurite_606tx, - &ws_protocol_acurite_609txc, - &ws_protocol_lacrosse_tx, - &ws_protocol_lacrosse_tx141thbv2, - &ws_protocol_oregon2, - &ws_protocol_oregon3, - &ws_protocol_acurite_592txr, - &ws_protocol_ambient_weather, - &ws_protocol_auriol_th, - &ws_protocol_oregon_v1, - &ws_protocol_tx_8300, - &ws_protocol_wendox_w6726, -}; - -const SubGhzProtocolRegistry weather_station_protocol_registry = { - .items = weather_station_protocol_registry_items, - .size = COUNT_OF(weather_station_protocol_registry_items)}; diff --git a/applications/external/weather_station/protocols/protocol_items.h b/applications/external/weather_station/protocols/protocol_items.h deleted file mode 100644 index 712eb07f2..000000000 --- a/applications/external/weather_station/protocols/protocol_items.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "../weather_station_app_i.h" - -#include "infactory.h" -#include "thermopro_tx4.h" -#include "nexus_th.h" -#include "gt_wt_02.h" -#include "gt_wt_03.h" -#include "acurite_606tx.h" -#include "acurite_609txc.h" -#include "lacrosse_tx.h" -#include "lacrosse_tx141thbv2.h" -#include "oregon2.h" -#include "oregon3.h" -#include "acurite_592txr.h" -#include "ambient_weather.h" -#include "auriol_hg0601a.h" -#include "oregon_v1.h" -#include "tx_8300.h" -#include "wendox_w6726.h" - -extern const SubGhzProtocolRegistry weather_station_protocol_registry; diff --git a/applications/external/weather_station/protocols/thermopro_tx4.c b/applications/external/weather_station/protocols/thermopro_tx4.c deleted file mode 100644 index 24e883e60..000000000 --- a/applications/external/weather_station/protocols/thermopro_tx4.c +++ /dev/null @@ -1,251 +0,0 @@ -#include "thermopro_tx4.h" - -#define TAG "WSProtocolThermoPRO_TX4" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/thermopro_tx2.c - * - * The sensor sends 37 bits 6 times, before the first packet there is a sync pulse. - * The packets are ppm modulated (distance coding) with a pulse of ~500 us - * followed by a short gap of ~2000 us for a 0 bit or a long ~4000 us gap for a - * 1 bit, the sync gap is ~9000 us. - * The data is grouped in 9 nibbles - * [type] [id0] [id1] [flags] [temp0] [temp1] [temp2] [humi0] [humi1] - * - type: 4 bit fixed 1001 (9) or 0110 (5) - * - id: 8 bit a random id that is generated when the sensor starts, could include battery status - * the same batteries often generate the same id - * - flags(3): is 1 when the battery is low, otherwise 0 (ok) - * - flags(2): is 1 when the sensor sends a reading when pressing the button on the sensor - * - flags(1,0): the channel number that can be set by the sensor (1, 2, 3, X) - * - temp: 12 bit signed scaled by 10 - * - humi: 8 bit always 11001100 (0xCC) if no humidity sensor is available - * - */ - -#define THERMO_PRO_TX4_TYPE_1 0b1001 -#define THERMO_PRO_TX4_TYPE_2 0b0110 - -static const SubGhzBlockConst ws_protocol_thermopro_tx4_const = { - .te_short = 500, - .te_long = 2000, - .te_delta = 150, - .min_count_bit_for_found = 37, -}; - -struct WSProtocolDecoderThermoPRO_TX4 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; -}; - -struct WSProtocolEncoderThermoPRO_TX4 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - ThermoPRO_TX4DecoderStepReset = 0, - ThermoPRO_TX4DecoderStepSaveDuration, - ThermoPRO_TX4DecoderStepCheckDuration, -} ThermoPRO_TX4DecoderStep; - -const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder = { - .alloc = ws_protocol_decoder_thermopro_tx4_alloc, - .free = ws_protocol_decoder_thermopro_tx4_free, - - .feed = ws_protocol_decoder_thermopro_tx4_feed, - .reset = ws_protocol_decoder_thermopro_tx4_reset, - - .get_hash_data = ws_protocol_decoder_thermopro_tx4_get_hash_data, - .serialize = ws_protocol_decoder_thermopro_tx4_serialize, - .deserialize = ws_protocol_decoder_thermopro_tx4_deserialize, - .get_string = ws_protocol_decoder_thermopro_tx4_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_thermopro_tx4 = { - .name = WS_PROTOCOL_THERMOPRO_TX4_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_thermopro_tx4_decoder, - .encoder = &ws_protocol_thermopro_tx4_encoder, -}; - -void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderThermoPRO_TX4* instance = malloc(sizeof(WSProtocolDecoderThermoPRO_TX4)); - instance->base.protocol = &ws_protocol_thermopro_tx4; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_thermopro_tx4_free(void* context) { - furi_assert(context); - WSProtocolDecoderThermoPRO_TX4* instance = context; - free(instance); -} - -void ws_protocol_decoder_thermopro_tx4_reset(void* context) { - furi_assert(context); - WSProtocolDecoderThermoPRO_TX4* instance = context; - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; -} - -static bool ws_protocol_thermopro_tx4_check(WSProtocolDecoderThermoPRO_TX4* instance) { - uint8_t type = instance->decoder.decode_data >> 33; - - if((type == THERMO_PRO_TX4_TYPE_1) || (type == THERMO_PRO_TX4_TYPE_2)) { - return true; - } else { - return false; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_thermopro_tx4_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 25) & 0xFF; - instance->battery_low = (instance->data >> 24) & 1; - instance->btn = (instance->data >> 23) & 1; - instance->channel = ((instance->data >> 21) & 0x03) + 1; - - if(!((instance->data >> 20) & 1)) { - instance->temp = (float)((instance->data >> 9) & 0x07FF) / 10.0f; - } else { - instance->temp = (float)((~(instance->data >> 9) & 0x07FF) + 1) / -10.0f; - } - - instance->humidity = (instance->data >> 1) & 0xFF; -} - -void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderThermoPRO_TX4* instance = context; - - switch(instance->decoder.parser_step) { - case ThermoPRO_TX4DecoderStepReset: - if((!level) && (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) < - ws_protocol_thermopro_tx4_const.te_delta * 10)) { - //Found sync - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } - break; - - case ThermoPRO_TX4DecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration; - } else { - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; - } - break; - - case ThermoPRO_TX4DecoderStepCheckDuration: - if(!level) { - if(DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_short * 18) < - ws_protocol_thermopro_tx4_const.te_delta * 10) { - //Found sync - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; - if((instance->decoder.decode_count_bit == - ws_protocol_thermopro_tx4_const.min_count_bit_for_found) && - ws_protocol_thermopro_tx4_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_thermopro_tx4_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepCheckDuration; - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - - break; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) < - ws_protocol_thermopro_tx4_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long) < - ws_protocol_thermopro_tx4_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, ws_protocol_thermopro_tx4_const.te_short) < - ws_protocol_thermopro_tx4_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_thermopro_tx4_const.te_long * 2) < - ws_protocol_thermopro_tx4_const.te_delta * 4)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; - } - } else { - instance->decoder.parser_step = ThermoPRO_TX4DecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderThermoPRO_TX4* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_thermopro_tx4_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderThermoPRO_TX4* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderThermoPRO_TX4* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - ws_protocol_thermopro_tx4_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderThermoPRO_TX4* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/thermopro_tx4.h b/applications/external/weather_station/protocols/thermopro_tx4.h deleted file mode 100644 index 526648d1e..000000000 --- a/applications/external/weather_station/protocols/thermopro_tx4.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_THERMOPRO_TX4_NAME "ThermoPRO-TX4" - -typedef struct WSProtocolDecoderThermoPRO_TX4 WSProtocolDecoderThermoPRO_TX4; -typedef struct WSProtocolEncoderThermoPRO_TX4 WSProtocolEncoderThermoPRO_TX4; - -extern const SubGhzProtocolDecoder ws_protocol_thermopro_tx4_decoder; -extern const SubGhzProtocolEncoder ws_protocol_thermopro_tx4_encoder; -extern const SubGhzProtocol ws_protocol_thermopro_tx4; - -/** - * Allocate WSProtocolDecoderThermoPRO_TX4. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderThermoPRO_TX4* pointer to a WSProtocolDecoderThermoPRO_TX4 instance - */ -void* ws_protocol_decoder_thermopro_tx4_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderThermoPRO_TX4. - * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance - */ -void ws_protocol_decoder_thermopro_tx4_free(void* context); - -/** - * Reset decoder WSProtocolDecoderThermoPRO_TX4. - * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance - */ -void ws_protocol_decoder_thermopro_tx4_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_thermopro_tx4_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_thermopro_tx4_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderThermoPRO_TX4. - * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_thermopro_tx4_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderThermoPRO_TX4. - * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_thermopro_tx4_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderThermoPRO_TX4 instance - * @param output Resulting text - */ -void ws_protocol_decoder_thermopro_tx4_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/tx_8300.c b/applications/external/weather_station/protocols/tx_8300.c deleted file mode 100644 index 3a06ce49d..000000000 --- a/applications/external/weather_station/protocols/tx_8300.c +++ /dev/null @@ -1,284 +0,0 @@ -#include "tx_8300.h" - -#define TAG "WSProtocolTX_8300" - -/* - * Help - * https://github.com/merbanan/rtl_433/blob/master/src/devices/ambientweather_tx8300.c - * - * Ambient Weather TX-8300 (also sold as TFA 30.3211.02). - * 1970us pulse with variable gap (third pulse 3920 us). - * Above 79% humidity, gap after third pulse is 5848 us. - * - Bit 1 : 1970us pulse with 3888 us gap - * - Bit 0 : 1970us pulse with 1936 us gap - * 74 bit (2 bit preamble and 72 bit data => 9 bytes => 18 nibbles) - * The preamble seems to be a repeat counter (00, and 01 seen), - * the first 4 bytes are data, - * the second 4 bytes the same data inverted, - * the last byte is a checksum. - * Preamble format (2 bits): - * [1 bit (0)] [1 bit rolling count] - * Payload format (32 bits): - * HHHHhhhh ??CCNIII IIIITTTT ttttuuuu - * - H = First BCD digit humidity (the MSB might be distorted by the demod) - * - h = Second BCD digit humidity, invalid humidity seems to be 0x0e - * - ? = Likely battery flag, 2 bits - * - C = Channel, 2 bits - * - N = Negative temperature sign bit - * - I = ID, 7-bit - * - T = First BCD digit temperature - * - t = Second BCD digit temperature - * - u = Third BCD digit temperature - * The Checksum seems to covers the 4 data bytes and is something like Fletcher-8. - **/ - -#define TX_8300_PACKAGE_SIZE 32 - -static const SubGhzBlockConst ws_protocol_tx_8300_const = { - .te_short = 1940, - .te_long = 3880, - .te_delta = 250, - .min_count_bit_for_found = 72, -}; - -struct WSProtocolDecoderTX_8300 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - uint32_t package_1; - uint32_t package_2; -}; - -struct WSProtocolEncoderTX_8300 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - TX_8300DecoderStepReset = 0, - TX_8300DecoderStepCheckPreambule, - TX_8300DecoderStepSaveDuration, - TX_8300DecoderStepCheckDuration, -} TX_8300DecoderStep; - -const SubGhzProtocolDecoder ws_protocol_tx_8300_decoder = { - .alloc = ws_protocol_decoder_tx_8300_alloc, - .free = ws_protocol_decoder_tx_8300_free, - - .feed = ws_protocol_decoder_tx_8300_feed, - .reset = ws_protocol_decoder_tx_8300_reset, - - .get_hash_data = ws_protocol_decoder_tx_8300_get_hash_data, - .serialize = ws_protocol_decoder_tx_8300_serialize, - .deserialize = ws_protocol_decoder_tx_8300_deserialize, - .get_string = ws_protocol_decoder_tx_8300_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_tx_8300_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_tx_8300 = { - .name = WS_PROTOCOL_TX_8300_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_tx_8300_decoder, - .encoder = &ws_protocol_tx_8300_encoder, -}; - -void* ws_protocol_decoder_tx_8300_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderTX_8300* instance = malloc(sizeof(WSProtocolDecoderTX_8300)); - instance->base.protocol = &ws_protocol_tx_8300; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_tx_8300_free(void* context) { - furi_assert(context); - WSProtocolDecoderTX_8300* instance = context; - free(instance); -} - -void ws_protocol_decoder_tx_8300_reset(void* context) { - furi_assert(context); - WSProtocolDecoderTX_8300* instance = context; - instance->decoder.parser_step = TX_8300DecoderStepReset; -} - -static bool ws_protocol_tx_8300_check_crc(WSProtocolDecoderTX_8300* instance) { - if(!instance->package_2) return false; - if(instance->package_1 != ~instance->package_2) return false; - - uint16_t x = 0; - uint16_t y = 0; - for(int i = 0; i < 32; i += 4) { - x += (instance->package_1 >> i) & 0x0F; - y += (instance->package_1 >> i) & 0x05; - } - uint8_t crc = (~x & 0xF) << 4 | (~y & 0xF); - return (crc == ((instance->decoder.decode_data) & 0xFF)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_tx_8300_remote_controller(WSBlockGeneric* instance) { - instance->humidity = (((instance->data >> 28) & 0x0F) * 10) + ((instance->data >> 24) & 0x0F); - instance->btn = WS_NO_BTN; - if(!((instance->data >> 22) & 0x03)) - instance->battery_low = 0; - else - instance->battery_low = 1; - instance->channel = (instance->data >> 20) & 0x03; - instance->id = (instance->data >> 12) & 0x7F; - - float temp_raw = ((instance->data >> 8) & 0x0F) * 10.0f + ((instance->data >> 4) & 0x0F) + - (instance->data & 0x0F) * 0.1f; - if(!((instance->data >> 19) & 1)) { - instance->temp = temp_raw; - } else { - instance->temp = -temp_raw; - } -} - -void ws_protocol_decoder_tx_8300_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderTX_8300* instance = context; - - switch(instance->decoder.parser_step) { - case TX_8300DecoderStepReset: - if((level) && (DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short * 2) < - ws_protocol_tx_8300_const.te_delta)) { - instance->decoder.parser_step = TX_8300DecoderStepCheckPreambule; - } - break; - - case TX_8300DecoderStepCheckPreambule: - if((!level) && ((DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short * 2) < - ws_protocol_tx_8300_const.te_delta) || - (DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short * 3) < - ws_protocol_tx_8300_const.te_delta))) { - instance->decoder.parser_step = TX_8300DecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 1; - instance->package_1 = 0; - instance->package_2 = 0; - } else { - instance->decoder.parser_step = TX_8300DecoderStepReset; - } - break; - - case TX_8300DecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = TX_8300DecoderStepCheckDuration; - } else { - instance->decoder.parser_step = TX_8300DecoderStepReset; - } - break; - - case TX_8300DecoderStepCheckDuration: - if(!level) { - if(duration >= ((uint32_t)ws_protocol_tx_8300_const.te_short * 5)) { - //Found syncPostfix - if((instance->decoder.decode_count_bit == - ws_protocol_tx_8300_const.min_count_bit_for_found) && - ws_protocol_tx_8300_check_crc(instance)) { - instance->generic.data = instance->package_1; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_tx_8300_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 1; - instance->decoder.parser_step = TX_8300DecoderStepReset; - break; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_tx_8300_const.te_short) < - ws_protocol_tx_8300_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_long) < - ws_protocol_tx_8300_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = TX_8300DecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_tx_8300_const.te_short) < - ws_protocol_tx_8300_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_tx_8300_const.te_short) < - ws_protocol_tx_8300_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = TX_8300DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = TX_8300DecoderStepReset; - } - - if(instance->decoder.decode_count_bit == TX_8300_PACKAGE_SIZE) { - instance->package_1 = instance->decoder.decode_data; - instance->decoder.decode_data = 0; - } else if(instance->decoder.decode_count_bit == TX_8300_PACKAGE_SIZE * 2) { - instance->package_2 = instance->decoder.decode_data; - instance->decoder.decode_data = 0; - } - - } else { - instance->decoder.parser_step = TX_8300DecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderTX_8300* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_tx_8300_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderTX_8300* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderTX_8300* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, flipper_format, ws_protocol_tx_8300_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_tx_8300_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderTX_8300* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/tx_8300.h b/applications/external/weather_station/protocols/tx_8300.h deleted file mode 100644 index 088ccd7c0..000000000 --- a/applications/external/weather_station/protocols/tx_8300.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_TX_8300_NAME "TX8300" - -typedef struct WSProtocolDecoderTX_8300 WSProtocolDecoderTX_8300; -typedef struct WSProtocolEncoderTX_8300 WSProtocolEncoderTX_8300; - -extern const SubGhzProtocolDecoder ws_protocol_tx_8300_decoder; -extern const SubGhzProtocolEncoder ws_protocol_tx_8300_encoder; -extern const SubGhzProtocol ws_protocol_tx_8300; - -/** - * Allocate WSProtocolDecoderTX_8300. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderTX_8300* pointer to a WSProtocolDecoderTX_8300 instance - */ -void* ws_protocol_decoder_tx_8300_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderTX_8300. - * @param context Pointer to a WSProtocolDecoderTX_8300 instance - */ -void ws_protocol_decoder_tx_8300_free(void* context); - -/** - * Reset decoder WSProtocolDecoderTX_8300. - * @param context Pointer to a WSProtocolDecoderTX_8300 instance - */ -void ws_protocol_decoder_tx_8300_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderTX_8300 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_tx_8300_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderTX_8300 instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_tx_8300_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderTX_8300. - * @param context Pointer to a WSProtocolDecoderTX_8300 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_tx_8300_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderTX_8300. - * @param context Pointer to a WSProtocolDecoderTX_8300 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_tx_8300_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderTX_8300 instance - * @param output Resulting text - */ -void ws_protocol_decoder_tx_8300_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/wendox_w6726.c b/applications/external/weather_station/protocols/wendox_w6726.c deleted file mode 100644 index 2fbe961f7..000000000 --- a/applications/external/weather_station/protocols/wendox_w6726.c +++ /dev/null @@ -1,299 +0,0 @@ -#include "wendox_w6726.h" - -#define TAG "WSProtocolWendoxW6726" - -/* - * Wendox W6726 - * - * Temperature -50C to +70C - * _ _ _ __ _ - * _| |___| |___| |___ ... | |_| |__...._______________ - * preamble data guard time - * - * 3 reps every 3 minutes - * in the first message 11 bytes of the preamble in the rest by 7 - * - * bit 0: 1955-hi, 5865-lo - * bit 1: 5865-hi, 1955-lo - * guard time: 12*1955+(lo last bit) - * data: 29 bit - * - * IIIII | ZTTTTTTTTT | uuuuuuuBuu | CCCC - * - * I: identification; - * Z: temperature sign; - * T: temperature sign dependent +12C; - * B: battery low; flag to indicate low battery voltage; - * C: CRC4 (polynomial = 0x9, start_data = 0xD); - * u: unknown; - */ - -static const SubGhzBlockConst ws_protocol_wendox_w6726_const = { - .te_short = 1955, - .te_long = 5865, - .te_delta = 300, - .min_count_bit_for_found = 29, -}; - -struct WSProtocolDecoderWendoxW6726 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - WSBlockGeneric generic; - - uint16_t header_count; -}; - -struct WSProtocolEncoderWendoxW6726 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - WSBlockGeneric generic; -}; - -typedef enum { - WendoxW6726DecoderStepReset = 0, - WendoxW6726DecoderStepCheckPreambule, - WendoxW6726DecoderStepSaveDuration, - WendoxW6726DecoderStepCheckDuration, -} WendoxW6726DecoderStep; - -const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder = { - .alloc = ws_protocol_decoder_wendox_w6726_alloc, - .free = ws_protocol_decoder_wendox_w6726_free, - - .feed = ws_protocol_decoder_wendox_w6726_feed, - .reset = ws_protocol_decoder_wendox_w6726_reset, - - .get_hash_data = ws_protocol_decoder_wendox_w6726_get_hash_data, - .serialize = ws_protocol_decoder_wendox_w6726_serialize, - .deserialize = ws_protocol_decoder_wendox_w6726_deserialize, - .get_string = ws_protocol_decoder_wendox_w6726_get_string, -}; - -const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol ws_protocol_wendox_w6726 = { - .name = WS_PROTOCOL_WENDOX_W6726_NAME, - .type = SubGhzProtocolWeatherStation, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &ws_protocol_wendox_w6726_decoder, - .encoder = &ws_protocol_wendox_w6726_encoder, -}; - -void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - WSProtocolDecoderWendoxW6726* instance = malloc(sizeof(WSProtocolDecoderWendoxW6726)); - instance->base.protocol = &ws_protocol_wendox_w6726; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void ws_protocol_decoder_wendox_w6726_free(void* context) { - furi_assert(context); - WSProtocolDecoderWendoxW6726* instance = context; - free(instance); -} - -void ws_protocol_decoder_wendox_w6726_reset(void* context) { - furi_assert(context); - WSProtocolDecoderWendoxW6726* instance = context; - instance->decoder.parser_step = WendoxW6726DecoderStepReset; -} - -static bool ws_protocol_wendox_w6726_check(WSProtocolDecoderWendoxW6726* instance) { - if(!instance->decoder.decode_data) return false; - uint8_t msg[] = { - instance->decoder.decode_data >> 28, - instance->decoder.decode_data >> 20, - instance->decoder.decode_data >> 12, - instance->decoder.decode_data >> 4}; - - uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD); - return (crc == (instance->decoder.decode_data & 0x0F)); -} - -/** - * Analysis of received data - * @param instance Pointer to a WSBlockGeneric* instance - */ -static void ws_protocol_wendox_w6726_remote_controller(WSBlockGeneric* instance) { - instance->id = (instance->data >> 24) & 0xFF; - instance->battery_low = (instance->data >> 6) & 1; - instance->channel = WS_NO_CHANNEL; - - if(((instance->data >> 23) & 1)) { - instance->temp = (float)(((instance->data >> 14) & 0x1FF) + 12) / 10.0f; - } else { - instance->temp = (float)((~(instance->data >> 14) & 0x1FF) + 1 - 12) / -10.0f; - } - - if(instance->temp < -50.0f) { - instance->temp = -50.0f; - } else if(instance->temp > 70.0f) { - instance->temp = 70.0f; - } - - instance->btn = WS_NO_BTN; - instance->humidity = WS_NO_HUMIDITY; -} - -void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - WSProtocolDecoderWendoxW6726* instance = context; - - switch(instance->decoder.parser_step) { - case WendoxW6726DecoderStepReset: - if((level) && (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < - ws_protocol_wendox_w6726_const.te_delta)) { - instance->decoder.parser_step = WendoxW6726DecoderStepCheckPreambule; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - - case WendoxW6726DecoderStepCheckPreambule: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < - ws_protocol_wendox_w6726_const.te_delta * 1) && - (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < - ws_protocol_wendox_w6726_const.te_delta * 2)) { - instance->header_count++; - } else if((instance->header_count > 4) && (instance->header_count < 12)) { - if((DURATION_DIFF( - instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < - ws_protocol_wendox_w6726_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < - ws_protocol_wendox_w6726_const.te_delta)) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = WendoxW6726DecoderStepReset; - } - - } else { - instance->decoder.parser_step = WendoxW6726DecoderStepReset; - } - } - break; - - case WendoxW6726DecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = WendoxW6726DecoderStepCheckDuration; - } else { - instance->decoder.parser_step = WendoxW6726DecoderStepReset; - } - break; - - case WendoxW6726DecoderStepCheckDuration: - if(!level) { - if(duration > - ws_protocol_wendox_w6726_const.te_short + ws_protocol_wendox_w6726_const.te_long) { - if(DURATION_DIFF( - instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < - ws_protocol_wendox_w6726_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; - } else if( - DURATION_DIFF( - instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < - ws_protocol_wendox_w6726_const.te_delta * 2) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = WendoxW6726DecoderStepReset; - } - if((instance->decoder.decode_count_bit == - ws_protocol_wendox_w6726_const.min_count_bit_for_found) && - ws_protocol_wendox_w6726_check(instance)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - ws_protocol_wendox_w6726_remote_controller(&instance->generic); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - - instance->decoder.parser_step = WendoxW6726DecoderStepReset; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_short) < - ws_protocol_wendox_w6726_const.te_delta) && - (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_long) < - ws_protocol_wendox_w6726_const.te_delta * 3)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, ws_protocol_wendox_w6726_const.te_long) < - ws_protocol_wendox_w6726_const.te_delta * 2) && - (DURATION_DIFF(duration, ws_protocol_wendox_w6726_const.te_short) < - ws_protocol_wendox_w6726_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = WendoxW6726DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = WendoxW6726DecoderStepReset; - } - } else { - instance->decoder.parser_step = WendoxW6726DecoderStepReset; - } - break; - } -} - -uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context) { - furi_assert(context); - WSProtocolDecoderWendoxW6726* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - WSProtocolDecoderWendoxW6726* instance = context; - return ws_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -SubGhzProtocolStatus - ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - WSProtocolDecoderWendoxW6726* instance = context; - return ws_block_generic_deserialize_check_count_bit( - &instance->generic, - flipper_format, - ws_protocol_wendox_w6726_const.min_count_bit_for_found); -} - -void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output) { - furi_assert(context); - WSProtocolDecoderWendoxW6726* instance = context; - furi_string_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%lX Ch:%d Bat:%d\r\n" - "Temp:%3.1f C Hum:%d%%", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data), - instance->generic.id, - instance->generic.channel, - instance->generic.battery_low, - (double)instance->generic.temp, - instance->generic.humidity); -} diff --git a/applications/external/weather_station/protocols/wendox_w6726.h b/applications/external/weather_station/protocols/wendox_w6726.h deleted file mode 100644 index 236777a1c..000000000 --- a/applications/external/weather_station/protocols/wendox_w6726.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include "ws_generic.h" -#include - -#define WS_PROTOCOL_WENDOX_W6726_NAME "Wendox W6726" - -typedef struct WSProtocolDecoderWendoxW6726 WSProtocolDecoderWendoxW6726; -typedef struct WSProtocolEncoderWendoxW6726 WSProtocolEncoderWendoxW6726; - -extern const SubGhzProtocolDecoder ws_protocol_wendox_w6726_decoder; -extern const SubGhzProtocolEncoder ws_protocol_wendox_w6726_encoder; -extern const SubGhzProtocol ws_protocol_wendox_w6726; - -/** - * Allocate WSProtocolDecoderWendoxW6726. - * @param environment Pointer to a SubGhzEnvironment instance - * @return WSProtocolDecoderWendoxW6726* pointer to a WSProtocolDecoderWendoxW6726 instance - */ -void* ws_protocol_decoder_wendox_w6726_alloc(SubGhzEnvironment* environment); - -/** - * Free WSProtocolDecoderWendoxW6726. - * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance - */ -void ws_protocol_decoder_wendox_w6726_free(void* context); - -/** - * Reset decoder WSProtocolDecoderWendoxW6726. - * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance - */ -void ws_protocol_decoder_wendox_w6726_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void ws_protocol_decoder_wendox_w6726_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance - * @return hash Hash sum - */ -uint8_t ws_protocol_decoder_wendox_w6726_get_hash_data(void* context); - -/** - * Serialize data WSProtocolDecoderWendoxW6726. - * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_protocol_decoder_wendox_w6726_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSProtocolDecoderWendoxW6726. - * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_protocol_decoder_wendox_w6726_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a WSProtocolDecoderWendoxW6726 instance - * @param output Resulting text - */ -void ws_protocol_decoder_wendox_w6726_get_string(void* context, FuriString* output); diff --git a/applications/external/weather_station/protocols/ws_generic.c b/applications/external/weather_station/protocols/ws_generic.c deleted file mode 100644 index 026856a9e..000000000 --- a/applications/external/weather_station/protocols/ws_generic.c +++ /dev/null @@ -1,256 +0,0 @@ -#include "ws_generic.h" -#include -#include -#include "../helpers/weather_station_types.h" - -#define TAG "WSBlockGeneric" - -void ws_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) { - const char* preset_name_temp; - if(!strcmp(preset_name, "AM270")) { - preset_name_temp = "FuriHalSubGhzPresetOok270Async"; - } else if(!strcmp(preset_name, "AM650")) { - preset_name_temp = "FuriHalSubGhzPresetOok650Async"; - } else if(!strcmp(preset_name, "FM238")) { - preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async"; - } else if(!strcmp(preset_name, "FM476")) { - preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async"; - } else { - preset_name_temp = "FuriHalSubGhzPresetCustom"; - } - furi_string_set(preset_str, preset_name_temp); -} - -SubGhzProtocolStatus ws_block_generic_serialize( - WSBlockGeneric* instance, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(instance); - SubGhzProtocolStatus res = SubGhzProtocolStatusError; - FuriString* temp_str; - temp_str = furi_string_alloc(); - do { - stream_clean(flipper_format_get_raw_stream(flipper_format)); - if(!flipper_format_write_header_cstr( - flipper_format, WS_KEY_FILE_TYPE, WS_KEY_FILE_VERSION)) { - FURI_LOG_E(TAG, "Unable to add header"); - res = SubGhzProtocolStatusErrorParserHeader; - break; - } - - if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { - FURI_LOG_E(TAG, "Unable to add Frequency"); - res = SubGhzProtocolStatusErrorParserFrequency; - break; - } - - ws_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); - if(!flipper_format_write_string_cstr( - flipper_format, "Preset", furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to add Preset"); - res = SubGhzProtocolStatusErrorParserPreset; - break; - } - if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { - if(!flipper_format_write_string_cstr( - flipper_format, "Custom_preset_module", "CC1101")) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); - res = SubGhzProtocolStatusErrorParserCustomPreset; - break; - } - if(!flipper_format_write_hex( - flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); - res = SubGhzProtocolStatusErrorParserCustomPreset; - break; - } - } - if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { - FURI_LOG_E(TAG, "Unable to add Protocol"); - res = SubGhzProtocolStatusErrorParserProtocolName; - break; - } - - uint32_t temp_data = instance->id; - if(!flipper_format_write_uint32(flipper_format, "Id", &temp_data, 1)) { - FURI_LOG_E(TAG, "Unable to add Id"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - temp_data = instance->data_count_bit; - if(!flipper_format_write_uint32(flipper_format, "Bit", &temp_data, 1)) { - FURI_LOG_E(TAG, "Unable to add Bit"); - res = SubGhzProtocolStatusErrorParserBitCount; - break; - } - - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; - } - - if(!flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Data"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - temp_data = instance->battery_low; - if(!flipper_format_write_uint32(flipper_format, "Batt", &temp_data, 1)) { - FURI_LOG_E(TAG, "Unable to add Battery_low"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - temp_data = instance->humidity; - if(!flipper_format_write_uint32(flipper_format, "Hum", &temp_data, 1)) { - FURI_LOG_E(TAG, "Unable to add Humidity"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - //DATE AGE set - FuriHalRtcDateTime curr_dt; - furi_hal_rtc_get_datetime(&curr_dt); - uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); - - temp_data = curr_ts; - if(!flipper_format_write_uint32(flipper_format, "Ts", &temp_data, 1)) { - FURI_LOG_E(TAG, "Unable to add timestamp"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - temp_data = instance->channel; - if(!flipper_format_write_uint32(flipper_format, "Ch", &temp_data, 1)) { - FURI_LOG_E(TAG, "Unable to add Channel"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - temp_data = instance->btn; - if(!flipper_format_write_uint32(flipper_format, "Btn", &temp_data, 1)) { - FURI_LOG_E(TAG, "Unable to add Btn"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - float temp = instance->temp; - if(!flipper_format_write_float(flipper_format, "Temp", &temp, 1)) { - FURI_LOG_E(TAG, "Unable to add Temperature"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - res = SubGhzProtocolStatusOk; - } while(false); - furi_string_free(temp_str); - return res; -} - -SubGhzProtocolStatus - ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format) { - furi_assert(instance); - SubGhzProtocolStatus res = SubGhzProtocolStatusError; - uint32_t temp_data = 0; - - do { - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - if(!flipper_format_read_uint32(flipper_format, "Id", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Id"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - instance->id = (uint32_t)temp_data; - - if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Bit"); - res = SubGhzProtocolStatusErrorParserBitCount; - break; - } - instance->data_count_bit = (uint8_t)temp_data; - - uint8_t key_data[sizeof(uint64_t)] = {0}; - if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Data"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - - for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - instance->data = instance->data << 8 | key_data[i]; - } - - if(!flipper_format_read_uint32(flipper_format, "Batt", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Battery_low"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - instance->battery_low = (uint8_t)temp_data; - - if(!flipper_format_read_uint32(flipper_format, "Hum", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Humidity"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - instance->humidity = (uint8_t)temp_data; - - if(!flipper_format_read_uint32(flipper_format, "Ts", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing timestamp"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - instance->timestamp = (uint32_t)temp_data; - - if(!flipper_format_read_uint32(flipper_format, "Ch", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Channel"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - instance->channel = (uint8_t)temp_data; - - if(!flipper_format_read_uint32(flipper_format, "Btn", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Btn"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - instance->btn = (uint8_t)temp_data; - - float temp; - if(!flipper_format_read_float(flipper_format, "Temp", (float*)&temp, 1)) { - FURI_LOG_E(TAG, "Missing Temperature"); - res = SubGhzProtocolStatusErrorParserOthers; - break; - } - instance->temp = temp; - - res = SubGhzProtocolStatusOk; - } while(0); - - return res; -} - -SubGhzProtocolStatus ws_block_generic_deserialize_check_count_bit( - WSBlockGeneric* instance, - FlipperFormat* flipper_format, - uint16_t count_bit) { - SubGhzProtocolStatus ret = SubGhzProtocolStatusError; - do { - ret = ws_block_generic_deserialize(instance, flipper_format); - if(ret != SubGhzProtocolStatusOk) { - break; - } - if(instance->data_count_bit != count_bit) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - ret = SubGhzProtocolStatusErrorValueBitCount; - break; - } - } while(false); - return ret; -} \ No newline at end of file diff --git a/applications/external/weather_station/protocols/ws_generic.h b/applications/external/weather_station/protocols/ws_generic.h deleted file mode 100644 index ff047fae6..000000000 --- a/applications/external/weather_station/protocols/ws_generic.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include "furi.h" -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define WS_NO_ID 0xFFFFFFFF -#define WS_NO_BATT 0xFF -#define WS_NO_HUMIDITY 0xFF -#define WS_NO_CHANNEL 0xFF -#define WS_NO_BTN 0xFF -#define WS_NO_TEMPERATURE -273.0f - -typedef struct WSBlockGeneric WSBlockGeneric; - -struct WSBlockGeneric { - const char* protocol_name; - uint64_t data; - uint32_t id; - uint8_t data_count_bit; - uint8_t battery_low; - uint8_t humidity; - uint32_t timestamp; - uint8_t channel; - uint8_t btn; - float temp; -}; - -/** - * Get name preset. - * @param preset_name name preset - * @param preset_str Output name preset - */ -void ws_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str); - -/** - * Serialize data WSBlockGeneric. - * @param instance Pointer to a WSBlockGeneric instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return status - */ -SubGhzProtocolStatus ws_block_generic_serialize( - WSBlockGeneric* instance, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data WSBlockGeneric. - * @param instance Pointer to a WSBlockGeneric instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return status - */ -SubGhzProtocolStatus - ws_block_generic_deserialize(WSBlockGeneric* instance, FlipperFormat* flipper_format); - -/** - * Deserialize data WSBlockGeneric. - * @param instance Pointer to a WSBlockGeneric instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param count_bit Count bit protocol - * @return status - */ -SubGhzProtocolStatus ws_block_generic_deserialize_check_count_bit( - WSBlockGeneric* instance, - FlipperFormat* flipper_format, - uint16_t count_bit); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/external/weather_station/scenes/weather_station_receiver.c b/applications/external/weather_station/scenes/weather_station_receiver.c deleted file mode 100644 index e76810430..000000000 --- a/applications/external/weather_station/scenes/weather_station_receiver.c +++ /dev/null @@ -1,211 +0,0 @@ -#include "../weather_station_app_i.h" -#include "../views/weather_station_receiver.h" - -static const NotificationSequence subghs_sequence_rx = { - &message_green_255, - - &message_vibro_on, - &message_note_c6, - &message_delay_50, - &message_sound_off, - &message_vibro_off, - - &message_delay_50, - NULL, -}; - -static const NotificationSequence subghs_sequence_rx_locked = { - &message_green_255, - - &message_display_backlight_on, - - &message_vibro_on, - &message_note_c6, - &message_delay_50, - &message_sound_off, - &message_vibro_off, - - &message_delay_500, - - &message_display_backlight_off, - NULL, -}; - -static void weather_station_scene_receiver_update_statusbar(void* context) { - WeatherStationApp* app = context; - FuriString* history_stat_str; - history_stat_str = furi_string_alloc(); - if(!ws_history_get_text_space_left(app->txrx->history, history_stat_str)) { - FuriString* frequency_str; - FuriString* modulation_str; - - frequency_str = furi_string_alloc(); - modulation_str = furi_string_alloc(); - - ws_get_frequency_modulation(app, frequency_str, modulation_str); - - ws_view_receiver_add_data_statusbar( - app->ws_receiver, - furi_string_get_cstr(frequency_str), - furi_string_get_cstr(modulation_str), - furi_string_get_cstr(history_stat_str)); - - furi_string_free(frequency_str); - furi_string_free(modulation_str); - } else { - ws_view_receiver_add_data_statusbar( - app->ws_receiver, furi_string_get_cstr(history_stat_str), "", ""); - } - furi_string_free(history_stat_str); -} - -void weather_station_scene_receiver_callback(WSCustomEvent event, void* context) { - furi_assert(context); - WeatherStationApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -static void weather_station_scene_receiver_add_to_history_callback( - SubGhzReceiver* receiver, - SubGhzProtocolDecoderBase* decoder_base, - void* context) { - furi_assert(context); - WeatherStationApp* app = context; - FuriString* str_buff; - str_buff = furi_string_alloc(); - - if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) == - WSHistoryStateAddKeyNewDada) { - furi_string_reset(str_buff); - - ws_history_get_text_item_menu( - app->txrx->history, str_buff, ws_history_get_item(app->txrx->history) - 1); - ws_view_receiver_add_item_to_menu( - app->ws_receiver, - furi_string_get_cstr(str_buff), - ws_history_get_type_protocol( - app->txrx->history, ws_history_get_item(app->txrx->history) - 1)); - - weather_station_scene_receiver_update_statusbar(app); - notification_message(app->notifications, &sequence_blink_green_10); - if(app->lock != WSLockOn) { - notification_message(app->notifications, &subghs_sequence_rx); - } else { - notification_message(app->notifications, &subghs_sequence_rx_locked); - } - } - subghz_receiver_reset(receiver); - furi_string_free(str_buff); - app->txrx->rx_key_state = WSRxKeyStateAddKey; -} - -void weather_station_scene_receiver_on_enter(void* context) { - WeatherStationApp* app = context; - - FuriString* str_buff; - str_buff = furi_string_alloc(); - - if(app->txrx->rx_key_state == WSRxKeyStateIDLE) { - ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0); - ws_history_reset(app->txrx->history); - app->txrx->rx_key_state = WSRxKeyStateStart; - } - - ws_view_receiver_set_lock(app->ws_receiver, app->lock); - - //Load history to receiver - ws_view_receiver_exit(app->ws_receiver); - for(uint8_t i = 0; i < ws_history_get_item(app->txrx->history); i++) { - furi_string_reset(str_buff); - ws_history_get_text_item_menu(app->txrx->history, str_buff, i); - ws_view_receiver_add_item_to_menu( - app->ws_receiver, - furi_string_get_cstr(str_buff), - ws_history_get_type_protocol(app->txrx->history, i)); - app->txrx->rx_key_state = WSRxKeyStateAddKey; - } - furi_string_free(str_buff); - weather_station_scene_receiver_update_statusbar(app); - - ws_view_receiver_set_callback(app->ws_receiver, weather_station_scene_receiver_callback, app); - subghz_receiver_set_rx_callback( - app->txrx->receiver, weather_station_scene_receiver_add_to_history_callback, app); - - if(app->txrx->txrx_state == WSTxRxStateRx) { - ws_rx_end(app); - }; - if((app->txrx->txrx_state == WSTxRxStateIDLE) || (app->txrx->txrx_state == WSTxRxStateSleep)) { - ws_begin( - app, - subghz_setting_get_preset_data_by_name( - app->setting, furi_string_get_cstr(app->txrx->preset->name))); - - ws_rx(app, app->txrx->preset->frequency); - } - - ws_view_receiver_set_idx_menu(app->ws_receiver, app->txrx->idx_menu_chosen); - view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiver); -} - -bool weather_station_scene_receiver_on_event(void* context, SceneManagerEvent event) { - WeatherStationApp* app = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case WSCustomEventViewReceiverBack: - // Stop CC1101 Rx - if(app->txrx->txrx_state == WSTxRxStateRx) { - ws_rx_end(app); - ws_sleep(app); - }; - app->txrx->hopper_state = WSHopperStateOFF; - app->txrx->idx_menu_chosen = 0; - subghz_receiver_set_rx_callback(app->txrx->receiver, NULL, app); - - app->txrx->rx_key_state = WSRxKeyStateIDLE; - ws_preset_init( - app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0); - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, WeatherStationSceneStart); - consumed = true; - break; - case WSCustomEventViewReceiverOK: - app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver); - scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverInfo); - consumed = true; - break; - case WSCustomEventViewReceiverConfig: - app->txrx->idx_menu_chosen = ws_view_receiver_get_idx_menu(app->ws_receiver); - scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiverConfig); - consumed = true; - break; - case WSCustomEventViewReceiverOffDisplay: - notification_message(app->notifications, &sequence_display_backlight_off); - consumed = true; - break; - case WSCustomEventViewReceiverUnlock: - app->lock = WSLockOff; - consumed = true; - break; - default: - break; - } - } else if(event.type == SceneManagerEventTypeTick) { - if(app->txrx->hopper_state != WSHopperStateOFF) { - ws_hopper_update(app); - weather_station_scene_receiver_update_statusbar(app); - } - // Get current RSSI - float rssi = furi_hal_subghz_get_rssi(); - ws_view_receiver_set_rssi(app->ws_receiver, rssi); - - if(app->txrx->txrx_state == WSTxRxStateRx) { - notification_message(app->notifications, &sequence_blink_cyan_10); - } - } - return consumed; -} - -void weather_station_scene_receiver_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/weather_station/scenes/weather_station_scene.c b/applications/external/weather_station/scenes/weather_station_scene.c deleted file mode 100644 index f9306e5f4..000000000 --- a/applications/external/weather_station/scenes/weather_station_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "../weather_station_app_i.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const weather_station_scene_on_enter_handlers[])(void*) = { -#include "weather_station_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const weather_station_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "weather_station_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const weather_station_scene_on_exit_handlers[])(void* context) = { -#include "weather_station_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers weather_station_scene_handlers = { - .on_enter_handlers = weather_station_scene_on_enter_handlers, - .on_event_handlers = weather_station_scene_on_event_handlers, - .on_exit_handlers = weather_station_scene_on_exit_handlers, - .scene_num = WeatherStationSceneNum, -}; diff --git a/applications/external/weather_station/scenes/weather_station_scene.h b/applications/external/weather_station/scenes/weather_station_scene.h deleted file mode 100644 index 8cee4ee60..000000000 --- a/applications/external/weather_station/scenes/weather_station_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) WeatherStationScene##id, -typedef enum { -#include "weather_station_scene_config.h" - WeatherStationSceneNum, -} WeatherStationScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers weather_station_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "weather_station_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "weather_station_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "weather_station_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/weather_station/scenes/weather_station_scene_about.c b/applications/external/weather_station/scenes/weather_station_scene_about.c deleted file mode 100644 index d916dc76f..000000000 --- a/applications/external/weather_station/scenes/weather_station_scene_about.c +++ /dev/null @@ -1,78 +0,0 @@ -#include "../weather_station_app_i.h" -#include "../helpers/weather_station_types.h" - -void weather_station_scene_about_widget_callback( - GuiButtonType result, - InputType type, - void* context) { - WeatherStationApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -void weather_station_scene_about_on_enter(void* context) { - WeatherStationApp* app = context; - - FuriString* temp_str; - temp_str = furi_string_alloc(); - furi_string_printf(temp_str, "\e#%s\n", "Information"); - - furi_string_cat_printf(temp_str, "Version: %s\n", WS_VERSION_APP); - furi_string_cat_printf(temp_str, "Developed by: %s\n", WS_DEVELOPED); - furi_string_cat_printf(temp_str, "Github: %s\n\n", WS_GITHUB); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); - furi_string_cat_printf( - temp_str, "Reading messages from\nweather stations that work\nwith SubGhz sensors\n\n"); - - furi_string_cat_printf(temp_str, "Supported protocols:\n"); - size_t i = 0; - const char* protocol_name = - subghz_environment_get_protocol_name_registry(app->txrx->environment, i++); - do { - furi_string_cat_printf(temp_str, "%s\n", protocol_name); - protocol_name = subghz_environment_get_protocol_name_registry(app->txrx->environment, i++); - } while(protocol_name != NULL); - - widget_add_text_box_element( - app->widget, - 0, - 0, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! \e!\n", - false); - widget_add_text_box_element( - app->widget, - 0, - 2, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! Weather station \e!\n", - false); - widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); - furi_string_free(temp_str); - - view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewWidget); -} - -bool weather_station_scene_about_on_event(void* context, SceneManagerEvent event) { - WeatherStationApp* app = context; - bool consumed = false; - UNUSED(app); - UNUSED(event); - - return consumed; -} - -void weather_station_scene_about_on_exit(void* context) { - WeatherStationApp* app = context; - - // Clear views - widget_reset(app->widget); -} diff --git a/applications/external/weather_station/scenes/weather_station_scene_config.h b/applications/external/weather_station/scenes/weather_station_scene_config.h deleted file mode 100644 index 0ba8ec013..000000000 --- a/applications/external/weather_station/scenes/weather_station_scene_config.h +++ /dev/null @@ -1,5 +0,0 @@ -ADD_SCENE(weather_station, start, Start) -ADD_SCENE(weather_station, about, About) -ADD_SCENE(weather_station, receiver, Receiver) -ADD_SCENE(weather_station, receiver_config, ReceiverConfig) -ADD_SCENE(weather_station, receiver_info, ReceiverInfo) diff --git a/applications/external/weather_station/scenes/weather_station_scene_receiver_config.c b/applications/external/weather_station/scenes/weather_station_scene_receiver_config.c deleted file mode 100644 index fcd8f6d3e..000000000 --- a/applications/external/weather_station/scenes/weather_station_scene_receiver_config.c +++ /dev/null @@ -1,223 +0,0 @@ -#include "../weather_station_app_i.h" - -enum WSSettingIndex { - WSSettingIndexFrequency, - WSSettingIndexHopping, - WSSettingIndexModulation, - WSSettingIndexLock, -}; - -#define HOPPING_COUNT 2 -const char* const hopping_text[HOPPING_COUNT] = { - "OFF", - "ON", -}; -const uint32_t hopping_value[HOPPING_COUNT] = { - WSHopperStateOFF, - WSHopperStateRunnig, -}; - -uint8_t weather_station_scene_receiver_config_next_frequency(const uint32_t value, void* context) { - furi_assert(context); - WeatherStationApp* app = context; - uint8_t index = 0; - for(uint8_t i = 0; i < subghz_setting_get_frequency_count(app->setting); i++) { - if(value == subghz_setting_get_frequency(app->setting, i)) { - index = i; - break; - } else { - index = subghz_setting_get_frequency_default_index(app->setting); - } - } - return index; -} - -uint8_t weather_station_scene_receiver_config_next_preset(const char* preset_name, void* context) { - furi_assert(context); - WeatherStationApp* app = context; - uint8_t index = 0; - for(uint8_t i = 0; i < subghz_setting_get_preset_count(app->setting); i++) { - if(!strcmp(subghz_setting_get_preset_name(app->setting, i), preset_name)) { - index = i; - break; - } else { - // index = subghz_setting_get_frequency_default_index(app ->setting); - } - } - return index; -} - -uint8_t weather_station_scene_receiver_config_hopper_value_index( - const uint32_t value, - const uint32_t values[], - uint8_t values_count, - void* context) { - furi_assert(context); - UNUSED(values_count); - WeatherStationApp* app = context; - - if(value == values[0]) { - return 0; - } else { - variable_item_set_current_value_text( - (VariableItem*)scene_manager_get_scene_state( - app->scene_manager, WeatherStationSceneReceiverConfig), - " -----"); - return 1; - } -} - -static void weather_station_scene_receiver_config_set_frequency(VariableItem* item) { - WeatherStationApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - if(app->txrx->hopper_state == WSHopperStateOFF) { - char text_buf[10] = {0}; - snprintf( - text_buf, - sizeof(text_buf), - "%lu.%02lu", - subghz_setting_get_frequency(app->setting, index) / 1000000, - (subghz_setting_get_frequency(app->setting, index) % 1000000) / 10000); - variable_item_set_current_value_text(item, text_buf); - app->txrx->preset->frequency = subghz_setting_get_frequency(app->setting, index); - } else { - variable_item_set_current_value_index( - item, subghz_setting_get_frequency_default_index(app->setting)); - } -} - -static void weather_station_scene_receiver_config_set_preset(VariableItem* item) { - WeatherStationApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text( - item, subghz_setting_get_preset_name(app->setting, index)); - ws_preset_init( - app, - subghz_setting_get_preset_name(app->setting, index), - app->txrx->preset->frequency, - subghz_setting_get_preset_data(app->setting, index), - subghz_setting_get_preset_data_size(app->setting, index)); -} - -static void weather_station_scene_receiver_config_set_hopping_running(VariableItem* item) { - WeatherStationApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, hopping_text[index]); - if(hopping_value[index] == WSHopperStateOFF) { - char text_buf[10] = {0}; - snprintf( - text_buf, - sizeof(text_buf), - "%lu.%02lu", - subghz_setting_get_default_frequency(app->setting) / 1000000, - (subghz_setting_get_default_frequency(app->setting) % 1000000) / 10000); - variable_item_set_current_value_text( - (VariableItem*)scene_manager_get_scene_state( - app->scene_manager, WeatherStationSceneReceiverConfig), - text_buf); - app->txrx->preset->frequency = subghz_setting_get_default_frequency(app->setting); - variable_item_set_current_value_index( - (VariableItem*)scene_manager_get_scene_state( - app->scene_manager, WeatherStationSceneReceiverConfig), - subghz_setting_get_frequency_default_index(app->setting)); - } else { - variable_item_set_current_value_text( - (VariableItem*)scene_manager_get_scene_state( - app->scene_manager, WeatherStationSceneReceiverConfig), - " -----"); - variable_item_set_current_value_index( - (VariableItem*)scene_manager_get_scene_state( - app->scene_manager, WeatherStationSceneReceiverConfig), - subghz_setting_get_frequency_default_index(app->setting)); - } - - app->txrx->hopper_state = hopping_value[index]; -} - -static void - weather_station_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { - furi_assert(context); - WeatherStationApp* app = context; - if(index == WSSettingIndexLock) { - view_dispatcher_send_custom_event(app->view_dispatcher, WSCustomEventSceneSettingLock); - } -} - -void weather_station_scene_receiver_config_on_enter(void* context) { - WeatherStationApp* app = context; - VariableItem* item; - uint8_t value_index; - - item = variable_item_list_add( - app->variable_item_list, - "Frequency:", - subghz_setting_get_frequency_count(app->setting), - weather_station_scene_receiver_config_set_frequency, - app); - value_index = - weather_station_scene_receiver_config_next_frequency(app->txrx->preset->frequency, app); - scene_manager_set_scene_state( - app->scene_manager, WeatherStationSceneReceiverConfig, (uint32_t)item); - variable_item_set_current_value_index(item, value_index); - char text_buf[10] = {0}; - snprintf( - text_buf, - sizeof(text_buf), - "%lu.%02lu", - subghz_setting_get_frequency(app->setting, value_index) / 1000000, - (subghz_setting_get_frequency(app->setting, value_index) % 1000000) / 10000); - variable_item_set_current_value_text(item, text_buf); - - item = variable_item_list_add( - app->variable_item_list, - "Hopping:", - HOPPING_COUNT, - weather_station_scene_receiver_config_set_hopping_running, - app); - value_index = weather_station_scene_receiver_config_hopper_value_index( - app->txrx->hopper_state, hopping_value, HOPPING_COUNT, app); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, hopping_text[value_index]); - - item = variable_item_list_add( - app->variable_item_list, - "Modulation:", - subghz_setting_get_preset_count(app->setting), - weather_station_scene_receiver_config_set_preset, - app); - value_index = weather_station_scene_receiver_config_next_preset( - furi_string_get_cstr(app->txrx->preset->name), app); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text( - item, subghz_setting_get_preset_name(app->setting, value_index)); - - variable_item_list_add(app->variable_item_list, "Lock Keyboard", 1, NULL, NULL); - variable_item_list_set_enter_callback( - app->variable_item_list, - weather_station_scene_receiver_config_var_list_enter_callback, - app); - - view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewVariableItemList); -} - -bool weather_station_scene_receiver_config_on_event(void* context, SceneManagerEvent event) { - WeatherStationApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == WSCustomEventSceneSettingLock) { - app->lock = WSLockOn; - scene_manager_previous_scene(app->scene_manager); - consumed = true; - } - } - return consumed; -} - -void weather_station_scene_receiver_config_on_exit(void* context) { - WeatherStationApp* app = context; - variable_item_list_set_selected_item(app->variable_item_list, 0); - variable_item_list_reset(app->variable_item_list); -} diff --git a/applications/external/weather_station/scenes/weather_station_scene_receiver_info.c b/applications/external/weather_station/scenes/weather_station_scene_receiver_info.c deleted file mode 100644 index b26661be3..000000000 --- a/applications/external/weather_station/scenes/weather_station_scene_receiver_info.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "../weather_station_app_i.h" -#include "../views/weather_station_receiver.h" - -void weather_station_scene_receiver_info_callback(WSCustomEvent event, void* context) { - furi_assert(context); - WeatherStationApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -static void weather_station_scene_receiver_info_add_to_history_callback( - SubGhzReceiver* receiver, - SubGhzProtocolDecoderBase* decoder_base, - void* context) { - furi_assert(context); - WeatherStationApp* app = context; - - if(ws_history_add_to_history(app->txrx->history, decoder_base, app->txrx->preset) == - WSHistoryStateAddKeyUpdateData) { - ws_view_receiver_info_update( - app->ws_receiver_info, - ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen)); - subghz_receiver_reset(receiver); - - notification_message(app->notifications, &sequence_blink_green_10); - app->txrx->rx_key_state = WSRxKeyStateAddKey; - } -} - -void weather_station_scene_receiver_info_on_enter(void* context) { - WeatherStationApp* app = context; - - subghz_receiver_set_rx_callback( - app->txrx->receiver, weather_station_scene_receiver_info_add_to_history_callback, app); - ws_view_receiver_info_update( - app->ws_receiver_info, - ws_history_get_raw_data(app->txrx->history, app->txrx->idx_menu_chosen)); - view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewReceiverInfo); -} - -bool weather_station_scene_receiver_info_on_event(void* context, SceneManagerEvent event) { - WeatherStationApp* app = context; - bool consumed = false; - UNUSED(app); - UNUSED(event); - return consumed; -} - -void weather_station_scene_receiver_info_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/weather_station/scenes/weather_station_scene_start.c b/applications/external/weather_station/scenes/weather_station_scene_start.c deleted file mode 100644 index 56dd6fa86..000000000 --- a/applications/external/weather_station/scenes/weather_station_scene_start.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../weather_station_app_i.h" - -typedef enum { - SubmenuIndexWeatherStationReceiver, - SubmenuIndexWeatherStationAbout, -} SubmenuIndex; - -void weather_station_scene_start_submenu_callback(void* context, uint32_t index) { - WeatherStationApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void weather_station_scene_start_on_enter(void* context) { - UNUSED(context); - WeatherStationApp* app = context; - Submenu* submenu = app->submenu; - - submenu_add_item( - submenu, - "Read Weather Station", - SubmenuIndexWeatherStationReceiver, - weather_station_scene_start_submenu_callback, - app); - submenu_add_item( - submenu, - "About", - SubmenuIndexWeatherStationAbout, - weather_station_scene_start_submenu_callback, - app); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, WeatherStationSceneStart)); - - view_dispatcher_switch_to_view(app->view_dispatcher, WeatherStationViewSubmenu); -} - -bool weather_station_scene_start_on_event(void* context, SceneManagerEvent event) { - WeatherStationApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexWeatherStationAbout) { - scene_manager_next_scene(app->scene_manager, WeatherStationSceneAbout); - consumed = true; - } else if(event.event == SubmenuIndexWeatherStationReceiver) { - scene_manager_next_scene(app->scene_manager, WeatherStationSceneReceiver); - consumed = true; - } - scene_manager_set_scene_state(app->scene_manager, WeatherStationSceneStart, event.event); - } - - return consumed; -} - -void weather_station_scene_start_on_exit(void* context) { - WeatherStationApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/external/weather_station/views/weather_station_receiver.c b/applications/external/weather_station/views/weather_station_receiver.c deleted file mode 100644 index c4ce1627e..000000000 --- a/applications/external/weather_station/views/weather_station_receiver.c +++ /dev/null @@ -1,464 +0,0 @@ -#include "weather_station_receiver.h" -#include "../weather_station_app_i.h" -#include -#include - -#include -#include -#include - -#define FRAME_HEIGHT 12 -#define MAX_LEN_PX 112 -#define MENU_ITEMS 4u -#define UNLOCK_CNT 3 - -#define SUBGHZ_RAW_THRESHOLD_MIN -90.0f -typedef struct { - FuriString* item_str; - uint8_t type; -} WSReceiverMenuItem; - -ARRAY_DEF(WSReceiverMenuItemArray, WSReceiverMenuItem, M_POD_OPLIST) - -#define M_OPL_WSReceiverMenuItemArray_t() ARRAY_OPLIST(WSReceiverMenuItemArray, M_POD_OPLIST) - -struct WSReceiverHistory { - WSReceiverMenuItemArray_t data; -}; - -typedef struct WSReceiverHistory WSReceiverHistory; - -static const Icon* ReceiverItemIcons[] = { - [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, - [SubGhzProtocolTypeStatic] = &I_Unlock_7x8, - [SubGhzProtocolTypeDynamic] = &I_Lock_7x8, - [SubGhzProtocolWeatherStation] = &I_station_icon, -}; - -typedef enum { - WSReceiverBarShowDefault, - WSReceiverBarShowLock, - WSReceiverBarShowToUnlockPress, - WSReceiverBarShowUnlock, -} WSReceiverBarShow; - -struct WSReceiver { - WSLock lock; - uint8_t lock_count; - FuriTimer* timer; - View* view; - WSReceiverCallback callback; - void* context; -}; - -typedef struct { - FuriString* frequency_str; - FuriString* preset_str; - FuriString* history_stat_str; - WSReceiverHistory* history; - uint16_t idx; - uint16_t list_offset; - uint16_t history_item; - WSReceiverBarShow bar_show; - uint8_t u_rssi; -} WSReceiverModel; - -void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi) { - furi_assert(instance); - with_view_model( - instance->view, - WSReceiverModel * model, - { - if(rssi < SUBGHZ_RAW_THRESHOLD_MIN) { - model->u_rssi = 0; - } else { - model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_THRESHOLD_MIN); - } - }, - true); -} - -void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock lock) { - furi_assert(ws_receiver); - ws_receiver->lock_count = 0; - if(lock == WSLockOn) { - ws_receiver->lock = lock; - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { model->bar_show = WSReceiverBarShowLock; }, - true); - furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000)); - } else { - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { model->bar_show = WSReceiverBarShowDefault; }, - true); - } -} - -void ws_view_receiver_set_callback( - WSReceiver* ws_receiver, - WSReceiverCallback callback, - void* context) { - furi_assert(ws_receiver); - furi_assert(callback); - ws_receiver->callback = callback; - ws_receiver->context = context; -} - -static void ws_view_receiver_update_offset(WSReceiver* ws_receiver) { - furi_assert(ws_receiver); - - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - size_t history_item = model->history_item; - uint16_t bounds = history_item > 3 ? 2 : history_item; - - if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) { - model->list_offset = model->idx - 3; - } else if(model->list_offset < model->idx - bounds) { - model->list_offset = - CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0); - } else if(model->list_offset > model->idx - bounds) { - model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0); - } - }, - true); -} - -void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type) { - furi_assert(ws_receiver); - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - WSReceiverMenuItem* item_menu = WSReceiverMenuItemArray_push_raw(model->history->data); - item_menu->item_str = furi_string_alloc_set(name); - item_menu->type = type; - if((model->idx == model->history_item - 1)) { - model->history_item++; - model->idx++; - } else { - model->history_item++; - } - }, - true); - ws_view_receiver_update_offset(ws_receiver); -} - -void ws_view_receiver_add_data_statusbar( - WSReceiver* ws_receiver, - const char* frequency_str, - const char* preset_str, - const char* history_stat_str) { - furi_assert(ws_receiver); - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - furi_string_set_str(model->frequency_str, frequency_str); - furi_string_set_str(model->preset_str, preset_str); - furi_string_set_str(model->history_stat_str, history_stat_str); - }, - true); -} - -static void ws_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT); - - canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1); - - canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT); - canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); -} - -static void ws_view_rssi_draw(Canvas* canvas, WSReceiverModel* model) { - for(uint8_t i = 1; i < model->u_rssi; i++) { - if(i % 5) { - canvas_draw_dot(canvas, 46 + i, 50); - canvas_draw_dot(canvas, 47 + i, 51); - canvas_draw_dot(canvas, 46 + i, 52); - } - } -} - -void ws_view_receiver_draw(Canvas* canvas, WSReceiverModel* model) { - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - - elements_button_left(canvas, "Config"); - - bool scrollbar = model->history_item > 4; - FuriString* str_buff; - str_buff = furi_string_alloc(); - - WSReceiverMenuItem* item_menu; - - for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) { - size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0); - item_menu = WSReceiverMenuItemArray_get(model->history->data, idx); - furi_string_set(str_buff, item_menu->item_str); - elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX); - if(model->idx == idx) { - ws_view_receiver_draw_frame(canvas, i, scrollbar); - } else { - canvas_set_color(canvas, ColorBlack); - } - canvas_draw_icon(canvas, 4, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]); - canvas_draw_str(canvas, 14, 9 + i * FRAME_HEIGHT, furi_string_get_cstr(str_buff)); - furi_string_reset(str_buff); - } - if(scrollbar) { - elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item); - } - furi_string_free(str_buff); - - canvas_set_color(canvas, ColorBlack); - - if(model->history_item == 0) { - canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 63, 46, "Scanning..."); - canvas_set_font(canvas, FontSecondary); - } - - // Draw RSSI - ws_view_rssi_draw(canvas, model); - - switch(model->bar_show) { - case WSReceiverBarShowLock: - canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); - canvas_draw_str(canvas, 74, 62, "Locked"); - break; - case WSReceiverBarShowToUnlockPress: - canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); - canvas_set_font(canvas, FontSecondary); - elements_bold_rounded_frame(canvas, 14, 8, 99, 48); - elements_multiline_text(canvas, 65, 26, "To unlock\npress:"); - canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8); - canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8); - canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8); - canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42); - canvas_draw_dot(canvas, 17, 61); - break; - case WSReceiverBarShowUnlock: - canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8); - canvas_draw_str(canvas, 74, 62, "Unlocked"); - break; - default: - canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); - canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); - break; - } -} - -static void ws_view_receiver_timer_callback(void* context) { - furi_assert(context); - WSReceiver* ws_receiver = context; - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { model->bar_show = WSReceiverBarShowDefault; }, - true); - if(ws_receiver->lock_count < UNLOCK_CNT) { - ws_receiver->callback(WSCustomEventViewReceiverOffDisplay, ws_receiver->context); - } else { - ws_receiver->lock = WSLockOff; - ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context); - } - ws_receiver->lock_count = 0; -} - -bool ws_view_receiver_input(InputEvent* event, void* context) { - furi_assert(context); - WSReceiver* ws_receiver = context; - - if(ws_receiver->lock == WSLockOn) { - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { model->bar_show = WSReceiverBarShowToUnlockPress; }, - true); - if(ws_receiver->lock_count == 0) { - furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(1000)); - } - if(event->key == InputKeyBack && event->type == InputTypeShort) { - ws_receiver->lock_count++; - } - if(ws_receiver->lock_count >= UNLOCK_CNT) { - ws_receiver->callback(WSCustomEventViewReceiverUnlock, ws_receiver->context); - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { model->bar_show = WSReceiverBarShowUnlock; }, - true); - ws_receiver->lock = WSLockOff; - furi_timer_start(ws_receiver->timer, pdMS_TO_TICKS(650)); - } - - return true; - } - - if(event->key == InputKeyBack && event->type == InputTypeShort) { - ws_receiver->callback(WSCustomEventViewReceiverBack, ws_receiver->context); - } else if( - event->key == InputKeyUp && - (event->type == InputTypeShort || event->type == InputTypeRepeat)) { - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - if(model->idx != 0) model->idx--; - }, - true); - } else if( - event->key == InputKeyDown && - (event->type == InputTypeShort || event->type == InputTypeRepeat)) { - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - if(model->history_item && model->idx != model->history_item - 1) model->idx++; - }, - true); - } else if(event->key == InputKeyLeft && event->type == InputTypeShort) { - ws_receiver->callback(WSCustomEventViewReceiverConfig, ws_receiver->context); - } else if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - if(model->history_item != 0) { - ws_receiver->callback(WSCustomEventViewReceiverOK, ws_receiver->context); - } - }, - false); - } - - ws_view_receiver_update_offset(ws_receiver); - - return true; -} - -void ws_view_receiver_enter(void* context) { - furi_assert(context); -} - -void ws_view_receiver_exit(void* context) { - furi_assert(context); - WSReceiver* ws_receiver = context; - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - furi_string_reset(model->frequency_str); - furi_string_reset(model->preset_str); - furi_string_reset(model->history_stat_str); - for - M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) { - furi_string_free(item_menu->item_str); - item_menu->type = 0; - } - WSReceiverMenuItemArray_reset(model->history->data); - model->idx = 0; - model->list_offset = 0; - model->history_item = 0; - }, - false); - furi_timer_stop(ws_receiver->timer); -} - -WSReceiver* ws_view_receiver_alloc() { - WSReceiver* ws_receiver = malloc(sizeof(WSReceiver)); - - // View allocation and configuration - ws_receiver->view = view_alloc(); - - ws_receiver->lock = WSLockOff; - ws_receiver->lock_count = 0; - view_allocate_model(ws_receiver->view, ViewModelTypeLocking, sizeof(WSReceiverModel)); - view_set_context(ws_receiver->view, ws_receiver); - view_set_draw_callback(ws_receiver->view, (ViewDrawCallback)ws_view_receiver_draw); - view_set_input_callback(ws_receiver->view, ws_view_receiver_input); - view_set_enter_callback(ws_receiver->view, ws_view_receiver_enter); - view_set_exit_callback(ws_receiver->view, ws_view_receiver_exit); - - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - model->frequency_str = furi_string_alloc(); - model->preset_str = furi_string_alloc(); - model->history_stat_str = furi_string_alloc(); - model->bar_show = WSReceiverBarShowDefault; - model->history = malloc(sizeof(WSReceiverHistory)); - WSReceiverMenuItemArray_init(model->history->data); - }, - true); - ws_receiver->timer = - furi_timer_alloc(ws_view_receiver_timer_callback, FuriTimerTypeOnce, ws_receiver); - return ws_receiver; -} - -void ws_view_receiver_free(WSReceiver* ws_receiver) { - furi_assert(ws_receiver); - - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - furi_string_free(model->frequency_str); - furi_string_free(model->preset_str); - furi_string_free(model->history_stat_str); - for - M_EACH(item_menu, model->history->data, WSReceiverMenuItemArray_t) { - furi_string_free(item_menu->item_str); - item_menu->type = 0; - } - WSReceiverMenuItemArray_clear(model->history->data); - free(model->history); - }, - false); - furi_timer_free(ws_receiver->timer); - view_free(ws_receiver->view); - free(ws_receiver); -} - -View* ws_view_receiver_get_view(WSReceiver* ws_receiver) { - furi_assert(ws_receiver); - return ws_receiver->view; -} - -uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver) { - furi_assert(ws_receiver); - uint32_t idx = 0; - with_view_model( - ws_receiver->view, WSReceiverModel * model, { idx = model->idx; }, false); - return idx; -} - -void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx) { - furi_assert(ws_receiver); - with_view_model( - ws_receiver->view, - WSReceiverModel * model, - { - model->idx = idx; - if(model->idx > 2) model->list_offset = idx - 2; - }, - true); - ws_view_receiver_update_offset(ws_receiver); -} diff --git a/applications/external/weather_station/views/weather_station_receiver.h b/applications/external/weather_station/views/weather_station_receiver.h deleted file mode 100644 index f81aa1f5e..000000000 --- a/applications/external/weather_station/views/weather_station_receiver.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include "../helpers/weather_station_types.h" -#include "../helpers/weather_station_event.h" - -typedef struct WSReceiver WSReceiver; - -typedef void (*WSReceiverCallback)(WSCustomEvent event, void* context); - -void ws_view_receiver_set_rssi(WSReceiver* instance, float rssi); - -void ws_view_receiver_set_lock(WSReceiver* ws_receiver, WSLock keyboard); - -void ws_view_receiver_set_callback( - WSReceiver* ws_receiver, - WSReceiverCallback callback, - void* context); - -WSReceiver* ws_view_receiver_alloc(); - -void ws_view_receiver_free(WSReceiver* ws_receiver); - -View* ws_view_receiver_get_view(WSReceiver* ws_receiver); - -void ws_view_receiver_add_data_statusbar( - WSReceiver* ws_receiver, - const char* frequency_str, - const char* preset_str, - const char* history_stat_str); - -void ws_view_receiver_add_item_to_menu(WSReceiver* ws_receiver, const char* name, uint8_t type); - -uint16_t ws_view_receiver_get_idx_menu(WSReceiver* ws_receiver); - -void ws_view_receiver_set_idx_menu(WSReceiver* ws_receiver, uint16_t idx); - -void ws_view_receiver_exit(void* context); diff --git a/applications/external/weather_station/views/weather_station_receiver_info.c b/applications/external/weather_station/views/weather_station_receiver_info.c deleted file mode 100644 index b3b3f2193..000000000 --- a/applications/external/weather_station/views/weather_station_receiver_info.c +++ /dev/null @@ -1,245 +0,0 @@ -#include "weather_station_receiver.h" -#include "../weather_station_app_i.h" -#include "weather_station_icons.h" -#include "../protocols/ws_generic.h" -#include -#include -#include - -struct WSReceiverInfo { - View* view; - FuriTimer* timer; -}; - -typedef struct { - uint32_t curr_ts; - FuriString* protocol_name; - WSBlockGeneric* generic; -} WSReceiverInfoModel; - -void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff) { - furi_assert(ws_receiver_info); - furi_assert(fff); - - with_view_model( - ws_receiver_info->view, - WSReceiverInfoModel * model, - { - flipper_format_rewind(fff); - flipper_format_read_string(fff, "Protocol", model->protocol_name); - - ws_block_generic_deserialize(model->generic, fff); - - FuriHalRtcDateTime curr_dt; - furi_hal_rtc_get_datetime(&curr_dt); - model->curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); - }, - true); -} - -void ws_view_receiver_info_draw(Canvas* canvas, WSReceiverInfoModel* model) { - char buffer[64]; - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontSecondary); - - snprintf( - buffer, - sizeof(buffer), - "%s %db", - furi_string_get_cstr(model->protocol_name), - model->generic->data_count_bit); - canvas_draw_str(canvas, 0, 8, buffer); - - if(model->generic->channel != WS_NO_CHANNEL) { - snprintf(buffer, sizeof(buffer), "Ch: %01d", model->generic->channel); - canvas_draw_str(canvas, 106, 8, buffer); - } - - if(model->generic->id != WS_NO_ID) { - snprintf(buffer, sizeof(buffer), "Sn: 0x%02lX", model->generic->id); - canvas_draw_str(canvas, 0, 20, buffer); - } - - if(model->generic->btn != WS_NO_BTN) { - snprintf(buffer, sizeof(buffer), "Btn: %01d", model->generic->btn); - canvas_draw_str(canvas, 57, 20, buffer); - } - - if(model->generic->battery_low != WS_NO_BATT) { - snprintf( - buffer, sizeof(buffer), "Batt: %s", (!model->generic->battery_low ? "ok" : "low")); - canvas_draw_str_aligned(canvas, 126, 17, AlignRight, AlignCenter, buffer); - } - - snprintf(buffer, sizeof(buffer), "Data: 0x%llX", model->generic->data); - canvas_draw_str(canvas, 0, 32, buffer); - - elements_bold_rounded_frame(canvas, 0, 38, 127, 25); - canvas_set_font(canvas, FontPrimary); - - if(!float_is_equal(model->generic->temp, WS_NO_TEMPERATURE)) { - canvas_draw_icon(canvas, 6, 43, &I_Therm_7x16); - - uint8_t temp_x1 = 0; - uint8_t temp_x2 = 0; - if(furi_hal_rtc_get_locale_units() == FuriHalRtcLocaleUnitsMetric) { - snprintf(buffer, sizeof(buffer), "%3.1f C", (double)model->generic->temp); - if(model->generic->temp < -9.0f) { - temp_x1 = 49; - temp_x2 = 40; - } else { - temp_x1 = 47; - temp_x2 = 38; - } - } else { - snprintf( - buffer, - sizeof(buffer), - "%3.1f F", - (double)locale_celsius_to_fahrenheit(model->generic->temp)); - if((model->generic->temp < -27.77f) || (model->generic->temp > 37.77f)) { - temp_x1 = 50; - temp_x2 = 42; - } else { - temp_x1 = 48; - temp_x2 = 40; - } - } - - canvas_draw_str_aligned(canvas, temp_x1, 47, AlignRight, AlignTop, buffer); - canvas_draw_circle(canvas, temp_x2, 46, 1); - } - - if(model->generic->humidity != WS_NO_HUMIDITY) { - canvas_draw_icon(canvas, 53, 44, &I_Humid_8x13); - snprintf(buffer, sizeof(buffer), "%d%%", model->generic->humidity); - canvas_draw_str(canvas, 64, 55, buffer); - } - - if((int)model->generic->timestamp > 0 && model->curr_ts) { - int ts_diff = (int)model->curr_ts - (int)model->generic->timestamp; - - canvas_draw_icon(canvas, 91, 46, &I_Timer_11x11); - - if(ts_diff > 60) { - int tmp_sec = ts_diff; - int cnt_min = 1; - for(int i = 1; tmp_sec > 60; i++) { - tmp_sec = tmp_sec - 60; - cnt_min = i; - } - - if(model->curr_ts % 2 == 0) { - canvas_draw_str_aligned(canvas, 105, 51, AlignLeft, AlignCenter, "Old"); - } else { - if(cnt_min >= 59) { - canvas_draw_str_aligned(canvas, 105, 51, AlignLeft, AlignCenter, "Old"); - } else { - snprintf(buffer, sizeof(buffer), "%dm", cnt_min); - canvas_draw_str_aligned(canvas, 114, 51, AlignCenter, AlignCenter, buffer); - } - } - - } else { - snprintf(buffer, sizeof(buffer), "%d", ts_diff); - canvas_draw_str_aligned(canvas, 112, 51, AlignCenter, AlignCenter, buffer); - } - } -} - -bool ws_view_receiver_info_input(InputEvent* event, void* context) { - furi_assert(context); - //WSReceiverInfo* ws_receiver_info = context; - - if(event->key == InputKeyBack) { - return false; - } - - return true; -} - -static void ws_view_receiver_info_enter(void* context) { - furi_assert(context); - WSReceiverInfo* ws_receiver_info = context; - - furi_timer_start(ws_receiver_info->timer, 1000); -} - -static void ws_view_receiver_info_exit(void* context) { - furi_assert(context); - WSReceiverInfo* ws_receiver_info = context; - - furi_timer_stop(ws_receiver_info->timer); - - with_view_model( - ws_receiver_info->view, - WSReceiverInfoModel * model, - { furi_string_reset(model->protocol_name); }, - false); -} - -static void ws_view_receiver_info_timer(void* context) { - WSReceiverInfo* ws_receiver_info = context; - // Force redraw - with_view_model( - ws_receiver_info->view, - WSReceiverInfoModel * model, - { - FuriHalRtcDateTime curr_dt; - furi_hal_rtc_get_datetime(&curr_dt); - model->curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); - }, - true); -} - -WSReceiverInfo* ws_view_receiver_info_alloc() { - WSReceiverInfo* ws_receiver_info = malloc(sizeof(WSReceiverInfo)); - - // View allocation and configuration - ws_receiver_info->view = view_alloc(); - - view_allocate_model(ws_receiver_info->view, ViewModelTypeLocking, sizeof(WSReceiverInfoModel)); - view_set_context(ws_receiver_info->view, ws_receiver_info); - view_set_draw_callback(ws_receiver_info->view, (ViewDrawCallback)ws_view_receiver_info_draw); - view_set_input_callback(ws_receiver_info->view, ws_view_receiver_info_input); - view_set_enter_callback(ws_receiver_info->view, ws_view_receiver_info_enter); - view_set_exit_callback(ws_receiver_info->view, ws_view_receiver_info_exit); - - with_view_model( - ws_receiver_info->view, - WSReceiverInfoModel * model, - { - model->generic = malloc(sizeof(WSBlockGeneric)); - model->protocol_name = furi_string_alloc(); - }, - true); - - ws_receiver_info->timer = - furi_timer_alloc(ws_view_receiver_info_timer, FuriTimerTypePeriodic, ws_receiver_info); - - return ws_receiver_info; -} - -void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info) { - furi_assert(ws_receiver_info); - - furi_timer_free(ws_receiver_info->timer); - - with_view_model( - ws_receiver_info->view, - WSReceiverInfoModel * model, - { - furi_string_free(model->protocol_name); - free(model->generic); - }, - false); - - view_free(ws_receiver_info->view); - free(ws_receiver_info); -} - -View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info) { - furi_assert(ws_receiver_info); - return ws_receiver_info->view; -} diff --git a/applications/external/weather_station/views/weather_station_receiver_info.h b/applications/external/weather_station/views/weather_station_receiver_info.h deleted file mode 100644 index 705434a23..000000000 --- a/applications/external/weather_station/views/weather_station_receiver_info.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include "../helpers/weather_station_types.h" -#include "../helpers/weather_station_event.h" -#include - -typedef struct WSReceiverInfo WSReceiverInfo; - -void ws_view_receiver_info_update(WSReceiverInfo* ws_receiver_info, FlipperFormat* fff); - -WSReceiverInfo* ws_view_receiver_info_alloc(); - -void ws_view_receiver_info_free(WSReceiverInfo* ws_receiver_info); - -View* ws_view_receiver_info_get_view(WSReceiverInfo* ws_receiver_info); diff --git a/applications/external/weather_station/weather_station_10px.png b/applications/external/weather_station/weather_station_10px.png deleted file mode 100644 index 7d5cc318c369f583c531ca5e8846a9a498ec44b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ihk44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcfQS24TkI`72U@f-asejv*Ssy?udP3<@01TYulb=@zZ(cJX(> z9E<0IiRH6?9buO;SoeAj)2m&2)(%T&iEpuE4*KbLE`4W{EU(#4ulkv@OZPmG6Pd23 R{2OQ -#include -#include "protocols/protocol_items.h" - -static bool weather_station_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - WeatherStationApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool weather_station_app_back_event_callback(void* context) { - furi_assert(context); - WeatherStationApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void weather_station_app_tick_event_callback(void* context) { - furi_assert(context); - WeatherStationApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -WeatherStationApp* weather_station_app_alloc() { - WeatherStationApp* app = malloc(sizeof(WeatherStationApp)); - - // GUI - app->gui = furi_record_open(RECORD_GUI); - - // View Dispatcher - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&weather_station_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, weather_station_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, weather_station_app_back_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, weather_station_app_tick_event_callback, 100); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - // Open Notification record - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - // Variable Item List - app->variable_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - WeatherStationViewVariableItemList, - variable_item_list_get_view(app->variable_item_list)); - - // SubMenu - app->submenu = submenu_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, WeatherStationViewSubmenu, submenu_get_view(app->submenu)); - - // Widget - app->widget = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, WeatherStationViewWidget, widget_get_view(app->widget)); - - // Receiver - app->ws_receiver = ws_view_receiver_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - WeatherStationViewReceiver, - ws_view_receiver_get_view(app->ws_receiver)); - - // Receiver Info - app->ws_receiver_info = ws_view_receiver_info_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - WeatherStationViewReceiverInfo, - ws_view_receiver_info_get_view(app->ws_receiver_info)); - - //init setting - app->setting = subghz_setting_alloc(); - - //ToDo FIX file name setting - subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); - - //init Worker & Protocol & History - app->lock = WSLockOff; - app->txrx = malloc(sizeof(WeatherStationTxRx)); - app->txrx->preset = malloc(sizeof(SubGhzRadioPreset)); - app->txrx->preset->name = furi_string_alloc(); - ws_preset_init(app, "AM650", subghz_setting_get_default_frequency(app->setting), NULL, 0); - - app->txrx->hopper_state = WSHopperStateOFF; - app->txrx->history = ws_history_alloc(); - app->txrx->worker = subghz_worker_alloc(); - app->txrx->environment = subghz_environment_alloc(); - subghz_environment_set_protocol_registry( - app->txrx->environment, (void*)&weather_station_protocol_registry); - app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment); - - subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable); - subghz_worker_set_overrun_callback( - app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); - subghz_worker_set_pair_callback( - app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); - subghz_worker_set_context(app->txrx->worker, app->txrx->receiver); - - furi_hal_power_suppress_charge_enter(); - - scene_manager_next_scene(app->scene_manager, WeatherStationSceneStart); - - return app; -} - -void weather_station_app_free(WeatherStationApp* app) { - furi_assert(app); - - //CC1101 off - ws_sleep(app); - - // Submenu - view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewSubmenu); - submenu_free(app->submenu); - - // Variable Item List - view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewVariableItemList); - variable_item_list_free(app->variable_item_list); - - // Widget - view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewWidget); - widget_free(app->widget); - - // Receiver - view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiver); - ws_view_receiver_free(app->ws_receiver); - - // Receiver Info - view_dispatcher_remove_view(app->view_dispatcher, WeatherStationViewReceiverInfo); - ws_view_receiver_info_free(app->ws_receiver_info); - - //setting - subghz_setting_free(app->setting); - - //Worker & Protocol & History - subghz_receiver_free(app->txrx->receiver); - subghz_environment_free(app->txrx->environment); - ws_history_free(app->txrx->history); - subghz_worker_free(app->txrx->worker); - furi_string_free(app->txrx->preset->name); - free(app->txrx->preset); - free(app->txrx); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Notifications - furi_record_close(RECORD_NOTIFICATION); - app->notifications = NULL; - - // Close records - furi_record_close(RECORD_GUI); - - furi_hal_power_suppress_charge_exit(); - - free(app); -} - -int32_t weather_station_app(void* p) { - UNUSED(p); - WeatherStationApp* weather_station_app = weather_station_app_alloc(); - - view_dispatcher_run(weather_station_app->view_dispatcher); - - weather_station_app_free(weather_station_app); - - return 0; -} diff --git a/applications/external/weather_station/weather_station_app_i.c b/applications/external/weather_station/weather_station_app_i.c deleted file mode 100644 index 712634a2c..000000000 --- a/applications/external/weather_station/weather_station_app_i.c +++ /dev/null @@ -1,156 +0,0 @@ -#include "weather_station_app_i.h" - -#define TAG "WeatherStation" -#include - -void ws_preset_init( - void* context, - const char* preset_name, - uint32_t frequency, - uint8_t* preset_data, - size_t preset_data_size) { - furi_assert(context); - WeatherStationApp* app = context; - furi_string_set(app->txrx->preset->name, preset_name); - app->txrx->preset->frequency = frequency; - app->txrx->preset->data = preset_data; - app->txrx->preset->data_size = preset_data_size; -} - -bool ws_set_preset(WeatherStationApp* app, const char* preset) { - if(!strcmp(preset, "FuriHalSubGhzPresetOok270Async")) { - furi_string_set(app->txrx->preset->name, "AM270"); - } else if(!strcmp(preset, "FuriHalSubGhzPresetOok650Async")) { - furi_string_set(app->txrx->preset->name, "AM650"); - } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev238Async")) { - furi_string_set(app->txrx->preset->name, "FM238"); - } else if(!strcmp(preset, "FuriHalSubGhzPreset2FSKDev476Async")) { - furi_string_set(app->txrx->preset->name, "FM476"); - } else if(!strcmp(preset, "FuriHalSubGhzPresetCustom")) { - furi_string_set(app->txrx->preset->name, "CUSTOM"); - } else { - FURI_LOG_E(TAG, "Unknown preset"); - return false; - } - return true; -} - -void ws_get_frequency_modulation( - WeatherStationApp* app, - FuriString* frequency, - FuriString* modulation) { - furi_assert(app); - if(frequency != NULL) { - furi_string_printf( - frequency, - "%03ld.%02ld", - app->txrx->preset->frequency / 1000000 % 1000, - app->txrx->preset->frequency / 10000 % 100); - } - if(modulation != NULL) { - furi_string_printf(modulation, "%.2s", furi_string_get_cstr(app->txrx->preset->name)); - } -} - -void ws_begin(WeatherStationApp* app, uint8_t* preset_data) { - furi_assert(app); - UNUSED(preset_data); - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - furi_hal_subghz_load_custom_preset(preset_data); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); - app->txrx->txrx_state = WSTxRxStateIDLE; -} - -uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency) { - furi_assert(app); - if(!furi_hal_subghz_is_frequency_valid(frequency)) { - furi_crash("WeatherStation: Incorrect RX frequency."); - } - furi_assert( - app->txrx->txrx_state != WSTxRxStateRx && app->txrx->txrx_state != WSTxRxStateSleep); - - furi_hal_subghz_idle(); - uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); - - furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker); - subghz_worker_start(app->txrx->worker); - app->txrx->txrx_state = WSTxRxStateRx; - return value; -} - -void ws_idle(WeatherStationApp* app) { - furi_assert(app); - furi_assert(app->txrx->txrx_state != WSTxRxStateSleep); - furi_hal_subghz_idle(); - app->txrx->txrx_state = WSTxRxStateIDLE; -} - -void ws_rx_end(WeatherStationApp* app) { - furi_assert(app); - furi_assert(app->txrx->txrx_state == WSTxRxStateRx); - if(subghz_worker_is_running(app->txrx->worker)) { - subghz_worker_stop(app->txrx->worker); - furi_hal_subghz_stop_async_rx(); - } - furi_hal_subghz_idle(); - app->txrx->txrx_state = WSTxRxStateIDLE; -} - -void ws_sleep(WeatherStationApp* app) { - furi_assert(app); - furi_hal_subghz_sleep(); - app->txrx->txrx_state = WSTxRxStateSleep; -} - -void ws_hopper_update(WeatherStationApp* app) { - furi_assert(app); - - switch(app->txrx->hopper_state) { - case WSHopperStateOFF: - case WSHopperStatePause: - return; - case WSHopperStateRSSITimeOut: - if(app->txrx->hopper_timeout != 0) { - app->txrx->hopper_timeout--; - return; - } - break; - default: - break; - } - float rssi = -127.0f; - if(app->txrx->hopper_state != WSHopperStateRSSITimeOut) { - // See RSSI Calculation timings in CC1101 17.3 RSSI - rssi = furi_hal_subghz_get_rssi(); - - // Stay if RSSI is high enough - if(rssi > -90.0f) { - app->txrx->hopper_timeout = 10; - app->txrx->hopper_state = WSHopperStateRSSITimeOut; - return; - } - } else { - app->txrx->hopper_state = WSHopperStateRunnig; - } - // Select next frequency - if(app->txrx->hopper_idx_frequency < - subghz_setting_get_hopper_frequency_count(app->setting) - 1) { - app->txrx->hopper_idx_frequency++; - } else { - app->txrx->hopper_idx_frequency = 0; - } - - if(app->txrx->txrx_state == WSTxRxStateRx) { - ws_rx_end(app); - }; - if(app->txrx->txrx_state == WSTxRxStateIDLE) { - subghz_receiver_reset(app->txrx->receiver); - app->txrx->preset->frequency = - subghz_setting_get_hopper_frequency(app->setting, app->txrx->hopper_idx_frequency); - ws_rx(app, app->txrx->preset->frequency); - } -} diff --git a/applications/external/weather_station/weather_station_app_i.h b/applications/external/weather_station/weather_station_app_i.h deleted file mode 100644 index 41e248112..000000000 --- a/applications/external/weather_station/weather_station_app_i.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "helpers/weather_station_types.h" - -#include "scenes/weather_station_scene.h" -#include -#include -#include -#include -#include -#include -#include -#include "views/weather_station_receiver.h" -#include "views/weather_station_receiver_info.h" -#include "weather_station_history.h" - -#include -#include -#include -#include -#include - -typedef struct WeatherStationApp WeatherStationApp; - -struct WeatherStationTxRx { - SubGhzWorker* worker; - - SubGhzEnvironment* environment; - SubGhzReceiver* receiver; - SubGhzRadioPreset* preset; - WSHistory* history; - uint16_t idx_menu_chosen; - WSTxRxState txrx_state; - WSHopperState hopper_state; - uint8_t hopper_timeout; - uint8_t hopper_idx_frequency; - WSRxKeyState rx_key_state; -}; - -typedef struct WeatherStationTxRx WeatherStationTxRx; - -struct WeatherStationApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - WeatherStationTxRx* txrx; - SceneManager* scene_manager; - NotificationApp* notifications; - VariableItemList* variable_item_list; - Submenu* submenu; - Widget* widget; - WSReceiver* ws_receiver; - WSReceiverInfo* ws_receiver_info; - WSLock lock; - SubGhzSetting* setting; -}; - -void ws_preset_init( - void* context, - const char* preset_name, - uint32_t frequency, - uint8_t* preset_data, - size_t preset_data_size); -bool ws_set_preset(WeatherStationApp* app, const char* preset); -void ws_get_frequency_modulation( - WeatherStationApp* app, - FuriString* frequency, - FuriString* modulation); -void ws_begin(WeatherStationApp* app, uint8_t* preset_data); -uint32_t ws_rx(WeatherStationApp* app, uint32_t frequency); -void ws_idle(WeatherStationApp* app); -void ws_rx_end(WeatherStationApp* app); -void ws_sleep(WeatherStationApp* app); -void ws_hopper_update(WeatherStationApp* app); diff --git a/applications/external/weather_station/weather_station_history.c b/applications/external/weather_station/weather_station_history.c deleted file mode 100644 index 9adff39c6..000000000 --- a/applications/external/weather_station/weather_station_history.c +++ /dev/null @@ -1,245 +0,0 @@ -#include "weather_station_history.h" -#include -#include -#include -#include "protocols/ws_generic.h" - -#include - -#define WS_HISTORY_MAX 50 -#define TAG "WSHistory" - -typedef struct { - FuriString* item_str; - FlipperFormat* flipper_string; - uint8_t type; - uint32_t id; - SubGhzRadioPreset* preset; -} WSHistoryItem; - -ARRAY_DEF(WSHistoryItemArray, WSHistoryItem, M_POD_OPLIST) - -#define M_OPL_WSHistoryItemArray_t() ARRAY_OPLIST(WSHistoryItemArray, M_POD_OPLIST) - -typedef struct { - WSHistoryItemArray_t data; -} WSHistoryStruct; - -struct WSHistory { - uint32_t last_update_timestamp; - uint16_t last_index_write; - uint8_t code_last_hash_data; - FuriString* tmp_string; - WSHistoryStruct* history; -}; - -WSHistory* ws_history_alloc(void) { - WSHistory* instance = malloc(sizeof(WSHistory)); - instance->tmp_string = furi_string_alloc(); - instance->history = malloc(sizeof(WSHistoryStruct)); - WSHistoryItemArray_init(instance->history->data); - return instance; -} - -void ws_history_free(WSHistory* instance) { - furi_assert(instance); - furi_string_free(instance->tmp_string); - for - M_EACH(item, instance->history->data, WSHistoryItemArray_t) { - furi_string_free(item->item_str); - furi_string_free(item->preset->name); - free(item->preset); - flipper_format_free(item->flipper_string); - item->type = 0; - } - WSHistoryItemArray_clear(instance->history->data); - free(instance->history); - free(instance); -} - -uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx) { - furi_assert(instance); - WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); - return item->preset->frequency; -} - -SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx) { - furi_assert(instance); - WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); - return item->preset; -} - -const char* ws_history_get_preset(WSHistory* instance, uint16_t idx) { - furi_assert(instance); - WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); - return furi_string_get_cstr(item->preset->name); -} - -void ws_history_reset(WSHistory* instance) { - furi_assert(instance); - furi_string_reset(instance->tmp_string); - for - M_EACH(item, instance->history->data, WSHistoryItemArray_t) { - furi_string_free(item->item_str); - furi_string_free(item->preset->name); - free(item->preset); - flipper_format_free(item->flipper_string); - item->type = 0; - } - WSHistoryItemArray_reset(instance->history->data); - instance->last_index_write = 0; - instance->code_last_hash_data = 0; -} - -uint16_t ws_history_get_item(WSHistory* instance) { - furi_assert(instance); - return instance->last_index_write; -} - -uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx) { - furi_assert(instance); - WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); - return item->type; -} - -const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx) { - furi_assert(instance); - WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); - flipper_format_rewind(item->flipper_string); - if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { - FURI_LOG_E(TAG, "Missing Protocol"); - furi_string_reset(instance->tmp_string); - } - return furi_string_get_cstr(instance->tmp_string); -} - -FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx) { - furi_assert(instance); - WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); - if(item->flipper_string) { - return item->flipper_string; - } else { - return NULL; - } -} -bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output) { - furi_assert(instance); - if(instance->last_index_write == WS_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, "Memory is FULL"); - return true; - } - if(output != NULL) - furi_string_printf(output, "%02u/%02u", instance->last_index_write, WS_HISTORY_MAX); - return false; -} - -void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx) { - WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, idx); - furi_string_set(output, item->item_str); -} - -WSHistoryStateAddKey - ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset) { - furi_assert(instance); - furi_assert(context); - - if(instance->last_index_write >= WS_HISTORY_MAX) return WSHistoryStateAddKeyOverflow; - - SubGhzProtocolDecoderBase* decoder_base = context; - if((instance->code_last_hash_data == - subghz_protocol_decoder_base_get_hash_data(decoder_base)) && - ((furi_get_tick() - instance->last_update_timestamp) < 500)) { - instance->last_update_timestamp = furi_get_tick(); - return WSHistoryStateAddKeyTimeOut; - } - - instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); - instance->last_update_timestamp = furi_get_tick(); - - FlipperFormat* fff = flipper_format_string_alloc(); - uint32_t id = 0; - subghz_protocol_decoder_base_serialize(decoder_base, fff, preset); - - do { - if(!flipper_format_rewind(fff)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(fff, "Id", (uint32_t*)&id, 1)) { - FURI_LOG_E(TAG, "Missing Id"); - break; - } - } while(false); - flipper_format_free(fff); - - //Update record if found - bool sensor_found = false; - for(size_t i = 0; i < WSHistoryItemArray_size(instance->history->data); i++) { - WSHistoryItem* item = WSHistoryItemArray_get(instance->history->data, i); - if(item->id == id) { - sensor_found = true; - Stream* flipper_string_stream = flipper_format_get_raw_stream(item->flipper_string); - stream_clean(flipper_string_stream); - subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); - return WSHistoryStateAddKeyUpdateData; - } - } - - // or add new record - if(!sensor_found) { //-V547 - WSHistoryItem* item = WSHistoryItemArray_push_raw(instance->history->data); - item->preset = malloc(sizeof(SubGhzRadioPreset)); - item->type = decoder_base->protocol->type; - item->preset->frequency = preset->frequency; - item->preset->name = furi_string_alloc(); - furi_string_set(item->preset->name, preset->name); - item->preset->data = preset->data; - item->preset->data_size = preset->data_size; - item->id = id; - - item->item_str = furi_string_alloc(); - item->flipper_string = flipper_format_string_alloc(); - subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); - - do { - if(!flipper_format_rewind(item->flipper_string)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_string( - item->flipper_string, "Protocol", instance->tmp_string)) { - FURI_LOG_E(TAG, "Missing Protocol"); - break; - } - - if(!flipper_format_rewind(item->flipper_string)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - if(!flipper_format_read_hex(item->flipper_string, "Data", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Data"); - break; - } - uint64_t data = 0; - for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - data = (data << 8) | key_data[i]; - } - uint32_t temp_data = 0; - if(!flipper_format_read_uint32(item->flipper_string, "Ch", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Channel"); - break; - } - if(temp_data != WS_NO_CHANNEL) { - furi_string_cat_printf(instance->tmp_string, " Ch:%X", (uint8_t)temp_data); - } - - furi_string_printf( - item->item_str, "%s %llX", furi_string_get_cstr(instance->tmp_string), data); - - } while(false); - instance->last_index_write++; - return WSHistoryStateAddKeyNewDada; - } - return WSHistoryStateAddKeyUnknown; -} diff --git a/applications/external/weather_station/weather_station_history.h b/applications/external/weather_station/weather_station_history.h deleted file mode 100644 index 11601fe79..000000000 --- a/applications/external/weather_station/weather_station_history.h +++ /dev/null @@ -1,112 +0,0 @@ - -#pragma once - -#include -#include -#include -#include -#include - -typedef struct WSHistory WSHistory; - -/** History state add key */ -typedef enum { - WSHistoryStateAddKeyUnknown, - WSHistoryStateAddKeyTimeOut, - WSHistoryStateAddKeyNewDada, - WSHistoryStateAddKeyUpdateData, - WSHistoryStateAddKeyOverflow, -} WSHistoryStateAddKey; - -/** Allocate WSHistory - * - * @return WSHistory* - */ -WSHistory* ws_history_alloc(void); - -/** Free WSHistory - * - * @param instance - WSHistory instance - */ -void ws_history_free(WSHistory* instance); - -/** Clear history - * - * @param instance - WSHistory instance - */ -void ws_history_reset(WSHistory* instance); - -/** Get frequency to history[idx] - * - * @param instance - WSHistory instance - * @param idx - record index - * @return frequency - frequency Hz - */ -uint32_t ws_history_get_frequency(WSHistory* instance, uint16_t idx); - -SubGhzRadioPreset* ws_history_get_radio_preset(WSHistory* instance, uint16_t idx); - -/** Get preset to history[idx] - * - * @param instance - WSHistory instance - * @param idx - record index - * @return preset - preset name - */ -const char* ws_history_get_preset(WSHistory* instance, uint16_t idx); - -/** Get history index write - * - * @param instance - WSHistory instance - * @return idx - current record index - */ -uint16_t ws_history_get_item(WSHistory* instance); - -/** Get type protocol to history[idx] - * - * @param instance - WSHistory instance - * @param idx - record index - * @return type - type protocol - */ -uint8_t ws_history_get_type_protocol(WSHistory* instance, uint16_t idx); - -/** Get name protocol to history[idx] - * - * @param instance - WSHistory instance - * @param idx - record index - * @return name - const char* name protocol - */ -const char* ws_history_get_protocol_name(WSHistory* instance, uint16_t idx); - -/** Get string item menu to history[idx] - * - * @param instance - WSHistory instance - * @param output - FuriString* output - * @param idx - record index - */ -void ws_history_get_text_item_menu(WSHistory* instance, FuriString* output, uint16_t idx); - -/** Get string the remaining number of records to history - * - * @param instance - WSHistory instance - * @param output - FuriString* output - * @return bool - is FUUL - */ -bool ws_history_get_text_space_left(WSHistory* instance, FuriString* output); - -/** Add protocol to history - * - * @param instance - WSHistory instance - * @param context - SubGhzProtocolCommon context - * @param preset - SubGhzRadioPreset preset - * @return WSHistoryStateAddKey; - */ -WSHistoryStateAddKey - ws_history_add_to_history(WSHistory* instance, void* context, SubGhzRadioPreset* preset); - -/** Get SubGhzProtocolCommonLoad to load into the protocol decoder bin data - * - * @param instance - WSHistory instance - * @param idx - record index - * @return SubGhzProtocolCommonLoad* - */ -FlipperFormat* ws_history_get_raw_data(WSHistory* instance, uint16_t idx); diff --git a/applications/main/application.fam b/applications/main/application.fam index 0a90ee224..e3ceac34d 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -11,6 +11,9 @@ App( "subghz", "bad_usb", "u2f", + "clock", + "music_player", + "snake_game", "archive", "main_apps_on_start", ], diff --git a/applications/external/clock/application.fam b/applications/main/clock/application.fam similarity index 100% rename from applications/external/clock/application.fam rename to applications/main/clock/application.fam diff --git a/applications/external/clock/clock.png b/applications/main/clock/clock.png similarity index 100% rename from applications/external/clock/clock.png rename to applications/main/clock/clock.png diff --git a/applications/external/clock/clock_app.c b/applications/main/clock/clock_app.c similarity index 100% rename from applications/external/clock/clock_app.c rename to applications/main/clock/clock_app.c diff --git a/applications/external/music_player/application.fam b/applications/main/music_player/application.fam similarity index 100% rename from applications/external/music_player/application.fam rename to applications/main/music_player/application.fam diff --git a/applications/external/music_player/icons/music_10px.png b/applications/main/music_player/icons/music_10px.png similarity index 100% rename from applications/external/music_player/icons/music_10px.png rename to applications/main/music_player/icons/music_10px.png diff --git a/applications/external/music_player/music_player.c b/applications/main/music_player/music_player.c similarity index 100% rename from applications/external/music_player/music_player.c rename to applications/main/music_player/music_player.c diff --git a/applications/external/snake_game/application.fam b/applications/main/snake_game/application.fam similarity index 100% rename from applications/external/snake_game/application.fam rename to applications/main/snake_game/application.fam diff --git a/applications/external/snake_game/snake_10px.png b/applications/main/snake_game/snake_10px.png similarity index 100% rename from applications/external/snake_game/snake_10px.png rename to applications/main/snake_game/snake_10px.png diff --git a/applications/external/snake_game/snake_game.c b/applications/main/snake_game/snake_game.c similarity index 100% rename from applications/external/snake_game/snake_game.c rename to applications/main/snake_game/snake_game.c diff --git a/documentation/Doxyfile b/documentation/Doxyfile index bb43ce8a7..f31cbb9d8 100644 --- a/documentation/Doxyfile +++ b/documentation/Doxyfile @@ -939,8 +939,7 @@ EXCLUDE = \ lib/FreeRTOS-Kernel \ lib/microtar \ lib/mbedtls \ - lib/cxxheaderparser \ - applications/external/dap_link/lib/free-dap + lib/cxxheaderparser # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 776977cda..0d3b1f92e 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -226,7 +226,6 @@ vars.AddVariables( ("applications/settings", False), ("applications/system", False), ("applications/debug", False), - ("applications/external", False), ("applications/examples", False), ("applications/drivers", False), ("applications_user", False), From c976ff11bf18c26cc889b0f7d0b555a6972651a8 Mon Sep 17 00:00:00 2001 From: twisted-pear Date: Thu, 10 Aug 2023 10:44:46 +0200 Subject: [PATCH 12/99] Expose additional functions of the crypto engine to user (#2923) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow loading user supplied keys and add CTR mode * Add GCM mode to furi_hal_crypto * Split up CTR and GCM code, add flag for adv crypto * Add convenience functions for GCM crypto * Run fbt format * Update GCM to support additional auth data * Update APIs * FuriHal: update crypto documentation, method names and usage * Clean up code for key (un)loading, GCM and CTR - get rid of goto - do not use furi_hal_bt_is_alive() when not using secure enclave - give defines a type and wrap in () * Add unit test for CTR and GCM crypto * FuriHal: const in crypto unit tests, cortex timer for crypto operations timeouts * FuriHal: update crypto docs Co-authored-by: twisted_pear Co-authored-by: hedger Co-authored-by: あく --- .../furi_hal/furi_hal_crypto_tests.c | 602 ++++++++++++++++++ applications/debug/unit_tests/test_index.c | 2 + applications/main/u2f/u2f_data.c | 28 +- applications/services/crypto/crypto_cli.c | 22 +- firmware/targets/f18/api_symbols.csv | 18 +- firmware/targets/f7/api_symbols.csv | 18 +- .../targets/f7/furi_hal/furi_hal_crypto.c | 446 ++++++++++++- firmware/targets/f7/furi_hal/furi_hal_info.c | 2 +- .../furi_hal_include/furi_hal_crypto.h | 239 ++++++- lib/subghz/subghz_keystore.c | 16 +- 10 files changed, 1301 insertions(+), 92 deletions(-) create mode 100644 applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c new file mode 100644 index 000000000..b06d51130 --- /dev/null +++ b/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c @@ -0,0 +1,602 @@ +#include +#include +#include +#include "../minunit.h" + +static const uint8_t key_ctr_1[32] = { + 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, + 0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04, +}; +static const uint8_t iv_ctr_1[16] = { + 0x00, + 0x00, + 0x00, + 0x60, + 0xDB, + 0x56, + 0x72, + 0xC9, + 0x7A, + 0xA8, + 0xF0, + 0xB2, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_1[16] = { + 0x53, + 0x69, + 0x6E, + 0x67, + 0x6C, + 0x65, + 0x20, + 0x62, + 0x6C, + 0x6F, + 0x63, + 0x6B, + 0x20, + 0x6D, + 0x73, + 0x67, +}; +static const uint8_t tv_ctr_ct_1[16] = { + 0x14, + 0x5A, + 0xD0, + 0x1D, + 0xBF, + 0x82, + 0x4E, + 0xC7, + 0x56, + 0x08, + 0x63, + 0xDC, + 0x71, + 0xE3, + 0xE0, + 0xC0, +}; + +static const uint8_t key_ctr_2[32] = { + 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, + 0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04, +}; +static const uint8_t iv_ctr_2[16] = { + 0x00, + 0x00, + 0x00, + 0x60, + 0xDB, + 0x56, + 0x72, + 0xC9, + 0x7A, + 0xA8, + 0xF0, + 0xB2, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_2[0] = {}; +//static const uint8_t tv_ctr_ct_2[0] = {}; + +static const uint8_t key_ctr_3[32] = { + 0xF6, 0xD6, 0x6D, 0x6B, 0xD5, 0x2D, 0x59, 0xBB, 0x07, 0x96, 0x36, 0x58, 0x79, 0xEF, 0xF8, 0x86, + 0xC6, 0x6D, 0xD5, 0x1A, 0x5B, 0x6A, 0x99, 0x74, 0x4B, 0x50, 0x59, 0x0C, 0x87, 0xA2, 0x38, 0x84, +}; +static const uint8_t iv_ctr_3[16] = { + 0x00, + 0xFA, + 0xAC, + 0x24, + 0xC1, + 0x58, + 0x5E, + 0xF1, + 0x5A, + 0x43, + 0xD8, + 0x75, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_3[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, +}; +static const uint8_t tv_ctr_ct_3[32] = { + 0xF0, 0x5E, 0x23, 0x1B, 0x38, 0x94, 0x61, 0x2C, 0x49, 0xEE, 0x00, 0x0B, 0x80, 0x4E, 0xB2, 0xA9, + 0xB8, 0x30, 0x6B, 0x50, 0x8F, 0x83, 0x9D, 0x6A, 0x55, 0x30, 0x83, 0x1D, 0x93, 0x44, 0xAF, 0x1C, +}; + +static const uint8_t key_ctr_4[32] = { + 0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2, + 0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D, +}; +static const uint8_t iv_ctr_4[16] = { + 0x00, + 0x1C, + 0xC5, + 0xB7, + 0x51, + 0xA5, + 0x1D, + 0x70, + 0xA1, + 0xC1, + 0x11, + 0x48, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_4[36] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, +}; +static const uint8_t tv_ctr_ct_4[36] = { + 0xEB, 0x6C, 0x52, 0x82, 0x1D, 0x0B, 0xBB, 0xF7, 0xCE, 0x75, 0x94, 0x46, + 0x2A, 0xCA, 0x4F, 0xAA, 0xB4, 0x07, 0xDF, 0x86, 0x65, 0x69, 0xFD, 0x07, + 0xF4, 0x8C, 0xC0, 0xB5, 0x83, 0xD6, 0x07, 0x1F, 0x1E, 0xC0, 0xE6, 0xB8, +}; + +static const uint8_t key_ctr_5[32] = { + 0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2, + 0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D, +}; +static const uint8_t iv_ctr_5[16] = { + 0x00, + 0x1C, + 0xC5, + 0xB7, + 0x51, + 0xA5, + 0x1D, + 0x70, + 0xA1, + 0xC1, + 0x11, + 0x48, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_5[0] = {}; +//static const uint8_t tv_ctr_ct_5[0] = {}; + +static const uint8_t key_gcm_1[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const uint8_t iv_gcm_1[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_1[0] = {}; +//static const uint8_t tv_gcm_ct_1[0] = {}; +static const uint8_t aad_gcm_1[0] = {}; +static const uint8_t tv_gcm_tag_1[16] = { + 0x53, + 0x0F, + 0x8A, + 0xFB, + 0xC7, + 0x45, + 0x36, + 0xB9, + 0xA9, + 0x63, + 0xB4, + 0xF1, + 0xC4, + 0xCB, + 0x73, + 0x8B, +}; + +static const uint8_t key_gcm_2[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const uint8_t iv_gcm_2[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_2[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t tv_gcm_ct_2[16] = { + 0xCE, + 0xA7, + 0x40, + 0x3D, + 0x4D, + 0x60, + 0x6B, + 0x6E, + 0x07, + 0x4E, + 0xC5, + 0xD3, + 0xBA, + 0xF3, + 0x9D, + 0x18, +}; +static const uint8_t aad_gcm_2[0] = {}; +static const uint8_t tv_gcm_tag_2[16] = { + 0xD0, + 0xD1, + 0xC8, + 0xA7, + 0x99, + 0x99, + 0x6B, + 0xF0, + 0x26, + 0x5B, + 0x98, + 0xB5, + 0xD4, + 0x8A, + 0xB9, + 0x19, +}; + +static const uint8_t key_gcm_3[32] = { + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, +}; +static const uint8_t iv_gcm_3[16] = { + 0xCA, + 0xFE, + 0xBA, + 0xBE, + 0xFA, + 0xCE, + 0xDB, + 0xAD, + 0xDE, + 0xCA, + 0xF8, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_3[64] = { + 0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, 0x9A, + 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, + 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, 0xA6, 0xB5, 0x25, + 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, 0x1A, 0xAF, 0xD2, 0x55, +}; +static const uint8_t tv_gcm_ct_3[64] = { + 0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, 0x7D, + 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, 0xD1, 0xAA, + 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, 0x89, 0x80, 0x15, 0xAD, +}; +static const uint8_t aad_gcm_3[0] = {}; +static const uint8_t tv_gcm_tag_3[16] = { + 0xB0, + 0x94, + 0xDA, + 0xC5, + 0xD9, + 0x34, + 0x71, + 0xBD, + 0xEC, + 0x1A, + 0x50, + 0x22, + 0x70, + 0xE3, + 0xCC, + 0x6C, +}; + +static const uint8_t key_gcm_4[32] = { + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, +}; +static const uint8_t iv_gcm_4[16] = { + 0xCA, + 0xFE, + 0xBA, + 0xBE, + 0xFA, + 0xCE, + 0xDB, + 0xAD, + 0xDE, + 0xCA, + 0xF8, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_4[60] = { + 0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, + 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, + 0x8A, 0x72, 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, + 0xA6, 0xB5, 0x25, 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, +}; +static const uint8_t tv_gcm_ct_4[60] = { + 0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, + 0x7D, 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, + 0xD1, 0xAA, 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, + 0x82, 0x88, 0x38, 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, +}; +static const uint8_t aad_gcm_4[20] = { + 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED, + 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2, +}; +static const uint8_t tv_gcm_tag_4[16] = { + 0x76, + 0xFC, + 0x6E, + 0xCE, + 0x0F, + 0x4E, + 0x17, + 0x68, + 0xCD, + 0xDF, + 0x88, + 0x53, + 0xBB, + 0x2D, + 0x55, + 0x1B, +}; + +static void furi_hal_crypto_ctr_setup() { +} + +static void furi_hal_crypto_ctr_teardown() { +} + +static void furi_hal_crypto_gcm_setup() { +} + +static void furi_hal_crypto_gcm_teardown() { +} + +MU_TEST(furi_hal_crypto_ctr_1) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_1)]; + + ret = furi_hal_crypto_ctr(key_ctr_1, iv_ctr_1, pt_ctr_1, ct, sizeof(pt_ctr_1)); + mu_assert(ret, "CTR 1 failed"); + mu_assert_mem_eq(tv_ctr_ct_1, ct, sizeof(pt_ctr_1)); +} + +MU_TEST(furi_hal_crypto_ctr_2) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_2)]; + + ret = furi_hal_crypto_ctr(key_ctr_2, iv_ctr_2, pt_ctr_2, ct, sizeof(pt_ctr_2)); + mu_assert(ret, "CTR 2 failed"); + //mu_assert_mem_eq(tv_ctr_ct_2, ct, sizeof(pt_ctr_2)); +} + +MU_TEST(furi_hal_crypto_ctr_3) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_3)]; + + ret = furi_hal_crypto_ctr(key_ctr_3, iv_ctr_3, pt_ctr_3, ct, sizeof(pt_ctr_3)); + mu_assert(ret, "CTR 3 failed"); + mu_assert_mem_eq(tv_ctr_ct_3, ct, sizeof(pt_ctr_3)); +} + +MU_TEST(furi_hal_crypto_ctr_4) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_4)]; + + ret = furi_hal_crypto_ctr(key_ctr_4, iv_ctr_4, pt_ctr_4, ct, sizeof(pt_ctr_4)); + mu_assert(ret, "CTR 4 failed"); + mu_assert_mem_eq(tv_ctr_ct_4, ct, sizeof(pt_ctr_4)); +} + +MU_TEST(furi_hal_crypto_ctr_5) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_5)]; + + ret = furi_hal_crypto_ctr(key_ctr_5, iv_ctr_5, pt_ctr_5, ct, sizeof(pt_ctr_5)); + mu_assert(ret, "CTR 5 failed"); + //mu_assert_mem_eq(tv_ctr_ct_5, ct, sizeof(pt_ctr_5)); +} + +MU_TEST(furi_hal_crypto_gcm_1) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_1)]; + uint8_t ct[sizeof(pt_gcm_1)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_1, + iv_gcm_1, + aad_gcm_1, + sizeof(aad_gcm_1), + pt_gcm_1, + ct, + sizeof(pt_gcm_1), + tag_enc, + false); + mu_assert(ret, "GCM 1 encryption failed"); + //mu_assert_mem_eq(tv_gcm_ct_1, ct, sizeof(pt_gcm_1)); + mu_assert_mem_eq(tv_gcm_tag_1, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_1, iv_gcm_1, aad_gcm_1, sizeof(aad_gcm_1), ct, pt, sizeof(pt_gcm_1), tag_dec, true); + mu_assert(ret, "GCM 1 decryption failed"); + //mu_assert_mem_eq(pt_gcm_1, pt, sizeof(pt_gcm_1)); + mu_assert_mem_eq(tv_gcm_tag_1, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_2) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_2)]; + uint8_t ct[sizeof(pt_gcm_2)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_2, + iv_gcm_2, + aad_gcm_2, + sizeof(aad_gcm_2), + pt_gcm_2, + ct, + sizeof(pt_gcm_2), + tag_enc, + false); + mu_assert(ret, "GCM 2 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_2, ct, sizeof(pt_gcm_2)); + mu_assert_mem_eq(tv_gcm_tag_2, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_2, iv_gcm_2, aad_gcm_2, sizeof(aad_gcm_2), ct, pt, sizeof(pt_gcm_2), tag_dec, true); + mu_assert(ret, "GCM 2 decryption failed"); + mu_assert_mem_eq(pt_gcm_2, pt, sizeof(pt_gcm_2)); + mu_assert_mem_eq(tv_gcm_tag_2, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_3) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_3)]; + uint8_t ct[sizeof(pt_gcm_3)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_3, + iv_gcm_3, + aad_gcm_3, + sizeof(aad_gcm_3), + pt_gcm_3, + ct, + sizeof(pt_gcm_3), + tag_enc, + false); + mu_assert(ret, "GCM 3 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_3, ct, sizeof(pt_gcm_3)); + mu_assert_mem_eq(tv_gcm_tag_3, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_3, iv_gcm_3, aad_gcm_3, sizeof(aad_gcm_3), ct, pt, sizeof(pt_gcm_3), tag_dec, true); + mu_assert(ret, "GCM 3 decryption failed"); + mu_assert_mem_eq(pt_gcm_3, pt, sizeof(pt_gcm_3)); + mu_assert_mem_eq(tv_gcm_tag_3, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_4) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_4)]; + uint8_t ct[sizeof(pt_gcm_4)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_4, + iv_gcm_4, + aad_gcm_4, + sizeof(aad_gcm_4), + pt_gcm_4, + ct, + sizeof(pt_gcm_4), + tag_enc, + false); + mu_assert(ret, "GCM 4 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_4, ct, sizeof(pt_gcm_4)); + mu_assert_mem_eq(tv_gcm_tag_4, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_4, iv_gcm_4, aad_gcm_4, sizeof(aad_gcm_4), ct, pt, sizeof(pt_gcm_4), tag_dec, true); + mu_assert(ret, "GCM 4 decryption failed"); + mu_assert_mem_eq(pt_gcm_4, pt, sizeof(pt_gcm_4)); + mu_assert_mem_eq(tv_gcm_tag_4, tag_dec, 16); +} + +MU_TEST_SUITE(furi_hal_crypto_ctr_test) { + MU_SUITE_CONFIGURE(&furi_hal_crypto_ctr_setup, &furi_hal_crypto_ctr_teardown); + MU_RUN_TEST(furi_hal_crypto_ctr_1); + MU_RUN_TEST(furi_hal_crypto_ctr_2); + MU_RUN_TEST(furi_hal_crypto_ctr_3); + MU_RUN_TEST(furi_hal_crypto_ctr_4); + MU_RUN_TEST(furi_hal_crypto_ctr_5); +} + +MU_TEST_SUITE(furi_hal_crypto_gcm_test) { + MU_SUITE_CONFIGURE(&furi_hal_crypto_gcm_setup, &furi_hal_crypto_gcm_teardown); + MU_RUN_TEST(furi_hal_crypto_gcm_1); + MU_RUN_TEST(furi_hal_crypto_gcm_2); + MU_RUN_TEST(furi_hal_crypto_gcm_3); + MU_RUN_TEST(furi_hal_crypto_gcm_4); +} + +int run_minunit_test_furi_hal_crypto() { + MU_RUN_SUITE(furi_hal_crypto_ctr_test); + MU_RUN_SUITE(furi_hal_crypto_gcm_test); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index 9d7631bfe..f7a53d7c8 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -10,6 +10,7 @@ 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(); @@ -39,6 +40,7 @@ typedef struct { const UnitTest unit_tests[] = { {.name = "furi", .entry = run_minunit_test_furi}, {.name = "furi_hal", .entry = run_minunit_test_furi_hal}, + {.name = "furi_hal_crypto", .entry = run_minunit_test_furi_hal_crypto}, {.name = "furi_string", .entry = run_minunit_test_furi_string}, {.name = "storage", .entry = run_minunit_test_storage}, {.name = "stream", .entry = run_minunit_test_stream}, diff --git a/applications/main/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c index e5433544a..8489ed91e 100644 --- a/applications/main/u2f/u2f_data.c +++ b/applications/main/u2f/u2f_data.c @@ -14,7 +14,7 @@ #define U2F_CNT_FILE U2F_DATA_FOLDER "cnt.u2f" #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2 -#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE 11 +#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT #define U2F_CERT_STOCK 0 // Stock certificate, private key is encrypted with factory key #define U2F_CERT_USER 1 // User certificate, private key is encrypted with unique key @@ -136,7 +136,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { // Generate random IV furi_hal_random_fill_buf(iv, 16); - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -145,7 +145,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -179,7 +179,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { uint32_t version = 0; // Check if unique key exists in secure eclave and generate it if missing - if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; + if(!furi_hal_crypto_enclave_ensure_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; FuriString* filetype; filetype = furi_string_alloc(); @@ -226,7 +226,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -237,7 +237,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } else { if(!flipper_format_read_hex(flipper_format, "Data", cert_key, 32)) { FURI_LOG_E(TAG, "Missing data"); @@ -292,7 +292,7 @@ bool u2f_data_key_load(uint8_t* device_key) { FURI_LOG_E(TAG, "Missing data"); break; } - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -302,7 +302,7 @@ bool u2f_data_key_load(uint8_t* device_key) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); state = true; } while(0); } @@ -324,7 +324,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { furi_hal_random_fill_buf(iv, 16); furi_hal_random_fill_buf(key, 32); - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -333,7 +333,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -398,7 +398,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { FURI_LOG_E(TAG, "Missing data"); break; } - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -408,7 +408,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); if(cnt.control == U2F_COUNTER_CONTROL_VAL) { *cnt_val = cnt.counter; state = true; @@ -440,7 +440,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { cnt.control = U2F_COUNTER_CONTROL_VAL; cnt.counter = cnt_val; - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -449,7 +449,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index a286f4457..af52b84fa 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -33,7 +33,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } @@ -88,7 +88,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) { } while(0); if(key_loaded) { - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } } @@ -108,7 +108,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } @@ -160,7 +160,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { } while(0); if(key_loaded) { - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } } @@ -175,14 +175,14 @@ void crypto_cli_has_key(Cli* cli, FuriString* args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } printf("Successfully loaded key from slot %d", key_slot); - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } while(0); } @@ -251,25 +251,25 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) { if(key_slot > 0) { uint8_t iv[16] = {0}; if(key_slot > 1) { - if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot - 1, iv)) { printf( "Slot %d before %d is empty, which is not allowed", key_slot - 1, key_slot); break; } - furi_hal_crypto_store_unload_key(key_slot - 1); + furi_hal_crypto_enclave_unload_key(key_slot - 1); } - if(furi_hal_crypto_store_load_key(key_slot, iv)) { - furi_hal_crypto_store_unload_key(key_slot); + if(furi_hal_crypto_enclave_load_key(key_slot, iv)) { + furi_hal_crypto_enclave_unload_key(key_slot); printf("Key slot %d is already used", key_slot); break; } } uint8_t slot; - if(furi_hal_crypto_store_add_key(&key, &slot)) { + if(furi_hal_crypto_enclave_store_key(&key, &slot)) { printf("Success. Stored to slot: %d", slot); } else { printf("Failure"); diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 529477b50..081fe502f 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,34.4,, +Version,+,35.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1036,14 +1036,20 @@ Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" +Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" +Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" Function,-,furi_hal_crypto_init,void, -Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" -Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" -Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t -Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" -Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t +Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_unload_key,_Bool, +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 6fc4356c5..535582c0c 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,34.4,, +Version,+,35.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -1107,14 +1107,20 @@ Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" +Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" +Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" Function,-,furi_hal_crypto_init,void, -Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" -Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" -Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t -Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" -Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t +Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_unload_key,_Bool, +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index eb5c3b782..b9a0feec9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -13,7 +14,7 @@ #define ENCLAVE_SIGNATURE_SIZE 16 #define CRYPTO_BLK_LEN (4 * sizeof(uint32_t)) -#define CRYPTO_TIMEOUT (1000) +#define CRYPTO_TIMEOUT_US (1000000) #define CRYPTO_MODE_ENCRYPT 0U #define CRYPTO_MODE_INIT (AES_CR_MODE_0) @@ -24,6 +25,19 @@ #define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) #define CRYPTO_AES_CBC (AES_CR_CHMOD_0) +#define CRYPTO_AES_CTR (AES_CR_CHMOD_1) +#define CRYPTO_CTR_IV_LEN (12U) +#define CRYPTO_CTR_CTR_LEN (4U) + +#define CRYPTO_AES_GCM (AES_CR_CHMOD_1 | AES_CR_CHMOD_0) +#define CRYPTO_GCM_IV_LEN (12U) +#define CRYPTO_GCM_CTR_LEN (4U) +#define CRYPTO_GCM_TAG_LEN (16U) +#define CRYPTO_GCM_PH_INIT (0x0U << AES_CR_GCMPH_Pos) +#define CRYPTO_GCM_PH_HEADER (AES_CR_GCMPH_0) +#define CRYPTO_GCM_PH_PAYLOAD (AES_CR_GCMPH_1) +#define CRYPTO_GCM_PH_FINAL (AES_CR_GCMPH_1 | AES_CR_GCMPH_0) + static FuriMutex* furi_hal_crypto_mutex = NULL; static bool furi_hal_crypto_mode_init_done = false; @@ -80,7 +94,7 @@ static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end key.size = FuriHalCryptoKeySize256; key.data = key_data; furi_hal_random_fill_buf(key_data, 32); - if(!furi_hal_crypto_store_add_key(&key, &slot)) { + if(!furi_hal_crypto_enclave_store_key(&key, &slot)) { FURI_LOG_E(TAG, "Error writing key to slot %u", slot); return false; } @@ -88,21 +102,21 @@ static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end return true; } -bool furi_hal_crypto_verify_key(uint8_t key_slot) { +bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot) { uint8_t keys_nb = 0; uint8_t valid_keys_nb = 0; uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS; uint8_t empty_iv[16] = {0}; - furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb); + furi_hal_crypto_enclave_verify(&keys_nb, &valid_keys_nb); if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key if(key_slot > keys_nb) return false; } else { // Unique key if(keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing return false; for(uint8_t i = key_slot; i > ENCLAVE_FACTORY_KEY_SLOTS; i--) { - if(furi_hal_crypto_store_load_key(i, empty_iv)) { + if(furi_hal_crypto_enclave_load_key(i, empty_iv)) { last_valid_slot = i; - furi_hal_crypto_store_unload_key(i); + furi_hal_crypto_enclave_unload_key(i); break; } } @@ -114,14 +128,14 @@ bool furi_hal_crypto_verify_key(uint8_t key_slot) { return true; } -bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { +bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb) { furi_assert(keys_nb); furi_assert(valid_keys_nb); uint8_t keys = 0; uint8_t keys_valid = 0; uint8_t buffer[ENCLAVE_SIGNATURE_SIZE]; for(size_t key_slot = 0; key_slot < ENCLAVE_FACTORY_KEY_SLOTS; key_slot++) { - if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { + if(furi_hal_crypto_enclave_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { keys++; if(furi_hal_crypto_encrypt( enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) { @@ -129,7 +143,7 @@ bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { memcmp(buffer, enclave_signature_expected[key_slot], ENCLAVE_SIGNATURE_SIZE) == 0; } - furi_hal_crypto_store_unload_key(key_slot + 1); + furi_hal_crypto_enclave_unload_key(key_slot + 1); } } *keys_nb = keys; @@ -140,7 +154,7 @@ bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { return false; } -bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) { +bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot) { furi_assert(key); furi_assert(slot); @@ -205,6 +219,16 @@ static void crypto_key_init(uint32_t* key, uint32_t* iv) { AES1->IVR0 = iv[3]; } +static bool furi_hal_crypto_wait_flag(uint32_t flag) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CRYPTO_TIMEOUT_US); + while(!READ_BIT(AES1->SR, flag)) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } + return true; +} + static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { furi_check((blk_len <= 4) && (blk_len > 0)); @@ -216,14 +240,8 @@ static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { } } - uint32_t countdown = CRYPTO_TIMEOUT; - while(!READ_BIT(AES1->SR, AES_SR_CCF)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; } SET_BIT(AES1->CR, AES_CR_CCFC); @@ -237,7 +255,7 @@ static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { return true; } -bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { +bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv) { furi_assert(slot > 0 && slot <= 100); furi_assert(furi_hal_crypto_mutex); furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); @@ -260,7 +278,7 @@ bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { } } -bool furi_hal_crypto_store_unload_key(uint8_t slot) { +bool furi_hal_crypto_enclave_unload_key(uint8_t slot) { if(!furi_hal_bt_is_alive()) { return false; } @@ -276,6 +294,27 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) { return (shci_state == SHCI_Success); } +bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv) { + furi_assert(furi_hal_crypto_mutex); + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + furi_hal_bus_enable(FuriHalBusAES1); + + furi_hal_crypto_mode_init_done = false; + crypto_key_init((uint32_t*)key, (uint32_t*)iv); + + return true; +} + +bool furi_hal_crypto_unload_key(void) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + + furi_hal_bus_disable(FuriHalBusAES1); + + furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); + return true; +} + bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) { bool state = false; @@ -307,14 +346,8 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) SET_BIT(AES1->CR, AES_CR_EN); - uint32_t countdown = CRYPTO_TIMEOUT; - while(!READ_BIT(AES1->SR, AES_SR_CCF)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; } SET_BIT(AES1->CR, AES_CR_CCFC); @@ -340,3 +373,360 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) return state; } + +static void crypto_key_init_bswap(uint32_t* key, uint32_t* iv, uint32_t chaining_mode) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + MODIFY_REG( + AES1->CR, + AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD, + CRYPTO_DATATYPE_32B | CRYPTO_KEYSIZE_256B | chaining_mode); + + if(key != NULL) { + AES1->KEYR7 = __builtin_bswap32(key[0]); + AES1->KEYR6 = __builtin_bswap32(key[1]); + AES1->KEYR5 = __builtin_bswap32(key[2]); + AES1->KEYR4 = __builtin_bswap32(key[3]); + AES1->KEYR3 = __builtin_bswap32(key[4]); + AES1->KEYR2 = __builtin_bswap32(key[5]); + AES1->KEYR1 = __builtin_bswap32(key[6]); + AES1->KEYR0 = __builtin_bswap32(key[7]); + } + + AES1->IVR3 = __builtin_bswap32(iv[0]); + AES1->IVR2 = __builtin_bswap32(iv[1]); + AES1->IVR1 = __builtin_bswap32(iv[2]); + AES1->IVR0 = __builtin_bswap32(iv[3]); +} + +static bool + furi_hal_crypto_load_key_bswap(const uint8_t* key, const uint8_t* iv, uint32_t chaining_mode) { + furi_assert(furi_hal_crypto_mutex); + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + furi_hal_bus_enable(FuriHalBusAES1); + + crypto_key_init_bswap((uint32_t*)key, (uint32_t*)iv, chaining_mode); + + return true; +} + +static bool wait_for_crypto(void) { + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; + } + + SET_BIT(AES1->CR, AES_CR_CCFC); + + return true; +} + +static bool furi_hal_crypto_process_block_bswap(const uint8_t* in, uint8_t* out, size_t bytes) { + uint32_t block[CRYPTO_BLK_LEN / 4]; + memset(block, 0, sizeof(block)); + + memcpy(block, in, bytes); + + block[0] = __builtin_bswap32(block[0]); + block[1] = __builtin_bswap32(block[1]); + block[2] = __builtin_bswap32(block[2]); + block[3] = __builtin_bswap32(block[3]); + + if(!crypto_process_block(block, block, CRYPTO_BLK_LEN / 4)) { + return false; + } + + block[0] = __builtin_bswap32(block[0]); + block[1] = __builtin_bswap32(block[1]); + block[2] = __builtin_bswap32(block[2]); + block[3] = __builtin_bswap32(block[3]); + + memcpy(out, block, bytes); + + return true; +} + +static bool furi_hal_crypto_process_block_no_read_bswap(const uint8_t* in, size_t bytes) { + uint32_t block[CRYPTO_BLK_LEN / 4]; + memset(block, 0, sizeof(block)); + + memcpy(block, in, bytes); + + AES1->DINR = __builtin_bswap32(block[0]); + AES1->DINR = __builtin_bswap32(block[1]); + AES1->DINR = __builtin_bswap32(block[2]); + AES1->DINR = __builtin_bswap32(block[3]); + + return wait_for_crypto(); +} + +static void furi_hal_crypto_ctr_prep_iv(uint8_t* iv) { + /* append counter to IV */ + iv[CRYPTO_CTR_IV_LEN] = 0; + iv[CRYPTO_CTR_IV_LEN + 1] = 0; + iv[CRYPTO_CTR_IV_LEN + 2] = 0; + iv[CRYPTO_CTR_IV_LEN + 3] = 1; +} + +static bool furi_hal_crypto_ctr_payload(const uint8_t* input, uint8_t* output, size_t length) { + SET_BIT(AES1->CR, AES_CR_EN); + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); + + size_t last_block_bytes = length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + CLEAR_BIT(AES1->CR, AES_CR_EN); + return true; +} + +bool furi_hal_crypto_ctr( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* input, + uint8_t* output, + size_t length) { + /* prepare IV and counter */ + uint8_t iv_and_counter[CRYPTO_CTR_IV_LEN + CRYPTO_CTR_CTR_LEN]; + memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); + furi_hal_crypto_ctr_prep_iv(iv_and_counter); + + /* load key and IV and set the mode to CTR */ + if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_CTR)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* process the input and write to output */ + bool state = furi_hal_crypto_ctr_payload(input, output, length); + + furi_hal_crypto_unload_key(); + + return state; +} + +static void furi_hal_crypto_gcm_prep_iv(uint8_t* iv) { + /* append counter to IV */ + iv[CRYPTO_GCM_IV_LEN] = 0; + iv[CRYPTO_GCM_IV_LEN + 1] = 0; + iv[CRYPTO_GCM_IV_LEN + 2] = 0; + iv[CRYPTO_GCM_IV_LEN + 3] = 2; +} + +static bool furi_hal_crypto_gcm_init(bool decrypt) { + /* GCM init phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_INIT); + if(decrypt) { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT); + } else { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); + } + + SET_BIT(AES1->CR, AES_CR_EN); + + if(!wait_for_crypto()) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + + return true; +} + +static bool furi_hal_crypto_gcm_header(const uint8_t* aad, size_t aad_length) { + /* GCM header phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_HEADER); + SET_BIT(AES1->CR, AES_CR_EN); + + size_t last_block_bytes = aad_length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < aad_length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + return true; +} + +static bool furi_hal_crypto_gcm_payload( + const uint8_t* input, + uint8_t* output, + size_t length, + bool decrypt) { + /* GCM payload phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_PAYLOAD); + SET_BIT(AES1->CR, AES_CR_EN); + + size_t last_block_bytes = length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!decrypt) { + MODIFY_REG( + AES1->CR, AES_CR_NPBLB, (CRYPTO_BLK_LEN - last_block_bytes) << AES_CR_NPBLB_Pos); + } + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + return true; +} + +static bool furi_hal_crypto_gcm_finish(size_t aad_length, size_t payload_length, uint8_t* tag) { + /* GCM final phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_FINAL); + + uint32_t last_block[CRYPTO_BLK_LEN / 4]; + memset(last_block, 0, sizeof(last_block)); + last_block[1] = __builtin_bswap32((uint32_t)(aad_length * 8)); + last_block[3] = __builtin_bswap32((uint32_t)(payload_length * 8)); + + if(!furi_hal_crypto_process_block_bswap((uint8_t*)&last_block[0], tag, CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + + return true; +} + +static bool furi_hal_crypto_gcm_compare_tag(const uint8_t* tag1, const uint8_t* tag2) { + uint8_t diff = 0; + + size_t i; + for(i = 0; i < CRYPTO_GCM_TAG_LEN; i++) { + diff |= tag1[i] ^ tag2[i]; + } + + return (diff == 0); +} + +bool furi_hal_crypto_gcm( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag, + bool decrypt) { + /* GCM init phase */ + + /* prepare IV and counter */ + uint8_t iv_and_counter[CRYPTO_GCM_IV_LEN + CRYPTO_GCM_CTR_LEN]; + memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); + furi_hal_crypto_gcm_prep_iv(iv_and_counter); + + /* load key and IV and set the mode to CTR */ + if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_GCM)) { + furi_hal_crypto_unload_key(); + return false; + } + + if(!furi_hal_crypto_gcm_init(decrypt)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* GCM header phase */ + + if(aad_length > 0) { + if(!furi_hal_crypto_gcm_header(aad, aad_length)) { + furi_hal_crypto_unload_key(); + return false; + } + } + + /* GCM payload phase */ + + if(!furi_hal_crypto_gcm_payload(input, output, length, decrypt)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* GCM final phase */ + + if(!furi_hal_crypto_gcm_finish(aad_length, length, tag)) { + furi_hal_crypto_unload_key(); + return false; + } + + furi_hal_crypto_unload_key(); + return true; +} + +FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag) { + if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, tag, false)) { + memset(output, 0, length); + memset(tag, 0, CRYPTO_GCM_TAG_LEN); + return FuriHalCryptoGCMStateError; + } + + return FuriHalCryptoGCMStateOk; +} + +FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + const uint8_t* tag) { + uint8_t dtag[CRYPTO_GCM_TAG_LEN]; + + if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, dtag, true)) { + memset(output, 0, length); + return FuriHalCryptoGCMStateError; + } + + if(!furi_hal_crypto_gcm_compare_tag(dtag, tag)) { + memset(output, 0, length); + return FuriHalCryptoGCMStateAuthFailure; + } + + return FuriHalCryptoGCMStateOk; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index cefb6a11b..4908cca69 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -283,7 +283,7 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { // Signature verification uint8_t enclave_keys = 0; uint8_t enclave_valid_keys = 0; - bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys); + bool enclave_valid = furi_hal_crypto_enclave_verify(&enclave_keys, &enclave_valid_keys); if(sep == '.') { property_value_out( &property_context, "%d", 3, "enclave", "keys", "valid", enclave_valid_keys); diff --git a/firmware/targets/furi_hal_include/furi_hal_crypto.h b/firmware/targets/furi_hal_include/furi_hal_crypto.h index 81788e96e..5e3b41040 100644 --- a/firmware/targets/furi_hal_include/furi_hal_crypto.h +++ b/firmware/targets/furi_hal_include/furi_hal_crypto.h @@ -1,6 +1,40 @@ /** * @file furi_hal_crypto.h + * * Cryptography HAL API + * + * !!! READ THIS FIRST !!! + * + * Flipper was never designed to be secure, nor it passed cryptography audit. + * Despite of the fact that keys are stored in secure enclave there are some + * types of attack that can be performed against AES engine to recover + * keys(theoretical). Also there is no way to securely deliver user keys to + * device and never will be. In addition device is fully open and there is no + * way to guarantee safety of your data, it can be easily dumped with debugger + * or modified code. + * + * Secure enclave on WB series is implemented on core2 FUS side and can be used + * only if core2 alive. Enclave is responsible for storing, loading and + * unloading keys to and from enclave/AES in secure manner(AES engine key + * registers will be locked when key from enclave loaded) + * + * There are 11 keys that we provision at factory: + * - 0 - Master key for secure key delivery. Impossible to use for anything but + * key provisioning. We don't plan to use it too. + * - 1 - 10 - Keys used by firmware. All devices got the same set of keys. You + * also can use them in your applications. + * + * Also there is a slot 11 that we use for device unique key. This slot is + * intentionally left blank till the moment of first use, so you can ensure that + * we don't know your unique key. Also you can provision this key by your self + * with crypto cli or API. + * + * Other slots can be used for your needs. But since enclave is sequential + * append only, we can not guarantee you that slots you want are free. NEVER USE + * THEM FOR PUBLIC APPLICATIONS. + * + * Also you can directly load raw keys into AES engine and use it for your + * needs. */ #pragma once @@ -12,10 +46,27 @@ extern "C" { #endif +/** Factory provisioned master key slot. Should never be used. */ +#define FURI_HAL_CRYPTO_ENCLAVE_MASTER_KEY_SLOT (0u) + +/** Factory provisioned keys slot range. All of them are exactly same on all flippers. */ +#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_START (1u) +#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END (10u) + +/** Device unique key slot. This key generated on first use or provisioned by user. Use furi_hal_crypto_enclave_ensure_key before using this slot. */ +#define FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT (11u) + +/** User key slot range. This slots can be used for your needs, but never use them in public apps. */ +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START (12u) +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END (100u) + +/** [Deprecated] Indicates availability of advanced crypto functions, will be dropped before v1.0 */ +#define FURI_HAL_CRYPTO_ADVANCED_AVAIL 1 + /** FuriHalCryptoKey Type */ typedef enum { FuriHalCryptoKeyTypeMaster, /**< Master key */ - FuriHalCryptoKeyTypeSimple, /**< Simple enencrypted key */ + FuriHalCryptoKeyTypeSimple, /**< Simple unencrypted key */ FuriHalCryptoKeyTypeEncrypted, /**< Encrypted with Master key */ } FuriHalCryptoKeyType; @@ -32,40 +83,89 @@ typedef struct { uint8_t* data; } FuriHalCryptoKey; -/** Initialize cryptography layer This includes AES engines, PKA and RNG - */ +/** FuriHalCryptoGCMState Result of a GCM operation */ +typedef enum { + FuriHalCryptoGCMStateOk, /**< operation successful */ + FuriHalCryptoGCMStateError, /**< error during encryption/decryption */ + FuriHalCryptoGCMStateAuthFailure, /**< tags do not match, auth failed */ +} FuriHalCryptoGCMState; + +/** Initialize cryptography layer(includes AES engines, PKA and RNG) */ void furi_hal_crypto_init(); -bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb); - -bool furi_hal_crypto_verify_key(uint8_t key_slot); - -/** Store key in crypto storage +/** Verify factory provisioned keys * - * @param key FuriHalCryptoKey to store. Only Master, Simple or - * Encrypted - * @param slot pinter to int where store slot number will be saved + * @param keys_nb The keys number of + * @param valid_keys_nb The valid keys number of + * + * @return true if all enclave keys are intact, false otherwise + */ +bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb); + +/** Ensure that requested slot and slots before this slot contains keys. + * + * This function is used to provision FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT. Also you + * may want to use it to generate some unique keys in user key slot range. + * + * @warning Because of the sequential nature of the secure enclave this + * method will generate key for all slots from + * FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END to the slot your requested. + * Keys are generated using on-chip RNG. + * + * @param[in] key_slot The key slot to enclave + * + * @return true if key exists or created, false if enclave corrupted + */ +bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot); + +/** Store key in crypto enclave + * + * @param key FuriHalCryptoKey to be stored + * @param slot pointer to int where enclave slot will be stored * * @return true on success */ -bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot); +bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot); -/** Init AES engine and load key from crypto store +/** Init AES engine and load key from crypto enclave * - * @param slot store slot number + * @warning Use only with furi_hal_crypto_enclave_unload_key() + * + * @param slot enclave slot * @param[in] iv pointer to 16 bytes Initialization Vector data * * @return true on success */ -bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv); +bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv); -/** Unload key engine and deinit AES engine +/** Unload key and deinit AES engine * - * @param slot store slot number + * @warning Use only with furi_hal_crypto_enclave_load_key() + * + * @param slot enclave slot * * @return true on success */ -bool furi_hal_crypto_store_unload_key(uint8_t slot); +bool furi_hal_crypto_enclave_unload_key(uint8_t slot); + +/** Init AES engine and load supplied key + * + * @warning Use only with furi_hal_crypto_unload_key() + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 16 bytes Initialization Vector data + * + * @return true on success + */ +bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv); + +/** Unload key and de-init AES engine + * + * @warning Use this function only with furi_hal_crypto_load_key() + * + * @return true on success + */ +bool furi_hal_crypto_unload_key(void); /** Encrypt data * @@ -87,6 +187,109 @@ bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) */ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size); +/** Encrypt the input using AES-CTR + * + * Decryption can be performed by supplying the ciphertext as input. Inits and + * deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * + * @return true on success + */ +bool furi_hal_crypto_ctr( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* input, + uint8_t* output, + size_t length); + +/** Encrypt/decrypt the input using AES-GCM + * + * When decrypting the tag generated needs to be compared to the tag attached to + * the ciphertext in a constant-time fashion. If the tags are not equal, the + * decryption failed and the plaintext returned needs to be discarded. Inits and + * deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes space for the tag + * @param decrypt true for decryption, false otherwise + * + * @return true on success + */ +bool furi_hal_crypto_gcm( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag, + bool decrypt); + +/** Encrypt the input using AES-GCM and generate a tag + * + * Inits and deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes space for the tag + * + * @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on + * failure + */ +FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag); + +/** Decrypt the input using AES-GCM and verify the provided tag + * + * Inits and deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes tag + * + * @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on + * failure, FuriHalCryptoGCMStateAuthFailure if the tag does not + * match + */ +FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + const uint8_t* tag); + #ifdef __cplusplus } #endif diff --git a/lib/subghz/subghz_keystore.c b/lib/subghz/subghz_keystore.c index e0b1cf6ca..4f602d2e2 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -116,7 +116,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, do { if(iv) { - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load decryption key"); break; } @@ -175,7 +175,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, } } while(ret > 0 && result); - if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + if(iv) furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); } while(false); free(encrypted_line); @@ -274,7 +274,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 subghz_keystore_mess_with_iv(iv); - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -320,7 +320,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 stream_write_char(stream, '\n'); encrypted_line_count++; } - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); size_t total_keys = SubGhzKeyArray_size(instance->data); result = encrypted_line_count == total_keys; if(result) { @@ -415,7 +415,7 @@ bool subghz_keystore_raw_encrypted_save( subghz_keystore_mess_with_iv(iv); - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -468,7 +468,7 @@ bool subghz_keystore_raw_encrypted_save( flipper_format_free(output_flipper_format); - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); if(!result) break; @@ -570,7 +570,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* } } - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -598,7 +598,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len); } while(0); - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); if(decrypted) result = true; } while(0); flipper_format_free(flipper_format); From 2702c00ba4c4f41075adca24bdb9d1899d7f5c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 10 Aug 2023 18:45:17 +0900 Subject: [PATCH 13/99] Scripts: OB recovery (#2964) * Scripts: OB recovery * Scripts: slightly different ob * Scripts: remove excessive return * Scripts: simplifying work with registers * Make PVS happy Co-authored-by: SG --- .../targets/f7/furi_hal/furi_hal_crypto.c | 4 +- scripts/flipper/utils/programmer.py | 4 + scripts/flipper/utils/programmer_openocd.py | 55 +++++--- scripts/flipper/utils/register.py | 17 ++- scripts/flipper/utils/stm32wb55.py | 133 ++++++++++-------- scripts/ob.py | 20 +++ 6 files changed, 148 insertions(+), 85 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index b9a0feec9..a897648a3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -500,7 +500,7 @@ bool furi_hal_crypto_ctr( size_t length) { /* prepare IV and counter */ uint8_t iv_and_counter[CRYPTO_CTR_IV_LEN + CRYPTO_CTR_CTR_LEN]; - memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); + memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); //-V1086 furi_hal_crypto_ctr_prep_iv(iv_and_counter); /* load key and IV and set the mode to CTR */ @@ -648,7 +648,7 @@ bool furi_hal_crypto_gcm( /* prepare IV and counter */ uint8_t iv_and_counter[CRYPTO_GCM_IV_LEN + CRYPTO_GCM_CTR_LEN]; - memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); + memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); //-V1086 furi_hal_crypto_gcm_prep_iv(iv_and_counter); /* load key and IV and set the mode to CTR */ diff --git a/scripts/flipper/utils/programmer.py b/scripts/flipper/utils/programmer.py index 84452d154..938065f7c 100644 --- a/scripts/flipper/utils/programmer.py +++ b/scripts/flipper/utils/programmer.py @@ -26,6 +26,10 @@ class Programmer(ABC): def option_bytes_set(self, file_path: str) -> bool: pass + @abstractmethod + def option_bytes_recover(self) -> bool: + pass + @abstractmethod def otp_write(self, address: int, file_path: str) -> bool: pass diff --git a/scripts/flipper/utils/programmer_openocd.py b/scripts/flipper/utils/programmer_openocd.py index 5a8029f37..77335ee5b 100644 --- a/scripts/flipper/utils/programmer_openocd.py +++ b/scripts/flipper/utils/programmer_openocd.py @@ -44,11 +44,11 @@ class OpenOCDProgrammer(Programmer): self.logger = logging.getLogger() def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool: - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) if mode == Programmer.RunMode.Run: - stm32.reset(self.openocd, stm32.RunMode.Run) + stm32.reset(stm32.RunMode.Run) elif mode == Programmer.RunMode.Stop: - stm32.reset(self.openocd, stm32.RunMode.Init) + stm32.reset(stm32.RunMode.Init) else: raise Exception("Unknown mode") @@ -96,11 +96,11 @@ class OpenOCDProgrammer(Programmer): def option_bytes_validate(self, file_path: str) -> bool: # Registers - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) # OpenOCD self.openocd.start() - stm32.reset(self.openocd, stm32.RunMode.Init) + stm32.reset(stm32.RunMode.Init) # Generate Option Bytes data ob_data = OptionBytesData(file_path) @@ -133,7 +133,7 @@ class OpenOCDProgrammer(Programmer): self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error) # Stop OpenOCD - stm32.reset(self.openocd, stm32.RunMode.Run) + stm32.reset(stm32.RunMode.Run) self.openocd.stop() return return_code @@ -143,11 +143,11 @@ class OpenOCDProgrammer(Programmer): def option_bytes_set(self, file_path: str) -> bool: # Registers - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) # OpenOCD self.openocd.start() - stm32.reset(self.openocd, stm32.RunMode.Init) + stm32.reset(stm32.RunMode.Init) # Generate Option Bytes data ob_data = OptionBytesData(file_path) @@ -159,11 +159,11 @@ class OpenOCDProgrammer(Programmer): ob_dwords = int(ob_length / 8) # Clear flash errors - stm32.clear_flash_errors(self.openocd) + stm32.clear_flash_errors() # Unlock Flash and Option Bytes - stm32.flash_unlock(self.openocd) - stm32.option_bytes_unlock(self.openocd) + stm32.flash_unlock() + stm32.option_bytes_unlock() ob_need_to_apply = False @@ -194,16 +194,16 @@ class OpenOCDProgrammer(Programmer): self.openocd.write_32(device_reg_addr, ob_value) if ob_need_to_apply: - stm32.option_bytes_apply(self.openocd) + stm32.option_bytes_apply() else: self.logger.info("Option Bytes are already correct") # Load Option Bytes # That will reset and also lock the Option Bytes and the Flash - stm32.option_bytes_load(self.openocd) + stm32.option_bytes_load() # Stop OpenOCD - stm32.reset(self.openocd, stm32.RunMode.Run) + stm32.reset(stm32.RunMode.Run) self.openocd.stop() return True @@ -233,11 +233,10 @@ class OpenOCDProgrammer(Programmer): self.logger.debug(f"Data: {data.hex().upper()}") # Start OpenOCD - oocd = self.openocd - oocd.start() + self.openocd.start() # Registers - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) try: # Check that OTP is empty for the given address @@ -245,7 +244,7 @@ class OpenOCDProgrammer(Programmer): already_written = True for i in range(0, data_size, 4): file_word = int.from_bytes(data[i : i + 4], "little") - device_word = oocd.read_32(address + i) + device_word = self.openocd.read_32(address + i) if device_word != 0xFFFFFFFF and device_word != file_word: self.logger.error( f"OTP memory at {address + i:08X} is not empty: {device_word:08X}" @@ -260,7 +259,7 @@ class OpenOCDProgrammer(Programmer): return OpenOCDProgrammerResult.Success self.reset(self.RunMode.Stop) - stm32.clear_flash_errors(oocd) + stm32.clear_flash_errors() # Write OTP memory by 8 bytes for i in range(0, data_size, 8): @@ -269,14 +268,14 @@ class OpenOCDProgrammer(Programmer): self.logger.debug( f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}" ) - stm32.write_flash_64(oocd, address + i, word_1, word_2) + stm32.write_flash_64(address + i, word_1, word_2) # Validate OTP memory validation_result = True for i in range(0, data_size, 4): file_word = int.from_bytes(data[i : i + 4], "little") - device_word = oocd.read_32(address + i) + device_word = self.openocd.read_32(address + i) if file_word != device_word: self.logger.error( f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}" @@ -284,11 +283,21 @@ class OpenOCDProgrammer(Programmer): validation_result = False finally: # Stop OpenOCD - stm32.reset(oocd, stm32.RunMode.Run) - oocd.stop() + stm32.reset(stm32.RunMode.Run) + self.openocd.stop() return ( OpenOCDProgrammerResult.Success if validation_result else OpenOCDProgrammerResult.ErrorValidation ) + + def option_bytes_recover(self) -> bool: + try: + self.openocd.start() + stm32 = STM32WB55(self.openocd) + stm32.reset(stm32.RunMode.Halt) + stm32.option_bytes_recover() + return True + finally: + self.openocd.stop() diff --git a/scripts/flipper/utils/register.py b/scripts/flipper/utils/register.py index 26d66730c..aad75eaca 100644 --- a/scripts/flipper/utils/register.py +++ b/scripts/flipper/utils/register.py @@ -16,6 +16,7 @@ class Register32: self.names = [definition.name for definition in definition_list] # typecheck self.address = address self.definition_list = definition_list + self.openocd = None # Validate that the definitions are not overlapping for i in range(len(definition_list)): @@ -76,6 +77,14 @@ class Register32: def __dir__(self): return self.names + def set_openocd(self, openocd: OpenOCD): + self.openocd = openocd + + def get_openocd(self) -> OpenOCD: + if self.openocd is None: + raise RuntimeError("OpenOCD is not installed") + return self.openocd + def set(self, value: int): for definition in self.definition_list: definition.value = (value >> definition.offset) & ( @@ -88,8 +97,8 @@ class Register32: value |= definition.value << definition.offset return value - def load(self, openocd: OpenOCD): - self.set(openocd.read_32(self.address)) + def load(self): + self.set(self.get_openocd().read_32(self.address)) - def store(self, openocd: OpenOCD): - openocd.write_32(self.address, self.get()) + def store(self): + self.get_openocd().write_32(self.address, self.get()) diff --git a/scripts/flipper/utils/stm32wb55.py b/scripts/flipper/utils/stm32wb55.py index 52a5ec4e3..4a47b8bea 100644 --- a/scripts/flipper/utils/stm32wb55.py +++ b/scripts/flipper/utils/stm32wb55.py @@ -108,23 +108,27 @@ class STM32WB55: 15: None, # Core 2 Options } - def __init__(self): + def __init__(self, openocd: OpenOCD): + self.openocd = openocd self.logger = logging.getLogger("STM32WB55") + self.FLASH_CR.set_openocd(self.openocd) + self.FLASH_SR.set_openocd(self.openocd) + class RunMode(Enum): Init = "init" Run = "run" Halt = "halt" - def reset(self, oocd: OpenOCD, mode: RunMode): + def reset(self, mode: RunMode): self.logger.debug("Resetting device") - oocd.send_tcl(f"reset {mode.value}") + self.openocd.send_tcl(f"reset {mode.value}") - def clear_flash_errors(self, oocd: OpenOCD): + def clear_flash_errors(self): # Errata 2.2.9: Flash OPTVERR flag is always set after system reset # And also clear all other flash error flags self.logger.debug("Resetting flash errors") - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() self.FLASH_SR.OP_ERR = 1 self.FLASH_SR.PROG_ERR = 1 self.FLASH_SR.WRP_ERR = 1 @@ -135,51 +139,51 @@ class STM32WB55: self.FLASH_SR.FAST_ERR = 1 self.FLASH_SR.RD_ERR = 1 self.FLASH_SR.OPTV_ERR = 1 - self.FLASH_SR.store(oocd) + self.FLASH_SR.store() - def flash_unlock(self, oocd: OpenOCD): + def flash_unlock(self): # Check if flash is already unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 0: self.logger.debug("Flash is already unlocked") return # Unlock flash self.logger.debug("Unlocking Flash") - oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1) - oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2) + self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1) + self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2) # Check if flash is unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 0: self.logger.debug("Flash unlocked") else: self.logger.error("Flash unlock failed") raise Exception("Flash unlock failed") - def option_bytes_unlock(self, oocd: OpenOCD): + def option_bytes_unlock(self): # Check if options is already unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 0: self.logger.debug("Options is already unlocked") return # Unlock options self.logger.debug("Unlocking Options") - oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1) - oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2) + self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1) + self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2) # Check if options is unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 0: self.logger.debug("Options unlocked") else: self.logger.error("Options unlock failed") raise Exception("Options unlock failed") - def option_bytes_lock(self, oocd: OpenOCD): + def option_bytes_lock(self): # Check if options is already locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 1: self.logger.debug("Options is already locked") return @@ -187,19 +191,19 @@ class STM32WB55: # Lock options self.logger.debug("Locking Options") self.FLASH_CR.OPT_LOCK = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Check if options is locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 1: self.logger.debug("Options locked") else: self.logger.error("Options lock failed") raise Exception("Options lock failed") - def flash_lock(self, oocd: OpenOCD): + def flash_lock(self): # Check if flash is already locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 1: self.logger.debug("Flash is already locked") return @@ -207,31 +211,31 @@ class STM32WB55: # Lock flash self.logger.debug("Locking Flash") self.FLASH_CR.LOCK = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Check if flash is locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 1: self.logger.debug("Flash locked") else: self.logger.error("Flash lock failed") raise Exception("Flash lock failed") - def option_bytes_apply(self, oocd: OpenOCD): + def option_bytes_apply(self): self.logger.debug("Applying Option Bytes") - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.OPT_STRT = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Wait for Option Bytes to be applied - self.flash_wait_for_operation(oocd) + self.flash_wait_for_operation() - def option_bytes_load(self, oocd: OpenOCD): + def option_bytes_load(self): self.logger.debug("Loading Option Bytes") - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.OBL_LAUNCH = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() def option_bytes_id_to_address(self, id: int) -> int: # Check if this option byte (dword) is mapped to a register @@ -241,16 +245,16 @@ class STM32WB55: return device_reg_addr - def flash_wait_for_operation(self, oocd: OpenOCD): + def flash_wait_for_operation(self): # Wait for flash operation to complete # TODO: timeout while True: - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() if self.FLASH_SR.BSY == 0: break - def flash_dump_status_register(self, oocd: OpenOCD): - self.FLASH_SR.load(oocd) + def flash_dump_status_register(self): + self.FLASH_SR.load() self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}") if self.FLASH_SR.EOP: self.logger.info(" End of operation") @@ -283,70 +287,87 @@ class STM32WB55: if self.FLASH_SR.PESD: self.logger.info(" Programming / erase operation suspended.") - def write_flash_64(self, oocd: OpenOCD, address: int, word_1: int, word_2: int): + def write_flash_64(self, address: int, word_1: int, word_2: int): self.logger.debug(f"Writing flash at address {address:08x}") if address % 8 != 0: self.logger.error("Address must be aligned to 8 bytes") raise Exception("Address must be aligned to 8 bytes") - if word_1 == oocd.read_32(address) and word_2 == oocd.read_32(address + 4): + if word_1 == self.openocd.read_32(address) and word_2 == self.openocd.read_32( + address + 4 + ): self.logger.debug("Data is already programmed") return - self.flash_unlock(oocd) + self.flash_unlock() # Check that no flash main memory operation is ongoing by checking the BSY bit - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() if self.FLASH_SR.BSY: self.logger.error("Flash is busy") - self.flash_dump_status_register(oocd) + self.flash_dump_status_register() raise Exception("Flash is busy") # Enable end of operation interrupts and error interrupts - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.EOPIE = 1 self.FLASH_CR.ERRIE = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Check that flash memory program and erase operations are allowed if self.FLASH_SR.PESD: self.logger.error("Flash operations are not allowed") - self.flash_dump_status_register(oocd) + self.flash_dump_status_register() raise Exception("Flash operations are not allowed") # Check and clear all error programming flags due to a previous programming. - self.clear_flash_errors(oocd) + self.clear_flash_errors() # Set the PG bit in the Flash memory control register (FLASH_CR) - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.PG = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed. # Write the first word - oocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}") + self.openocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}") # Write the second word - oocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}") + self.openocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}") # Wait for the BSY bit to be cleared - self.flash_wait_for_operation(oocd) + self.flash_wait_for_operation() # Check that EOP flag is set in the FLASH_SR register - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() if not self.FLASH_SR.EOP: self.logger.error("Flash operation failed") - self.flash_dump_status_register(oocd) + self.flash_dump_status_register() raise Exception("Flash operation failed") # Clear the EOP flag - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() self.FLASH_SR.EOP = 1 - self.FLASH_SR.store(oocd) + self.FLASH_SR.store() # Clear the PG bit in the FLASH_CR register - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.PG = 0 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() - self.flash_lock(oocd) + self.flash_lock() + + def option_bytes_recover(self): + self.openocd.send_tcl("mww 0x58004010 0x8000") # set OPTVERR to reset + # Replace flash_unlock and option_bytes_unlock with the following lines, if this does not work + # self.openocd.send_tcl("mww 0x58004008 0x45670123") # unlock FLASH + # self.openocd.send_tcl("mww 0x58004008 0xCDEF89AB") + # self.openocd.send_tcl("mww 0x5800400c 0x08192A3B") # unlock OB + # self.openocd.send_tcl("mww 0x5800400c 0x4C5D6E7F") + self.flash_unlock() + self.option_bytes_unlock() + self.openocd.send_tcl("mmw 0x58004020 0x3ffff1aa 0xffffffff") # Reset OB + self.openocd.send_tcl("mww 0x5800402c 0xff") # Reset WRP1AR + self.openocd.send_tcl("mww 0x58004030 0xff") # Reset WRP1BR + self.openocd.send_tcl("mmw 0x58004014 0x00020000 0") # OPTSTRT + self.openocd.send_tcl("mmw 0x58004014 0x08000000 0") # OBL_LAUNCH diff --git a/scripts/ob.py b/scripts/ob.py index 7010bdec5..b7a601612 100755 --- a/scripts/ob.py +++ b/scripts/ob.py @@ -22,6 +22,12 @@ class Main(App): self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes") self._add_args(self.parser_set) self.parser_set.set_defaults(func=self.set) + # Set command + self.parser_recover = self.subparsers.add_parser( + "recover", help="Recover Option Bytes" + ) + self._add_args(self.parser_recover) + self.parser_recover.set_defaults(func=self.recover) def _add_args(self, parser): parser.add_argument( @@ -75,6 +81,20 @@ class Main(App): return return_code + def recover(self): + self.logger.info("Setting Option Bytes") + + # OpenOCD + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + openocd.option_bytes_recover() + + return 0 + if __name__ == "__main__": Main()() From c40e4ba01e2ce8083e8c462d23ce2bd9b00ecc44 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Thu, 10 Aug 2023 12:53:12 +0300 Subject: [PATCH 14/99] FBT: devboard_flash to update WiFi devboard (#2968) --- SConstruct | 3 +++ documentation/fbt.md | 1 + 2 files changed, 4 insertions(+) diff --git a/SConstruct b/SConstruct index b51154f70..d968254b1 100644 --- a/SConstruct +++ b/SConstruct @@ -327,6 +327,9 @@ distenv.PhonyTarget( "cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}" ) +# Update WiFi devboard firmware +distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") + # Find blackmagic probe distenv.PhonyTarget( diff --git a/documentation/fbt.md b/documentation/fbt.md index c19780ef5..1ab67b4e6 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -69,6 +69,7 @@ To run cleanup (think of `make clean`) for specified targets, add the `-c` optio - `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded. - `debug_other`, `debug_other_blackmagic` - attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB. - `updater_debug` - attach GDB with the updater's `.elf` loaded. +- `devboard_flash` - update WiFi dev board with the latest firmware. - `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board). - `openocd` - just start OpenOCD. - `get_blackmagic` - output the blackmagic address in the GDB remote format. Useful for IDE integration. From ee64a962d978b1f570228c096902dbe8c7e48770 Mon Sep 17 00:00:00 2001 From: gid9798 <30450294+gid9798@users.noreply.github.com> Date: Thu, 10 Aug 2023 14:48:42 +0300 Subject: [PATCH 15/99] NFC App: Add scene for MF Classic custom UID --- .../main/nfc/helpers/nfc_custom_event.h | 9 +- .../main/nfc/scenes/nfc_scene_config.h | 1 + .../main/nfc/scenes/nfc_scene_save_name.c | 3 - .../main/nfc/scenes/nfc_scene_set_type.c | 79 +------------- .../nfc/scenes/nfc_scene_set_type_mf_uid.c | 103 ++++++++++++++++++ .../main/nfc/scenes/nfc_scene_set_uid.c | 63 +++-------- 6 files changed, 127 insertions(+), 131 deletions(-) create mode 100644 applications/main/nfc/scenes/nfc_scene_set_type_mf_uid.c diff --git a/applications/main/nfc/helpers/nfc_custom_event.h b/applications/main/nfc/helpers/nfc_custom_event.h index b2ad3170c..00feb8484 100644 --- a/applications/main/nfc/helpers/nfc_custom_event.h +++ b/applications/main/nfc/helpers/nfc_custom_event.h @@ -14,11 +14,4 @@ enum NfcCustomEvent { NfcCustomEventRpcSessionClose, NfcCustomEventUpdateLog, NfcCustomEventSaveShadow, -}; - -enum NfcSceneSetUidState { - NfcSceneSetUidStateNotSet, - NfcSceneSetUidStateMFClassic1k, - NfcSceneSetUidStateMFClassic4k, - NfcSceneSetUidStateMFClassicMini, -}; +}; \ No newline at end of file diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index f11d14798..6232aaf30 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -3,6 +3,7 @@ ADD_SCENE(nfc, read, Read) ADD_SCENE(nfc, saved_menu, SavedMenu) ADD_SCENE(nfc, extra_actions, ExtraActions) ADD_SCENE(nfc, set_type, SetType) +ADD_SCENE(nfc, set_type_mf_uid, SetTypeMfUid) ADD_SCENE(nfc, set_sak, SetSak) ADD_SCENE(nfc, set_atqa, SetAtqa) ADD_SCENE(nfc, set_uid, SetUid) diff --git a/applications/main/nfc/scenes/nfc_scene_save_name.c b/applications/main/nfc/scenes/nfc_scene_save_name.c index 327a31292..a7b97aac0 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_name.c +++ b/applications/main/nfc/scenes/nfc_scene_save_name.c @@ -67,9 +67,6 @@ bool nfc_scene_save_name_on_event(void* context, SceneManagerEvent event) { if(!scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { // Nothing, do not count editing as saving } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetType)) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateNotSet); - dolphin_deed(DolphinDeedNfcAddSave); } else { dolphin_deed(DolphinDeedNfcSave); diff --git a/applications/main/nfc/scenes/nfc_scene_set_type.c b/applications/main/nfc/scenes/nfc_scene_set_type.c index d17b7616f..b6f8d3bce 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_type.c +++ b/applications/main/nfc/scenes/nfc_scene_set_type.c @@ -4,19 +4,10 @@ enum SubmenuIndex { SubmenuIndexNFCA4, SubmenuIndexNFCA7, - SubmenuIndexMFC1k4Uid, - SubmenuIndexMFC4k4Uid, - SubmenuIndexMFC1k7Uid, - SubmenuIndexMFC4k7Uid, - SubmenuIndexMFCMini, + SubmenuIndexMFClassicCustomUID, SubmenuIndexGeneratorsStart, }; -static const NfcGenerator ganeator_gag = { - .name = "Mifare Classic Custom UID", - .generator_func = NULL, -}; - void nfc_scene_set_type_submenu_callback(void* context, uint32_t index) { Nfc* nfc = context; @@ -35,32 +26,8 @@ void nfc_scene_set_type_on_enter(void* context) { submenu, "NFC-A 4-bytes UID", SubmenuIndexNFCA4, nfc_scene_set_type_submenu_callback, nfc); submenu_add_item( submenu, - "MFClassic1k4b Custom uid", - SubmenuIndexMFC1k4Uid, - nfc_scene_set_type_submenu_callback, - nfc); - submenu_add_item( - submenu, - "MFClassic4k4b Custom uid", - SubmenuIndexMFC4k4Uid, - nfc_scene_set_type_submenu_callback, - nfc); - submenu_add_item( - submenu, - "MFClassic1k7b Custom uid", - SubmenuIndexMFC1k7Uid, - nfc_scene_set_type_submenu_callback, - nfc); - submenu_add_item( - submenu, - "MFClassic4k7b Custom uid ", - SubmenuIndexMFC4k7Uid, - nfc_scene_set_type_submenu_callback, - nfc); - submenu_add_item( - submenu, - "MFClassic Mini Custom uid ", - SubmenuIndexMFCMini, + "Mifare Classic Custom UID", + SubmenuIndexMFClassicCustomUID, nfc_scene_set_type_submenu_callback, nfc); @@ -89,45 +56,9 @@ bool nfc_scene_set_type_on_event(void* context, SceneManagerEvent event) { nfc->dev->format = NfcDeviceSaveFormatUid; scene_manager_next_scene(nfc->scene_manager, NfcSceneSetSak); consumed = true; - } else if(event.event == SubmenuIndexMFC1k4Uid) { - nfc->dev->dev_data.nfc_data.uid_len = 4; + } else if(event.event == SubmenuIndexMFClassicCustomUID) { nfc->dev->format = NfcDeviceSaveFormatMifareClassic; - nfc->generator = &ganeator_gag; - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassic1k); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); - consumed = true; - } else if(event.event == SubmenuIndexMFC1k7Uid) { - nfc->dev->dev_data.nfc_data.uid_len = 7; - nfc->dev->format = NfcDeviceSaveFormatMifareClassic; - nfc->generator = &ganeator_gag; - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassic1k); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); - consumed = true; - } else if(event.event == SubmenuIndexMFC4k4Uid) { - nfc->dev->dev_data.nfc_data.uid_len = 4; - nfc->dev->format = NfcDeviceSaveFormatMifareClassic; - nfc->generator = &ganeator_gag; - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassic4k); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); - consumed = true; - } else if(event.event == SubmenuIndexMFC4k7Uid) { - nfc->dev->dev_data.nfc_data.uid_len = 7; - nfc->dev->format = NfcDeviceSaveFormatMifareClassic; - nfc->generator = &ganeator_gag; - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassic4k); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); - consumed = true; - } else if(event.event == SubmenuIndexMFCMini) { - nfc->dev->dev_data.nfc_data.uid_len = 4; - nfc->dev->format = NfcDeviceSaveFormatMifareClassic; - nfc->generator = &ganeator_gag; - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateMFClassicMini); - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetTypeMfUid); consumed = true; } else { nfc_device_clear(nfc->dev); diff --git a/applications/main/nfc/scenes/nfc_scene_set_type_mf_uid.c b/applications/main/nfc/scenes/nfc_scene_set_type_mf_uid.c new file mode 100644 index 000000000..55919500a --- /dev/null +++ b/applications/main/nfc/scenes/nfc_scene_set_type_mf_uid.c @@ -0,0 +1,103 @@ +#include "../nfc_i.h" +#include "lib/nfc/helpers/nfc_generators.h" + +enum SubmenuIndex { + SubmenuIndexMFC1k4b, + SubmenuIndexMFC4k4b, + SubmenuIndexMFC1k7b, + SubmenuIndexMFC4k7b, + SubmenuIndexMFCMini, +}; + +static const NfcGenerator ganeator_gag = { + .name = "Mifare Classic Custom UID", + .generator_func = NULL, +}; + +void nfc_scene_set_type_mf_uid_submenu_callback(void* context, uint32_t index) { + Nfc* nfc = context; + + view_dispatcher_send_custom_event(nfc->view_dispatcher, index); +} + +void nfc_scene_set_type_mf_uid_on_enter(void* context) { + Nfc* nfc = context; + Submenu* submenu = nfc->submenu; + + submenu_add_item( + submenu, + "Mifare Classic 1k 4byte UID", + SubmenuIndexMFC1k4b, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Mifare Classic 4k 4byte UID", + SubmenuIndexMFC4k4b, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Mifare Classic 1k 7byte UID", + SubmenuIndexMFC1k7b, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Mifare Classic 4k 7byte UID", + SubmenuIndexMFC4k7b, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + submenu_add_item( + submenu, + "Mifare Classic Mini", + SubmenuIndexMFCMini, + nfc_scene_set_type_mf_uid_submenu_callback, + nfc); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); +} + +bool nfc_scene_set_type_mf_uid_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + bool correct_index = false; + MfClassicType mf_type = MfClassicType1k; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexMFC1k4b) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + mf_type = MfClassicType1k; + correct_index = true; + } else if(event.event == SubmenuIndexMFC1k7b) { + nfc->dev->dev_data.nfc_data.uid_len = 7; + mf_type = MfClassicType1k; + correct_index = true; + } else if(event.event == SubmenuIndexMFC4k4b) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + mf_type = MfClassicType4k; + correct_index = true; + } else if(event.event == SubmenuIndexMFC4k7b) { + nfc->dev->dev_data.nfc_data.uid_len = 7; + mf_type = MfClassicType4k; + correct_index = true; + } else if(event.event == SubmenuIndexMFCMini) { + nfc->dev->dev_data.nfc_data.uid_len = 4; + mf_type = MfClassicTypeMini; + correct_index = true; + } + if(correct_index) { + nfc->generator = &ganeator_gag; + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSetTypeMfUid, mf_type); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetUid); + consumed = true; + } + } + return consumed; +} + +void nfc_scene_set_type_mf_uid_on_exit(void* context) { + Nfc* nfc = context; + + submenu_reset(nfc->submenu); +} diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index d1df352f1..80ea5f6d0 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -27,10 +27,7 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = (Nfc*)context; bool consumed = false; - if(event.type == SceneManagerEventTypeBack) { - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneSetUid, NfcSceneSetUidStateNotSet); - } else if(event.type == SceneManagerEventTypeCustom) { + if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSavedMenu)) { nfc->dev->dev_data.nfc_data = nfc->dev_edit_data; @@ -38,50 +35,24 @@ bool nfc_scene_set_uid_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveSuccess); consumed = true; } - } else { - switch(scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSetUid)) { - case NfcSceneSetUidStateMFClassic1k: - nfc_generate_mf_classic_ext( - &nfc->dev->dev_data, - nfc->dev_edit_data.uid_len, - MfClassicType1k, - false, - nfc->dev_edit_data.uid); - scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); - consumed = true; - break; - - case NfcSceneSetUidStateMFClassic4k: - nfc_generate_mf_classic_ext( - &nfc->dev->dev_data, - nfc->dev_edit_data.uid_len, - MfClassicType4k, - false, - nfc->dev_edit_data.uid); - scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); - consumed = true; - break; - - case NfcSceneSetUidStateMFClassicMini: - nfc_generate_mf_classic_ext( - &nfc->dev->dev_data, - nfc->dev_edit_data.uid_len, - MfClassicTypeMini, - false, - nfc->dev_edit_data.uid); - scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); - consumed = true; - break; - - case NfcSceneSetUidStateNotSet: - scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); - consumed = true; - break; - - default: + } else if(scene_manager_has_previous_scene(nfc->scene_manager, NfcSceneSetTypeMfUid)) { + MfClassicType mf_type = + scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSetTypeMfUid); + if(mf_type > MfClassicTypeMini) { furi_crash("Nfc unknown type"); - break; } + nfc_generate_mf_classic_ext( + &nfc->dev->dev_data, + nfc->dev_edit_data.uid_len, + mf_type, + false, + nfc->dev_edit_data.uid); + scene_manager_next_scene(nfc->scene_manager, NfcSceneGenerateInfo); + consumed = true; + + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName); + consumed = true; } } } From 498aee20a20d3e157c578780ab9df5fe05834632 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Thu, 10 Aug 2023 15:29:44 +0300 Subject: [PATCH 16/99] uFBT: devboard_flash to update WiFi devboard (#2969) * uFBT: devboard_flash to update WiFi devboard * uFBT: help --- scripts/ufbt/SConstruct | 3 +++ scripts/ufbt/site_tools/ufbt_help.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 9a9e0938c..342cd2532 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -348,6 +348,9 @@ appenv.PhonyTarget( '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}', ) +# Update WiFi devboard firmware +dist_env.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") + # Linter dist_env.PhonyTarget( diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py index 3f13edcdb..3d7f6f002 100644 --- a/scripts/ufbt/site_tools/ufbt_help.py +++ b/scripts/ufbt/site_tools/ufbt_help.py @@ -26,6 +26,8 @@ Flashing & debugging: Install firmware using self-update package debug, debug_other, blackmagic: Start GDB + devboard_flash: + Update WiFi dev board with the latest firmware Other: cli: From f75fcd4e34d921397c0fb23a1956ecd82af73b13 Mon Sep 17 00:00:00 2001 From: MMX <10697207+xMasterX@users.noreply.github.com> Date: Thu, 10 Aug 2023 19:10:15 +0300 Subject: [PATCH 17/99] UI: Clock on Desktop (#2891) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Clock on desktop * Gui: gui_active_view_port_count * Gui: move gui_active_view_port_count to private header, update docs * Desktop: simplify desktop clock code * Desktop: refactor clock * Desktop: optimize clock code * Desktop: 3rd cleanup round * Desktop: 4th cleanup round, missing bits and pieces Co-authored-by: hedger Co-authored-by: あく --- applications/services/desktop/desktop.c | 91 ++++++++++++++++++- applications/services/desktop/desktop_i.h | 8 +- .../services/desktop/desktop_settings.h | 3 +- applications/services/gui/gui.c | 20 ++++ applications/services/gui/gui_i.h | 28 ++++++ .../scenes/desktop_settings_scene_start.c | 30 ++++++ firmware/targets/f18/api_symbols.csv | 1 + firmware/targets/f7/api_symbols.csv | 1 + 8 files changed, 179 insertions(+), 3 deletions(-) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 1233af893..30e7253da 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include "animations/animation_manager.h" #include "desktop/scenes/desktop_scene.h" @@ -36,7 +38,6 @@ static void desktop_loader_callback(const void* message, void* context) { 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); @@ -49,6 +50,65 @@ static void desktop_dummy_mode_icon_draw_callback(Canvas* canvas, void* context) canvas_draw_icon(canvas, 0, 0, &I_GameMode_11x8); } +static void desktop_clock_update(Desktop* desktop) { + furi_assert(desktop); + + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + bool time_format_12 = locale_get_time_format() == LocaleTimeFormat12h; + + if(desktop->time_hour != curr_dt.hour || desktop->time_minute != curr_dt.minute || + desktop->time_format_12 != time_format_12) { + desktop->time_format_12 = time_format_12; + desktop->time_hour = curr_dt.hour; + desktop->time_minute = curr_dt.minute; + view_port_update(desktop->clock_viewport); + } +} + +static void desktop_clock_reconfigure(Desktop* desktop) { + furi_assert(desktop); + + desktop_clock_update(desktop); + + if(desktop->settings.display_clock) { + furi_timer_start(desktop->update_clock_timer, furi_ms_to_ticks(1000)); + } else { + furi_timer_stop(desktop->update_clock_timer); + } + + view_port_enabled_set(desktop->clock_viewport, desktop->settings.display_clock); +} + +static void desktop_clock_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + furi_assert(canvas); + + Desktop* desktop = context; + + canvas_set_font(canvas, FontPrimary); + + uint8_t hour = desktop->time_hour; + if(desktop->time_format_12) { + if(hour > 12) { + hour -= 12; + } + if(hour == 0) { + hour = 12; + } + } + + char buffer[20]; + snprintf(buffer, sizeof(buffer), "%02u:%02u", hour, desktop->time_minute); + + // ToDo: never do that, may cause visual glitches + view_port_set_width( + desktop->clock_viewport, + canvas_string_width(canvas, buffer) - 1 + (desktop->time_minute % 10 == 1)); + + canvas_draw_str_aligned(canvas, 0, 8, AlignLeft, AlignBottom, buffer); +} + static void desktop_stealth_mode_icon_draw_callback(Canvas* canvas, void* context) { UNUSED(context); furi_assert(canvas); @@ -69,6 +129,9 @@ static bool desktop_custom_event_callback(void* context, uint32_t event) { // TODO: Implement a message mechanism for loading settings and (optionally) // locking and unlocking DESKTOP_SETTINGS_LOAD(&desktop->settings); + + desktop_clock_reconfigure(desktop); + desktop_auto_lock_arm(desktop); return true; case DesktopGlobalAutoLock: @@ -134,6 +197,19 @@ static void desktop_auto_lock_inhibit(Desktop* desktop) { } } +static void desktop_clock_timer_callback(void* context) { + furi_assert(context); + Desktop* desktop = context; + + if(gui_active_view_port_count(desktop->gui, GuiLayerStatusBarLeft) < 6) { + desktop_clock_update(desktop); + + view_port_enabled_set(desktop->clock_viewport, true); + } else { + view_port_enabled_set(desktop->clock_viewport, false); + } +} + void desktop_lock(Desktop* desktop) { furi_hal_rtc_set_flag(FuriHalRtcFlagLock); @@ -286,6 +362,13 @@ Desktop* desktop_alloc() { view_port_enabled_set(desktop->dummy_mode_icon_viewport, false); gui_add_view_port(desktop->gui, desktop->dummy_mode_icon_viewport, GuiLayerStatusBarLeft); + // Clock + desktop->clock_viewport = view_port_alloc(); + view_port_set_width(desktop->clock_viewport, 25); + view_port_draw_callback_set(desktop->clock_viewport, desktop_clock_draw_callback, desktop); + view_port_enabled_set(desktop->clock_viewport, false); + gui_add_view_port(desktop->gui, desktop->clock_viewport, GuiLayerStatusBarRight); + // Stealth mode icon desktop->stealth_mode_icon_viewport = view_port_alloc(); view_port_set_width(desktop->stealth_mode_icon_viewport, icon_get_width(&I_Muted_8x8)); @@ -317,6 +400,9 @@ Desktop* desktop_alloc() { desktop->status_pubsub = furi_pubsub_alloc(); + desktop->update_clock_timer = + furi_timer_alloc(desktop_clock_timer_callback, FuriTimerTypePeriodic, desktop); + furi_record_create(RECORD_DESKTOP, desktop); return desktop; @@ -362,6 +448,9 @@ int32_t desktop_srv(void* p) { } view_port_enabled_set(desktop->dummy_mode_icon_viewport, desktop->settings.dummy_mode); + + desktop_clock_reconfigure(desktop); + desktop_main_set_dummy_mode_state(desktop->main_view, desktop->settings.dummy_mode); animation_manager_set_dummy_mode_state( desktop->animation_manager, desktop->settings.dummy_mode); diff --git a/applications/services/desktop/desktop_i.h b/applications/services/desktop/desktop_i.h index 0b3d56801..bb495c920 100644 --- a/applications/services/desktop/desktop_i.h +++ b/applications/services/desktop/desktop_i.h @@ -59,6 +59,7 @@ struct Desktop { ViewPort* lock_icon_viewport; ViewPort* dummy_mode_icon_viewport; + ViewPort* clock_viewport; ViewPort* stealth_mode_icon_viewport; AnimationManager* animation_manager; @@ -70,10 +71,15 @@ struct Desktop { FuriPubSub* input_events_pubsub; FuriPubSubSubscription* input_events_subscription; FuriTimer* auto_lock_timer; + FuriTimer* update_clock_timer; FuriPubSub* status_pubsub; - bool in_transition; + uint8_t time_hour; + uint8_t time_minute; + bool time_format_12 : 1; // 1 - 12 hour, 0 - 24H + + bool in_transition : 1; }; Desktop* desktop_alloc(); diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index 9b88868a8..a189f9f05 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -8,7 +8,7 @@ #include #include -#define DESKTOP_SETTINGS_VER (8) +#define DESKTOP_SETTINGS_VER (9) #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) #define DESKTOP_SETTINGS_MAGIC (0x17) @@ -51,4 +51,5 @@ typedef struct { PinCode pin_code; uint32_t auto_lock_delay_ms; uint8_t dummy_mode; + uint8_t display_clock; } DesktopSettings; diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 392011620..b96f89db9 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -17,6 +17,26 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array) { return NULL; } +size_t gui_active_view_port_count(Gui* gui, GuiLayer layer) { + furi_assert(gui); + furi_check(layer < GuiLayerMAX); + size_t ret = 0; + + gui_lock(gui); + ViewPortArray_it_t it; + ViewPortArray_it_last(it, gui->layers[layer]); + while(!ViewPortArray_end_p(it)) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + ret++; + } + ViewPortArray_previous(it); + } + gui_unlock(gui); + + return ret; +} + void gui_update(Gui* gui) { furi_assert(gui); if(!gui->direct_draw) furi_thread_flags_set(gui->thread_id, GUI_THREAD_FLAG_DRAW); diff --git a/applications/services/gui/gui_i.h b/applications/services/gui/gui_i.h index a5cd84120..a5e269e03 100644 --- a/applications/services/gui/gui_i.h +++ b/applications/services/gui/gui_i.h @@ -75,6 +75,12 @@ struct Gui { ViewPort* ongoing_input_view_port; }; +/** Find enabled ViewPort in ViewPortArray + * + * @param[in] array The ViewPortArray instance + * + * @return ViewPort instance or NULL + */ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); /** Update GUI, request redraw @@ -83,8 +89,30 @@ ViewPort* gui_view_port_find_enabled(ViewPortArray_t array); */ void gui_update(Gui* gui); +/** Input event callback + * + * Used to receive input from input service or to inject new input events + * + * @param[in] value The value pointer (InputEvent*) + * @param ctx The context (Gui instance) + */ void gui_input_events_callback(const void* value, void* ctx); +/** Get count of view ports in layer + * + * @param gui The Gui instance + * @param[in] layer GuiLayer that we want to get count of view ports + */ +size_t gui_active_view_port_count(Gui* gui, GuiLayer layer); + +/** Lock GUI + * + * @param gui The Gui instance + */ void gui_lock(Gui* gui); +/** Unlock GUI + * + * @param gui The Gui instance + */ void gui_unlock(Gui* gui); diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 8530a1a7d..02acd51ca 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -8,6 +8,7 @@ #define SCENE_EVENT_SELECT_FAVORITE_SECONDARY 1 #define SCENE_EVENT_SELECT_PIN_SETUP 2 #define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 3 +#define SCENE_EVENT_SELECT_CLOCK_DISPLAY 4 #define AUTO_LOCK_DELAY_COUNT 6 const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { @@ -22,11 +23,27 @@ const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] = {0, 30000, 60000, 120000, 300000, 600000}; +#define CLOCK_ENABLE_COUNT 2 +const char* const clock_enable_text[CLOCK_ENABLE_COUNT] = { + "OFF", + "ON", +}; + +const uint32_t clock_enable_value[CLOCK_ENABLE_COUNT] = {0, 1}; + static void desktop_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) { DesktopSettingsApp* app = context; view_dispatcher_send_custom_event(app->view_dispatcher, index); } +static void desktop_settings_scene_start_clock_enable_changed(VariableItem* item) { + DesktopSettingsApp* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, clock_enable_text[index]); + app->settings.display_clock = index; +} + static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* item) { DesktopSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -62,6 +79,18 @@ void desktop_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, auto_lock_delay_text[value_index]); + item = variable_item_list_add( + variable_item_list, + "Show Clock", + CLOCK_ENABLE_COUNT, + desktop_settings_scene_start_clock_enable_changed, // + app); + + value_index = + value_index_uint32(app->settings.display_clock, clock_enable_value, CLOCK_ENABLE_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, clock_enable_text[value_index]); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); } @@ -86,6 +115,7 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even consumed = true; break; case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: + case SCENE_EVENT_SELECT_CLOCK_DISPLAY: consumed = true; break; } diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 081fe502f..eab140d5c 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1471,6 +1471,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* +Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 535582c0c..8efe980a7 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1642,6 +1642,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* +Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" From 7178bd20cf883228b81133d50ea174a19b246eb7 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 10 Aug 2023 19:21:56 +0300 Subject: [PATCH 18/99] ufbt: fixed FAP_SRC_DIR (#2970) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt, ufbt: fixed "_appdir" internal property and FAP_SRC_DIR not working in ufbt environment * fbt, ufbt: reworked CompileIcons(); added app's own root to app's #include path * fbt: cleaner resolve_real_dir_node Co-authored-by: あく --- scripts/fbt/appmanifest.py | 3 ++- scripts/fbt/util.py | 12 ++---------- scripts/fbt_tools/fbt_assets.py | 18 ++++++++---------- scripts/fbt_tools/fbt_extapps.py | 9 +++++---- scripts/ufbt/SConstruct | 32 +++++++++++++++----------------- site_scons/environ.scons | 4 ++-- 6 files changed, 34 insertions(+), 44 deletions(-) diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 067b4a26f..a677d8413 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -2,6 +2,7 @@ import os import re from dataclasses import dataclass, field from enum import Enum +from fbt.util import resolve_real_dir_node from typing import Callable, ClassVar, List, Optional, Tuple, Union @@ -152,7 +153,7 @@ class AppManager: FlipperApplication( *args, **kw, - _appdir=app_dir_node, + _appdir=resolve_real_dir_node(app_dir_node), _apppath=os.path.dirname(app_manifest_path), _appmanager=self, ), diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index 57e60aecf..fb36ef55a 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -45,7 +45,7 @@ def single_quote(arg_list): return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list) -def extract_abs_dir(node): +def resolve_real_dir_node(node): if isinstance(node, SCons.Node.FS.EntryProxy): node = node.get() @@ -53,15 +53,7 @@ def extract_abs_dir(node): if os.path.exists(repo_dir.abspath): return repo_dir - -def extract_abs_dir_path(node): - abs_dir_node = extract_abs_dir(node) - if abs_dir_node is None: - raise StopError(f"Can't find absolute path for {node.name}") - - # Don't return abspath attribute (type is str), it will break in - # OverrideEnvironment.subst_list() by splitting path on spaces - return abs_dir_node + raise StopError(f"Can't find absolute path for {node.name} ({node})") def path_as_posix(path): diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index b2b9310ba..4f4d3bffd 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -8,11 +8,14 @@ from SCons.Errors import StopError def icons_emitter(target, source, env): + icons_src = env.GlobRecursive("*.png", env["ICON_SRC_DIR"]) + icons_src += env.GlobRecursive("frame_rate", env["ICON_SRC_DIR"]) + target = [ target[0].File(env.subst("${ICON_FILE_NAME}.c")), target[0].File(env.subst("${ICON_FILE_NAME}.h")), ] - return target, source + return target, icons_src def proto_emitter(target, source, env): @@ -104,17 +107,12 @@ def proto_ver_generator(target, source, env): def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons"): - # Gathering icons sources - icons_src = env.GlobRecursive("*.png", source_dir) - icons_src += env.GlobRecursive("frame_rate", source_dir) - - icons = env.IconBuilder( + return env.IconBuilder( target_dir, - source_dir, + None, + ICON_SRC_DIR=source_dir, ICON_FILE_NAME=icon_bundle_name, ) - env.Depends(icons, icons_src) - return icons def generate(env): @@ -137,7 +135,7 @@ def generate(env): BUILDERS={ "IconBuilder": Builder( action=Action( - '${PYTHON3} ${ASSETS_COMPILER} icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename "${ICON_FILE_NAME}"', + '${PYTHON3} ${ASSETS_COMPILER} icons ${ICON_SRC_DIR} ${TARGET.dir} --filename "${ICON_FILE_NAME}"', "${ICONSCOMSTR}", ), emitter=icons_emitter, diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index 1766d4c44..642c1c989 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -11,7 +11,7 @@ from fbt.appmanifest import FlipperApplication, FlipperAppType, FlipperManifestE from fbt.elfmanifest import assemble_manifest_data from fbt.fapassets import FileBundler from fbt.sdk.cache import SdkCache -from fbt.util import extract_abs_dir_path +from fbt.util import resolve_real_dir_node from SCons.Action import Action from SCons.Builder import Builder from SCons.Errors import UserError @@ -50,7 +50,8 @@ class AppBuilder: def _setup_app_env(self): self.app_env = self.fw_env.Clone( - FAP_SRC_DIR=self.app._appdir, FAP_WORK_DIR=self.app_work_dir + FAP_SRC_DIR=self.app._appdir, + FAP_WORK_DIR=self.app_work_dir, ) self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False) @@ -119,7 +120,7 @@ class AppBuilder: CPPDEFINES=lib_def.cdefines, CPPPATH=list( map( - lambda cpath: extract_abs_dir_path(self.app._appdir.Dir(cpath)), + lambda cpath: resolve_real_dir_node(self.app._appdir.Dir(cpath)), lib_def.cincludes, ) ), @@ -133,7 +134,7 @@ class AppBuilder: def _build_app(self): self.app_env.Append( LIBS=[*self.app.fap_libs, *self.private_libs], - CPPPATH=self.app_env.Dir(self.app_work_dir), + CPPPATH=[self.app_env.Dir(self.app_work_dir), self.app._appdir], ) app_sources = list( diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 342cd2532..1c2f2bdf5 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -1,12 +1,11 @@ -from SCons.Platform import TempFileMunge -from SCons.Node import FS -from SCons.Errors import UserError - - -import os import multiprocessing +import os import pathlib +from SCons.Errors import UserError +from SCons.Node import FS +from SCons.Platform import TempFileMunge + SetOption("num_jobs", multiprocessing.cpu_count()) SetOption("max_drift", 1) # SetOption("silent", False) @@ -67,16 +66,15 @@ core_env.Append(CPPDEFINES=GetOption("extra_defines")) # Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state -from fbt.util import ( - tempfile_arg_esc_func, - single_quote, - extract_abs_dir, - extract_abs_dir_path, - wrap_tempfile, - path_as_posix, -) -from fbt.appmanifest import FlipperAppType, FlipperApplication +from fbt.appmanifest import FlipperApplication, FlipperAppType from fbt.sdk.cache import SdkCache +from fbt.util import ( + path_as_posix, + resolve_real_dir_node, + single_quote, + tempfile_arg_esc_func, + wrap_tempfile, +) # Base environment with all tools loaded from SDK env = core_env.Clone( @@ -107,7 +105,7 @@ env = core_env.Clone( PROGSUFFIX=".elf", TEMPFILEARGESCFUNC=tempfile_arg_esc_func, SINGLEQUOTEFUNC=single_quote, - ABSPATHGETTERFUNC=extract_abs_dir_path, + ABSPATHGETTERFUNC=resolve_real_dir_node, APPS=[], UFBT_API_VERSION=SdkCache( core_env.subst("$SDK_DEFINITION"), load_version_only=True @@ -277,7 +275,7 @@ for app in known_extapps: continue app_artifacts = appenv.BuildAppElf(app) - app_src_dir = extract_abs_dir(app_artifacts.app._appdir) + app_src_dir = resolve_real_dir_node(app_artifacts.app._appdir) app_artifacts.installer = [ appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact), appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug), diff --git a/site_scons/environ.scons b/site_scons/environ.scons index acdc83e2a..b638b1018 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -3,7 +3,7 @@ from fbt.util import ( tempfile_arg_esc_func, single_quote, wrap_tempfile, - extract_abs_dir_path, + resolve_real_dir_node, ) import os @@ -58,7 +58,7 @@ coreenv = VAR_ENV.Clone( PROGSUFFIX=".elf", ENV=forward_os_env, SINGLEQUOTEFUNC=single_quote, - ABSPATHGETTERFUNC=extract_abs_dir_path, + ABSPATHGETTERFUNC=resolve_real_dir_node, # Setting up temp file parameters - to overcome command line length limits TEMPFILEARGESCFUNC=tempfile_arg_esc_func, ROOT_DIR=Dir("#"), From 12d9b1069cf2f449f19cf63cb1fad7ad6460da40 Mon Sep 17 00:00:00 2001 From: Astra <93453568+Astrrra@users.noreply.github.com> Date: Fri, 11 Aug 2023 01:31:01 +0900 Subject: [PATCH 19/99] [FL-3480] Add the Sad song animation (#2973) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../external/L1_Sad_song_128x64/frame_0.png | Bin 0 -> 1464 bytes .../external/L1_Sad_song_128x64/frame_1.png | Bin 0 -> 1442 bytes .../external/L1_Sad_song_128x64/frame_10.png | Bin 0 -> 1044 bytes .../external/L1_Sad_song_128x64/frame_11.png | Bin 0 -> 1069 bytes .../external/L1_Sad_song_128x64/frame_12.png | Bin 0 -> 980 bytes .../external/L1_Sad_song_128x64/frame_13.png | Bin 0 -> 1150 bytes .../external/L1_Sad_song_128x64/frame_14.png | Bin 0 -> 1153 bytes .../external/L1_Sad_song_128x64/frame_15.png | Bin 0 -> 1137 bytes .../external/L1_Sad_song_128x64/frame_16.png | Bin 0 -> 979 bytes .../external/L1_Sad_song_128x64/frame_17.png | Bin 0 -> 988 bytes .../external/L1_Sad_song_128x64/frame_18.png | Bin 0 -> 1049 bytes .../external/L1_Sad_song_128x64/frame_19.png | Bin 0 -> 259 bytes .../external/L1_Sad_song_128x64/frame_2.png | Bin 0 -> 1483 bytes .../external/L1_Sad_song_128x64/frame_20.png | Bin 0 -> 922 bytes .../external/L1_Sad_song_128x64/frame_21.png | Bin 0 -> 895 bytes .../external/L1_Sad_song_128x64/frame_22.png | Bin 0 -> 831 bytes .../external/L1_Sad_song_128x64/frame_23.png | Bin 0 -> 803 bytes .../external/L1_Sad_song_128x64/frame_24.png | Bin 0 -> 859 bytes .../external/L1_Sad_song_128x64/frame_25.png | Bin 0 -> 854 bytes .../external/L1_Sad_song_128x64/frame_26.png | Bin 0 -> 821 bytes .../external/L1_Sad_song_128x64/frame_27.png | Bin 0 -> 791 bytes .../external/L1_Sad_song_128x64/frame_28.png | Bin 0 -> 871 bytes .../external/L1_Sad_song_128x64/frame_29.png | Bin 0 -> 954 bytes .../external/L1_Sad_song_128x64/frame_3.png | Bin 0 -> 1470 bytes .../external/L1_Sad_song_128x64/frame_30.png | Bin 0 -> 871 bytes .../external/L1_Sad_song_128x64/frame_31.png | Bin 0 -> 926 bytes .../external/L1_Sad_song_128x64/frame_32.png | Bin 0 -> 889 bytes .../external/L1_Sad_song_128x64/frame_33.png | Bin 0 -> 933 bytes .../external/L1_Sad_song_128x64/frame_34.png | Bin 0 -> 873 bytes .../external/L1_Sad_song_128x64/frame_35.png | Bin 0 -> 907 bytes .../external/L1_Sad_song_128x64/frame_36.png | Bin 0 -> 803 bytes .../external/L1_Sad_song_128x64/frame_37.png | Bin 0 -> 769 bytes .../external/L1_Sad_song_128x64/frame_38.png | Bin 0 -> 909 bytes .../external/L1_Sad_song_128x64/frame_39.png | Bin 0 -> 915 bytes .../external/L1_Sad_song_128x64/frame_4.png | Bin 0 -> 1442 bytes .../external/L1_Sad_song_128x64/frame_40.png | Bin 0 -> 918 bytes .../external/L1_Sad_song_128x64/frame_41.png | Bin 0 -> 883 bytes .../external/L1_Sad_song_128x64/frame_42.png | Bin 0 -> 1020 bytes .../external/L1_Sad_song_128x64/frame_43.png | Bin 0 -> 788 bytes .../external/L1_Sad_song_128x64/frame_44.png | Bin 0 -> 997 bytes .../external/L1_Sad_song_128x64/frame_45.png | Bin 0 -> 1061 bytes .../external/L1_Sad_song_128x64/frame_46.png | Bin 0 -> 949 bytes .../external/L1_Sad_song_128x64/frame_47.png | Bin 0 -> 1084 bytes .../external/L1_Sad_song_128x64/frame_48.png | Bin 0 -> 1016 bytes .../external/L1_Sad_song_128x64/frame_49.png | Bin 0 -> 1092 bytes .../external/L1_Sad_song_128x64/frame_5.png | Bin 0 -> 1453 bytes .../external/L1_Sad_song_128x64/frame_50.png | Bin 0 -> 988 bytes .../external/L1_Sad_song_128x64/frame_51.png | Bin 0 -> 1159 bytes .../external/L1_Sad_song_128x64/frame_52.png | Bin 0 -> 991 bytes .../external/L1_Sad_song_128x64/frame_53.png | Bin 0 -> 778 bytes .../external/L1_Sad_song_128x64/frame_54.png | Bin 0 -> 830 bytes .../external/L1_Sad_song_128x64/frame_55.png | Bin 0 -> 720 bytes .../external/L1_Sad_song_128x64/frame_56.png | Bin 0 -> 576 bytes .../external/L1_Sad_song_128x64/frame_57.png | Bin 0 -> 398 bytes .../external/L1_Sad_song_128x64/frame_58.png | Bin 0 -> 474 bytes .../external/L1_Sad_song_128x64/frame_59.png | Bin 0 -> 970 bytes .../external/L1_Sad_song_128x64/frame_6.png | Bin 0 -> 1487 bytes .../external/L1_Sad_song_128x64/frame_60.png | Bin 0 -> 1370 bytes .../external/L1_Sad_song_128x64/frame_61.png | Bin 0 -> 1578 bytes .../external/L1_Sad_song_128x64/frame_62.png | Bin 0 -> 1508 bytes .../external/L1_Sad_song_128x64/frame_7.png | Bin 0 -> 1480 bytes .../external/L1_Sad_song_128x64/frame_8.png | Bin 0 -> 1438 bytes .../external/L1_Sad_song_128x64/frame_9.png | Bin 0 -> 1480 bytes .../external/L1_Sad_song_128x64/meta.txt | 284 ++++++++++++++++++ assets/dolphin/external/manifest.txt | 7 + 65 files changed, 291 insertions(+) create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_0.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_1.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_10.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_11.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_12.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_13.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_14.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_15.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_16.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_17.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_18.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_19.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_2.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_20.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_21.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_22.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_23.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_24.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_25.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_26.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_27.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_28.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_29.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_3.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_30.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_31.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_32.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_33.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_34.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_35.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_36.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_37.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_38.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_39.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_4.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_40.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_41.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_42.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_43.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_44.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_45.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_46.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_47.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_48.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_49.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_5.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_50.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_51.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_52.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_53.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_54.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_55.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_56.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_57.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_58.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_59.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_6.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_60.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_61.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_62.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_7.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_8.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/frame_9.png create mode 100644 assets/dolphin/external/L1_Sad_song_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_0.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..71e85fe8f97f8a8de21ed4f969091bce2a428dfb GIT binary patch literal 1464 zcmV;p1xNacP)Csv{+h&~S&lHa1`1OAZdha+6)rQt5a^FIx?^W=zdjb#k;vv8v$J-O&?h-v4Z%=?~ z@Z4i|ze<}@`-cF39JeRHBk?)ho&YnX^)KP}1b7r6huah2QGgt7Pk=`Oa=1MK9tFtZ z_5^qo;1kh}aC-vmRDgFN-|Y#o(+TeoeqOsLkcH%ge(gNZsjqBDcKO^;!mK3qB?urb z6uJoA$=dbR3PIUhYwEi;)MzNd#lJhObBcF6hzk7i?68U^BBH^Px=z4gulmO_?r}10b z;dBAC9KbUtRB7n=yiYc6qq1L}+bkmh>U;|6!%r)-UXk!=A*XWek?dzlPw`r@XnnGl zWNN#qwPx=4>|#WAQy+77f@aBn6$vg;21*YTiJci}rKE<+T{wajaq3X^BvE# zZ`2W1;)y7eG*?f_I+`0YR&RqQ#-p~c)yJ7yTpu}J0_uFJvfy?`_D5}2{DFx#VWsw^ z>#Z%w0ko`{Pt5wOAITo=gODKloJje#I~*W=T9Xy<_Ai4fK8;2&a&nXeqXf7TedAEo z+DZT|mXFx&Np>nv31`C_3TP$bMJK}T6~IEZ!)K^KwTkS*j@%or+(52AZ?!+ zD~sxrxGX#gpM<4VKt`^td!83K6|hLKk|5Gp-ggE|s(=oSt^IdN1+?~(z0BNpqw-9F z+8f!L9s+o|W-$~g|DFjM2~|rwLhIh2HLtJbKG_iq{%05C*7mhGLD~x(Y&mX z6-fpyw%Rx+58e`F4oBK@WlK$C5+4eog96j56M^qhSMsw*Au zxFrOjF^B)GTG-j=>{09_j|B8^EPNEKIobL-16I7F|E^N6Q+Sf!EVymHCME$Q&gOE= ze`dks_~<3g2$|#5N>s&tD-4R2)Ont#$EhCYOAg4EPPE+NV~p-|Tl9XmLWn$9CjM*l z<+;(w_VIbW&gr9vh!Vge7qS@I9IBGA%yW6hM~LdNS*8HUZ%$^4h2UGkqeVbEJNnz3+BW1+b8D47ZVW3LgtqsK|I( z0%$h0Yoqx=Z5p5e95Z&$3Pe=O(>@BW0ue%t?5b@v1>o@R9JJ4BT$u#YI0zv$dr-SZ z6oA4jIi6%@HzH15MFc)()cLJpbA6!+oF!4bFC{~!?wso~5~KHa!8@lHR4W2F1RRHR+ z5eXtT5m={WeO@DIO3*_cwRu${MA!7ynqA^Wc9Ya+WhfYxsahYa#9c*%nMx^+$7?{O z#2XqN?8=UcOx$y#hlDz5UfNklg@dehL4A=S<$nao8l*NPWvM(L@H!#;0)^j8ExcgM zQZDU&M|iVp3Lp*YHa^%<9kb3Nyz@fsTF!{5Oe8c`g@<2dAb+q-D6ojMyEH@y?kR0O z&P4N*RVS(63J_fkwEZ**D^RIS_0!vSWR7dSb}I1eb+Fm6pv0R&f{J9!N?N_P5oN0y zs{*c*@6}yoKv0`Vp5kW;xA=#MinBC5At+jvrhSNKR SlLUDH0000 zQA#<>Vw~rH6OQA!{7gZg9mgT+kYqg1HAH0Zg4b#T9<0R^0Dl}`9{{^k^k{s20Az#L z9;4+lb+*Po0r1DMeE>WZAH((mFoIhD61ES3hXG>PJ^&sDh++Exco-mt?E~OpfEcz9 zfQJEo;N1w@2f#`O_=e!yJ^)ra;TzJ=d$j?j{9Z`QHS3)jp!HrEdNZ*!0JJ-NGziY~ zY%VPQ-OL5en0Kb_O9DXpsDlFu0%@0_b41YkKqbRr`~#URy)8@+fR>8P} zH2a3v0Gg9BDAdXT)+!n~eyvUih)gMMUo5>0;*At^C$(g(^Lk{oxxBVm`WjKx`iZ8l z~M4Ez5j^x9hP4QTUnq+umxGpAHf3NI}6Sl^jK=a77TEg;~1T9M*jqXR9C@8 zWm$X*;NwccdhEz}1YTK9F1u3q*n!$GtrN(1PRqdF#ZGHHEoxS!h74r(6sW$Qt|ciz zGEA$6GcQK?Zi}>oBg{i>h(Ov{h>h<%?E(rZ7$OTdx_4jETX?N0<|zg>=IGfn{S=UK z`BL@JXT8Ov=^_hAhci=W4<5PU0g$!e5j|+1eFY*nnHlq@1OUIF;5ldnG`gc0ePml1 zJT0JiBJM_z}f3JWs@_7UydYh&EnYb4c@Q_Oy5>$tsuDrqyn=igBHPe0P4@zsK&=GK`=|sis(3JI9t5T2wP5cAQ))ZG(Y+?b$n*6`MBA|=-epGauXrJmjL`kAOi*pBtVWs5+g)1; zt4(j}JZt-rPppj{SwLkljC2HEQT@-vB?Wk?=c3S%4l)*1>ylaKXk8Mm3IvZIyy@st z1h$TNnUH!2*=W%@BE#jWtW}>Ebp2=G*Jt59D(^v(AMrwnol=Vx(aHM-wC9nvL9`-t z)Y0!E+d2hg(4E%brgKE*u{t4ofx@3f)~TgSOF2FEbgv-~fS%(_%7MkK@GfG;;#>7< z&OmZCALrS@S?T$b0{DwX(m^kfR<>1<0JofIEY3s=%UQjX4+BIRJ;-=TM>G{=uvFyu zTgO&J8aCM{Bn0m|G1BO<7ExXuGojgK>48ug%XliYVr@Pjgg}Fhxek07*qoM6N<$f&saYGynhq literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_10.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_10.png new file mode 100644 index 0000000000000000000000000000000000000000..da8f13680c05b8d3fd5e93d57e1a1cdc7bf26444 GIT binary patch literal 1044 zcmV+v1nc{WP)N9~?nyN&WF1&}TH6>h-W=e&u}lQ%~V{`0H_ zTj|^WzVGwCFVu5rQ3+r*`jLY_q6T&(iktcH_z?8qQ)oy2O(GEsfHV40xj~p(jyu3j zoqVGUhy-xM=&$ri-%Wz0aMS^A?9AIla&#Gp1l5%|tAW|?WD*El08#djG_CAJlF22G zIRLimi{`@Gf*)TfOPbGb(&;Eax?z653;xbO8~pNe}($soZxFa(?e((tBGMr zAa|!NQ>RukxPXZOxT?=yJ6Ms;1;_%hvj42XUp-fb_Rk2d14IVjYLzm&PwN8c(}1f0 zM*Hit?>R_1*h+ZWEWamBB`~uEBfEfF0Ftul=slE?quq&S<%4zzM)}BxwaiX)F3M zGB^S3K(<=sw#`rpK$7`#M-AnE7L-zcedg%eZJ0U0uA|U9vdz@n!0!Z>eHsXv0<>0n zG8wEws4s2X&~X(2r(+^Ja`m~bLvW@DR2N9I7jS~r!B*~}*Q}MmuvGx!G+-6rOw)k7 z0MMoZ%KfcIH{vvqt`5**b&0Gi@MvHWz@HX3c$K1prI83CE&YcaMo@#7YIoL zQ4=Qa^mGGT0Z`8ZNYU=4_wTnzF0IILP9SU<1?8N8SO7@M9=QOA zSt6;~k3j056=r`JqJew5M$0NWldLLM4lrvsiVIx*cLAJE9aR9mcO)Z#DFs_6gOU-| zilE-}cESrmrz(TH>16b3+jC+yu(}>4HW&vuf#k4?65<-LCmI00DI1V)=Ps6CQdXHB>O`ruPHpF9#i zsTehfNxfYlQ~N8s_-r#EDuJst{R%6qY>$DR5IXhv zctm9|JO_xzV}zbQRHH%U0NPC#ZlZSz?|^XJz>DxHK#No)QKNB}<$F&9QvsZ^Ed)D6 zu;S>WU=?@=uucXMf?q+{CJy;Wkw`({uo9WoK^AX@0$2q~n*c^4?P$tqj6T>Q#cc}E zjDF9WKo7M(&?a}>c8uEYH4VV$0Fg+=ijv?__$2^T`;T-13EZ|sxxg2R8Wui-ccKUY O0000lK(03DIIdD~g8yE97lDMXUNZ_i{PBt+5n@j}RCP0+@Be|6oNiwN~ zn*!`E0J4EcbLp=m&4CLu0!VlNsYR3Q$lZv)U4twtutk7X-M@l6@g3u97xM(sGFB7A zg23IAwoHp!kwFDa2(Zig?6rd#vZ(-@09Nv!)%de{Wf=bmp>=>r<6C7Z)N@)DKz9R2 z2|%5%kAB~QWPz=Smre3}q^SgEmSAKRP!m8>Hl^<2?4;9%)?_AGlz~M6>-_&DG-Kk< zHH)z+9Crbn8ankp^_~^sq!mn#Vxb3vM_R&=VI2%+Ln;FFi=0t%(Evw)lRHUEAkJ9P z+sNPuumZBwB9CnZ6#*oX@9wVQJkJ89lwY4oUAqlt3b0xz^o49A_15q!f@OCDAud2G zm7c+1B|?2^$A*rh1lU=a$c3DJZmSSGE&^2r(wqf&VAj~W+w_{X5*U^RAi4o717~sr z#|ePu1~|{R+TDn5AYC1xg_R1lMyYnhm?^+$+$bt=_4fjH7L}?1o<5O`08A;^IvAAf zsFnovnYSan5ad-ET)7kK)sDv_8<<^>A{&eXctFyuVudVKK_~)fgTYgIH^;b=6gMIm zgdhM)%Jdh4z8(++u*B>vpL6eD0~`UYY68^Z3@Res00aRf)i>_*{xL^50ea(e51&p1 zd{Q!Mnn_QqKqvu5#*cZSt?uzsgYY151?ylC&G;iwbMgpML;g`DVlc$PjAS138D#~QYCsZTW+-g{ z7zruyIH?)+Hx@)6HL6&V;Ft^0?EaHAfgarYKwHA~7zb4%wr~mXF+e1wWc5tnD3WPN n@QVPb@*k-J5_rZqmTK??hay8P3g!AZ00000NkvXXu0mjfD@e-& literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_12.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_12.png new file mode 100644 index 0000000000000000000000000000000000000000..52ecb01c1ff201494f4c469a03ab2c7f7e060e91 GIT binary patch literal 980 zcmV;_11tQAP)7?9OPWFHkOQ zx_fFVr4=Pw`@5-H>%ad;zVFvswRX+odu+4gUA>o$Us@ZAlKk1eTWjR^vQdkI14K&x z2zS(v=b44y);mB=ekpswcD~2nTAR8ic)7C(cQ} zG?*`h7Ok7;Itk=nZjBPu5p)+&698BB(R&9wBDnxd09y7RHTk>qig5fh-1`8Y$=6z? z%(gSS0CE|4E`Ztf_0?+*Hv!fXUL?!^;_d((B3m%33#bU-rfoX=4rL^vCwe4;GmtL^ zX|MmYfjYuZJi8-k*D-i`6P5=>uj0~gzvLzY03_uFt1hPPok8Orn z05{7VN|3w)S!)fIKq;yYa0;AvU^&Uv76NJ+a4P}Q5ia?@wGbkef#(9?1jag$v*hkB z_-Ndag60AtCxN$UBsa>)rs669&oVH>s^%l()(WQL5lG96QD|i#dLC=wo zQU*i60F))A#SdE`10w)URxY49YO??-0BITMMEU_I zuQC85fbWB&n%n!&8VCWbo*LG00V!4&nO5Ixs(E5Q0ZKP}SF?lHGG~*}$%S*wC z$=7PiAfDR{m`>nsO}|6SD*K(m-F`Lc$4Fg;yzC6RmjDD_j8N;FYHqo9fbV3D3wNSt z#p{HiUCV$d0a9**>NOfq$NRDjY)gQZZ6Vm9_rAx!rTDEBLd*fQI)fdr*U(bj}p3084Jgsx3w<5&Qwd@NQc>685VA0000kfLsu{n7Yw&TLH{r{(@vO#w9-Ocp zbKl?hecty?H4ZIQ1i)5*X5&NXhJ{FRGya_TlI+H(U|jh(kwi=Y&g##K8;Gfey8_%a z$v3KihyXXN{*F25yNR$AxGKPnlX)9ST+_fzpsErl8(2M4i6CqOM9F_9x3VKiC6#bf z0PX~2Ht=dJ^Yu()pu&s*($#-z(j+UA8}W~GP(=k;1lZO6JE#Na7+*V?Cx}+Dniv)Y za(CJ)O=?F46)+(Hm-W?a2NuE8e;AJECRK4>#^{31kgw&b&GmuOw7%U_b7eMevB*~0d`nTdN*`yZF#nT zZl}MuD(1}XOZ%1?DgsD+9(UDH+Z(rGd*4<>f$l-qB_yZ@)AInk`==yOySlNl%yM2m z8wha$+DV`UgZ=vWc?<&Q=dk+_u=pX9fp<7c0M5i%Or$jUnhCKYhIBlcg28e@AQjkT zQ_uL#*RXm)rBy#7i~!zl09&=Y%m90qLa0iFI8K1mZUAai`>a&LGrED&E&x*jDse0c zR%O+X2ql1ZO`wCCa(bqwSZ0z*1wuB1ylVm)<8um7H(*Nu>RA9Ow7Y0fC2+MTFoRMg z(xAc)Vc2#sXJRB1$vFd{1(!&fDuZGIWMcMeKR^qOJIFLY7|ebcq5-#_&h3+QQ%6%4 z-npbKDsc7h0-){Tew~6(2nf?039}9cpXc8)Eq2HQYx8!56TmAN99KV7Fgl6MZXlch zo|s+0NyAsE3Z^1}HW-BNc^I8~JdA>96-YsVEGg3`#s(<}U0Dl4#51CRiip|k;DB&5Wnq-M}F7DS)tdW;P$=Kq)r(5(KG zHGvt_`oLJi_2>swBDQb|>0^LMNXhD%zCk3@kl;T8WR?F&6_CI)#<5g`Z>Chf8%=&^ Qk^lez07*qoM6N<$f;Fu700000 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_14.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_14.png new file mode 100644 index 0000000000000000000000000000000000000000..3ad1f1c2d64d6f0fb0074f9b386f73402e7221ea GIT binary patch literal 1153 zcmV-{1b+L8P){EXOhQ^#(@`xpt(&G-XSgTwc{3IClOysGh^ zXHD3F`Pskkd%o{0^&VE32(ViHm5q;K7#1VN&G->tWqfnNGV0&xS4ScCs3^1MpljhJM1R?eP8rE6$emb zt!fp!xS$hE1km_A>8g>A2MxPAj=2Ep8u%UTcxSlw1MJR6O&mpn`8<5K)o1M7T=(y~ z8%S{h9)6zF%{%*b7z94g@5aQ=uds3*l?=Q?C;=oBqn*gvSC6r%dr-%*KdXD~P&F8A z7XD= zX+B*F?M}fG{qJf8RaD^W-vwZXKe4Uru+k}%X$kWV2B}UVszOMOc{|bxa4HzYWEI;T zM4+-8NGHHaV0HhfN$!#rEJXloFu1!ua(AFnwG0&~L4Yb-roS;ZC_w;EP%7ZLYHvb0 z0(j*Fyh^wc$aDiG2mp<9s&nTbaHJDpHa@rb6tSJtl2P+aSt^i90Lu8;uojS3zb&YS zB>_&Bf-4%|t8!TJ-6*8Dgzfh9D|ksItPz|bIyV88(Jho717ss&g`RGzu|TB&nob)C zX`pusSv7%5Y2tJjV1S*vjukmBrCuz~Ul;2jKBF#Z7b+>t7f z@*hQ3GJ*svl6f_;g2A&s{BW020gHP7Eac2oQK=S6E%jPsd}k8 ThQkno00000NkvXXu0mjf0z(G^ literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_15.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_15.png new file mode 100644 index 0000000000000000000000000000000000000000..dace07e837079e675d4e68f82217f27cf94784f2 GIT binary patch literal 1137 zcmV-%1djWOP)%a8*gN8<;(li6CMEM9DwWva%ydCY89R z08#@WJ9svi@jB8RxHv`tZS;E?O%svQjK8-*78MW?U|08V;7(lPe5)}%h?cRM7?uP| zJ8hYa+K@p7ju1e~`s}rX6|$)Sn*gWeKdbXs^U5&(8A9s-ks`<0GN>j160>LhfE73mBg=AtWS10q+2bd{AX2)_7UI@BX8QeLqFBOkR z#w1-q?pRQ30x$}21018?GJKY*AQSI2+%v{#N?5kjfQXnjLzpapF9#iEg3aqic*140?eEr4eQ>V z(`P`zk^nbb!HCX3Rg^)T+YAtuz}239g;P@5%D|^9fTipkPSmsQvj@U+fM|F`=QPH@|^1YXVDFJTD7J?iiNOAO0um*SqIGqe4 zIKKg*O&s!%B9WXR!HQ(34zhTIB*4m1)&wvTT0?W9G5TPK7H&&`CHg&U0wcKffj+sD zj)NMz*D?T~14KegR+I#fgZ~JCD*urxpn-HOQ7Z5UL>G=LP}zsj00000NkvXXu0mjf DC4v5i literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_16.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_16.png new file mode 100644 index 0000000000000000000000000000000000000000..2f2321888977fb38fdac72e359d24e92c5f79ebf GIT binary patch literal 979 zcmV;^11$WBP)$Z6FdX zrvrc-;8|bB*OB@_MNI(Q+3z)IoEdqV@ju5PiwhhHU|09=P&8Kk}czYXLWzQg52 z&=YPAJ>wu}I6vSnfLqBZ&{C2KAnPC_11Esnf{eZlzzJXnvOtjAHbW!;&NhcKNErt* zD{2m~3m8H`Ypo&eKwbtymH_Dt*LDa^VPtbJR{@+1j75-lzgvRp0_l=K4%GVynP#pQ zVFIvT2D+_N4s>dZAk#7sQVOQR8OW$aj{@Q{ka`KwoM|Wl@g`IN(KP|;d4CSGcB8pK zhyW}t8evc~u@wOIEC842nt-(!QS*^b*b-ow45GUL(lWq45{7i#N$dC(N`E|2aR80S zwzQMMw=;+RXCz`7KyiVqp9@$WJ?q#1>%i0jB9g(CEBqD01rT|I$P46UAkBjz*~lVP zC;?C(4w=2j>tPH^aH(yGevS}A04Q5Zi!Zi72SNav%v?Zo*4_l10Hn)6C({==MJ@vn z0WvUbX8qGyG7Ku+Aii|{2t z3RfgivvHT@doKgiCBVwI5bUrIaAdK_>%PSf_#8m1GFZw!%l8(nWcY+c<0Y>a%Rr@+g34!Ef9KyuZ#&{M9@-YVb?h z3$`(~{rkT1eIL|yyz(S~-RMUS{)`sbnJ6;z$KoXD!KcuV{7fQY3*ct-qmn_Guc$jf zrcTc20xSV!82ycL(w9lF6jU7`V`pX)iRv;C392jItOi!!i6oF~0eIOzQd&8QB$7+0 zIlyfPAP0DLE#v1%*FZ%~0Nl}U)oGj&c{}6p`yh%7TnXS*_is=y{3iIXooOMoh}A^5 zN#O0Bwn&}Yh~NSu0=QLu^xDCWXf8k&fRz154gT)BBD8;nYaPHd_)@Es*>QRoKu-gv z0+`)jU%kJ9)4@{0i)Q&FVJd-;G&ZkMI*F zJA!(+HS~y`9AW=}xd5^fQJ|#+6F}5XMg~p*2tls3inRef4ZsQDgtkEXV{?i{-r~LG zNdTN>4yBWjIufNFT8KHosiP1&l49+rr-e8mrvaA~@H)b!9by#5OOTM7LCxUkvIx~x)*?pcTGTBjHvUGhFc2I zoD8D708$!YZwW)%?j*JSN}~*nj)((DR>@wzGk$-3-~L8C(g2DJq<${ow6u%-1hW({ z+rnRMob&tJ-OL$rF9dnsAhH5E4Zt`+Hlhd>N&wV{Lss9hdKiNeTxuJRfY&t87z2(RT+cKBApq%JIa0xC4NJHHm($XW zv$ObwmkYQG;PlA22b6ZK<`=UHBf>T9*a|@NR&dnd&lm%bcx@{nDuKH-{SGOsl-fr) zv+wqvx(czb47v-zn_hrYojwNLtH?uvC4h0VcFXNVPYX?Zesu#Y!aD$0Bv!fk)(x!F zz)}EO6F}`fh7_#Ny>kO^;2l7EGUzEhst7Dyz*GPTqu(1eCzuLg7pT_>peH-wSq8#8 z=sji*@G|->YXTn3+HREw;O78YK@sdP0ifEyg$q~*WgO)mF7N}=(|~f;U%_zz0000< KMNUMnLSTZJy1kwN literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_18.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_18.png new file mode 100644 index 0000000000000000000000000000000000000000..e4526da940cdca01fc44819f8672c6478b5a0e8e GIT binary patch literal 1049 zcmV+!1m^pRP)I=Dh!3;sPF&E>5n5SO1b!omuWw#(o7~hJHd;E zF{HJW@)afD_x~kot-pTm8IRXmwO^@XyvNynua3P&ElL4o3x0ze@cubp;)mzOQG;K~ zUa*bv-oNiF-}ga1hZdCpcD)}t_%m8yXQFtS|89HAIfnn+xyfk;qYi8C8mos&r*Yym{sKhm&r5=kbP zaB~1|10V-@^(^CYq-UUFCID&gztw1x9?8x4`!>kp0xSWX>i!MtgL8tfZOj`&%UDee zO9HtoZJ8Rik--H_1i)2&_S(UYY%V|+fR+7c4gT)AGTi@)&^kb5@U3Pkv+MLOfIbXd z6~JtLef2vBNdsF6FPr6$q^SgEwqRrzPzyj(Hl2Nj(v#2w-IK)`NQ*(%^Z(aC?%^k{ z>C6xdo8u_$yI6H6xK_Y zziI9biI7q-6-h%%O14@u4+E)(0Lz}XTc3EDPys~O1T=}=Nf@j9MstA>0eD(8LZfEl zC;;kS04dQm0dFy)Cxc-_fM+s@?gB`|fOtz7(sn1S?N^+oU-X_$#l!(DUfZ%x24{N? zXUn>!#0&!{F7Whw0jH^*)w=~127Ko z8Yn_N0VM$H%OR_CZoQ0QQ7P=ti+qm|LI5aJ%D-_r7(xJ+tX#md*S-Xt0JOtEV+=TQ za3hBS2mwfTmZXn?&tc$a0 T!W7^d00000NkvXXu0mjf_+{3o literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_19.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_19.png new file mode 100644 index 0000000000000000000000000000000000000000..b6e3de1acd2fcd3eb3215288e06d304d9130b170 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!3HERU8}DLQk(@Ik;M!Q+`=Ht$S`Y;1W<6R zr;B4q#hka73>g^|cn)l6tUs@i>@Lh|Tb1^VH?rKiA(pMf*kKOiBOZbD1`FmRk_zV- d3I|%KHZ+B?e)o$HIY19Ec)I$ztaD0e0sxHPOo{*i literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_2.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_2.png new file mode 100644 index 0000000000000000000000000000000000000000..a76a000224fe9a1482500050448c90aced435ca6 GIT binary patch literal 1483 zcmV;+1vL7JP)TTL$3 z%}SV^q&`9b?oJ=WV!UZ&?(^Ld0_u|aoJB3vDeAIC+#T>I*7( zT*T|8_fd_l1h9HO^xpY*m>=Mm1?6RL>pVn*Yg0y(vXnJ{$JZ}R^+Usj)JM+{R zBe~v!1s0;5J`Sic5E4O`=KxxghU_}j8&|%- zu^OK$g)N{G@Z-0E-nx2vz7-XXBDl8JdiJgnIhNqOITx0t*7;+MdqjX}Ikv*vs~a&{ zc+PtYM+rc+RdFuBjc?6=u2&gNBEe3AaQ%VS?8nKLDgc&i5SoE08PsRoOTUVjT2R?j zfbuk1M>>I$_fT{2Gm9*bUYT}qcI}Gy&-GI*Iv!5~IZV%{ygnPXpr@^H081Im&5Nw1 z$&_^2+6Zbb=WUc6$nuJ=UPLb~npMfv5V7TDBLGI$Xp+b8?Brp1FQKY&8v47oBZ zKS>@6o;!usiU5N-lc#>Qtb&ic88E(6NARG7D{ zc?A!S>Okd2+W5D3)*Aoc#Bs*;{HZBm1tu3&GJ3mWmFI8tA}G4PWzQD*eT*@(Mz&_* zP|55@wP(^8%}X)_jm-0`@7c3OZvXzg#`v0ISV_-Dx#PvU7KBEI(@}uaL6RRObvOb< z7V{|0H8?WH;pSJ7AW8tdSC8?vqhNw1KyyR@N!aOn)&g%%kTGU-4p-H9^BeOREdoGj zoBIS3z%kmmN}cdgM?I515g?+NCnDUL1d;I-h!Vh~>({E1N6Si2!@KV>eBVOOBIEt7 z+4tva1^51bk3N65DseRuwC308YLN`XyC3tv4AR2Ar!#e>ml&;FqMbhrcAWhaUlzt# z!u?$%!PIR=H5l2nJzZ%Zt;OE!OKam=8KotKD_>Ye|ADK7v^`x7UY)+?B+5XM#KUUj zDl$Ow&Y$_Ybq%;joZDQ{(<||o1j5a(JmV&^FP4j35h7p`;K=A9)ColG^{WcdLb(Rc zV_@}A2`x8)M6duK1vtX2CIWQ89bn7Ai~^Vhkrr|#yk#pz0*VL~fC*=Gjf^h8%0TI2 z!Yy1$E+Hc8MD?@iWqFqA4pOqD7CZNRBHYk}1jyVx{SvGY=_CQP`I=!RV`%zV?oKSI zI!b>CkZJT-=G=({7SEQ-MhTLabMiG^GFbAjgLhOARiKhyc;1ib zAw(Q#G^!_(R5Wlrx_M+o&v$RhnOUm>uIpe+&)@sJESb3w2{LQ$hU+#^DordKv6ME^ llBS|G!!mX>!cHHR{{X@xiC$hD0zLo$002ovPDHLkV1lo8xLg1L literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_20.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_20.png new file mode 100644 index 0000000000000000000000000000000000000000..b33656867fe4b0e67a4fcfc22e30b63d8a3a28bc GIT binary patch literal 922 zcmV;L17-Y)P)5^IfYJUO5xjWZVz>6w^ z2%stZU(UxkQ;G;)2!KigwFh_dN?EEV;bqDJ5uh~|)^M0oF|^Kr?TyC#H;YSpU`@r|LOp(fwdz-+GwNt z#x3}73aC4GsWEHw7K2+Jr`ndV9a2a{=dHPb7y)iHZVRg_pr!z~Mw~T>5}6@ulzzEV?xo(4c4#2UYvnGF7h0)&bCJEI_V8vIqk8yrGO}uL3H&hj1XE!h8x)EV4 zMA;X#`=>&HQNiY9xz|Sh!yeRgfEKGSjE#Oba=bU$*Ca0ba3~g#bv-0MSULUFsM7BY zv`A})04a@0uv!MrK1z8Cjpe5X2}B97oAhDw11g8L$KN6!Co`1;qy&?H$h{Q(uyO{H z6Ji9|8JzU#;}s&;6U-?<#V%nuqNjDdwQ~|w5FxZXqq8}Q`qwAum zz|{)i0o;MaLI4lZUlApev3LsL0Gt6uP5{o%az!h&-H1x@6u=3%0*m|~M@C096?h8Z w2pj=Mb}}e(^v^yt5n;?v0g|teMo{07*qoM6N<$g1GIJG5`Po literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_21.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_21.png new file mode 100644 index 0000000000000000000000000000000000000000..6048810f0b3111dfceb9317717ed0d743e250c44 GIT binary patch literal 895 zcmV-_1AzRAP)1;8G3Bv1+f4a|b16999t5kV&aRImz@N&u`uMh2At(7`BB8UZi{mjN^a zU;&#@DFnb4R3=aefDKH7g%VM-|PZZ4&a$Z5h6fz0V)A} zSPvQjL}bq!T_9Be?Sag9D3gN}0knj-#CwRKCA{5AUd`=IE)aSW*njvLY)|4xCyYo0 zWFyQ0WeM$K;{~93zxC^|L7>x*?lK&PL z2w4L>&BDG+NZ)}FV046+fE8JF*7=AG-Q5MkN z)XC1E70A4dN-qKZs{JY$BQa^lwK&D@H-#Y`L6*aqD>u+A1XuL`?c6n_Dgk{Eq-|W5 z51W+*E%|J%W6=;hz{m>k?E3C`YwPzD_psn3KcbsB+65k$0I4wgrq0ptcaUVE=iNd? zH*vH89xnp>BV0>|uXen9I>F3>wZB`@3(Wf_GXm}ckk)JM zvv=coT>|YUg7n+KKA0MkXTN*i0FD;2%;yB@0w`rnp!Cq@N!@}%?dZbp%sS&gLv*d_a0SbG{6vcHti0Ys39AnNR4HI|(NWMn_$z8h&S5_}Gz#BTpt{0p9- Vkn2m%OVR)U002ovPDHLkV1mx{d7l6P literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_22.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..657788f2b0ca93da8d4d7e875b6899cfa0ef0267 GIT binary patch literal 831 zcmV-F1Hk-=P)i_>TeTqaQ*kFw98QAkusp`pvSsn`xDdH_x`BSQ25MClapr~x>|gh){X5Umpcqy^xF z#zzn>04QPykrDv*Mn@7Q0BB+ck`4go#zqt!0H|UGlL`RVMn)DD0O(=_lm-CC#$^x< z04!nyl>z{^MrG1(^aXfnw25OUWTh0EO(XCCXpE^%7#-*N+uPD6C-5y{D~4#y+zA3h z3P2~os(~8wo_~8wB2WwhXQeg2Rw+f@movZv3xKrocAEdb4u&XS6)XTq(m;To0+0Zp zRR0Zv1OQQ|rT`oO-|F`j8~~>Z6;dbwb~e&XTuUSU#3uqM0Ho^0zr>`Rvzw`iDF6e& zuBlgvne{n6K|m7pRIpSl5P4T6S~mib0Ep3eiy#2ds8vn7#sF58iUfRk7htjMDmms8 z$t|;yjIt7a0=!LX4eUk(yq3|{=A*zBngBbOZfiY{htedtQS=G$RCfK7#Y}|j3;VWCetp=M>Dc}l#NA-=&`6j}D9g_fc1gjLZe8J%=!luXw>;MPEvwWMQgZ+h|2Ge$Em_f` zO@Qdvx2^kXulq6uz`F0B{rzsCZS2&DHUSWHLJF7xU|IcM@pwv=e-c#!%q;d;o-0Du z*I)tw;>f?&%ooFE?}2?hK$8HHMIPyf(B5%SWLZ$#0q|P!tIyXnT)N#`@@QFCK646C ztOZDS`Si??EEA*23bL&LL=2i*U4!&nf08eXo+kJ)z$fZQFMtR#iH=$TnH&)XwE!Z?B0gFHWN|<=v;sgN zg9s@FkiqaM&?J)pf1ioS6EXG{E3#0&~4z+aB7ZEV^9&iF+GF}GY1R%wuazCLLfD!eTgsZ~Ok~D(wfi|a9Xq>0#O6(GK;EO>v9e7)RqRQVh>9JTB~<62d%W<+PP~B)p1(T*8rmk zW)2&jJD%4s1;4eGi4Xx;EZ=I~6LoFD3ile|gs5VIx(H+hEdX2v;PK;EM*v^1V<%M) z(6)!I=GxusGTs}XSReV46wqJu)Fy(%v`L!+dWT8Y`y)UQm1z$Uontj;2(yy52WZR+!P6bz zFcScpfzh6)5uFCo_W+F(mwF|b)Bs3^ecS92U4)eqYcF*HAkCynor@rXC{nEjFcZ}a zs0zUHN32Gv#re?%n+3lf;BeFg5YdHs6mgz~VuTH;hk@4b^f}>OK`K3f_bS@mv9B|I z^%P_m5#J_#VI@SCxd3Rax&h3EaL zi$E!}{ejVrMqpb2sK#r~mPB@c93}c2Ua5wn1{hhH<9~AO%2x19dB0~%04Py1L;8o& h@q6hXOTF;0^MA_K)*cHPfCK;l002ovPDHLkV1m&DVDA6` literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_24.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_24.png new file mode 100644 index 0000000000000000000000000000000000000000..d8497ee6aff403f17c973e5939ccdbba2393408e GIT binary patch literal 859 zcmV-h1El(NY>tkag$Yio} zN~t7LO0lha&e`;S7X0(Bz%JI}M}^h^9z--9r2!%e;RU4uJc(vLIs-%#z#A$9{KS`s z$^a-tG9rxuA~iY^6bAT-uLp$z5Q$<+cmqUfXjI@0fJ_8)LK`4L1ET|LfS>p_{CQVE zgRgB{NpqD_sJDqB4X`WFBg9wvTCm$A4V(dX1eq1ScaPPw(Z~txM9_*5lH%o@kFLX- z2A~FktKyeZ#Ck4!F5d;p0JCTbbz%b66ar&_*#^8lXG;TN03 zgiHb6Fhvm;128pEbj%I#6CVq@6j0~aw_GbR3pmZ=EJin-1xPo_XuiI3ubbxmEp!p! z;pyc)p-19B<=QCGmXRVMf zq}i|-G zEd*JC6kU!DpxuZP4K+GCmeSo3>?j39X*iVL=mGRBpkAFj{cg|>nxPt{^K2Wy>H(~J z=jc9>BLr*&xM6J`;rX@_6X^@$jd&$8?e?A~7Z+}Q}~89?((;~pvJb2A7{ l{Q7r2MxwWWm#!Bh{{aIa1tZNY2Ydhk002ovPDHLkV1nhFYBm4> literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_25.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_25.png new file mode 100644 index 0000000000000000000000000000000000000000..d647aae1bf1259e85c91d9a4bade68db2cf180c7 GIT binary patch literal 854 zcmV-c1F8IpP)i+*P-+fpiA(m!v8JnD^Eh!{3wrNSv z^L!u2e7}0lPbsP0ON$@t2ArTNepFl=K%0n$b8CQzVp!nP0I&FbxHNzz(ah)00MP`n z#+3nH@%eFM03`8|+H#5;1H8!K!T^yX4h?t%po)*$E-LT_c##F#08ye08CU~&5Z^-^ zXTTcZMF&U&M2IXeqz7z6$cgx~4I5I%^1M;xW8Hv~U4gOyv-3-#-YoM6lmV!$3ZE&D zqn;ZTm=Zt{f>XSd65Dk=0?`Sy55#}^e9bpuev-%0-~QG8>)Z{ea3uu4jaBy8*0 z+sc6IRIt`!jOE0VH`ELu`GR+@eXHO#p-KP>p3J3U0Pe+>UXAAH1Kid*6fXf1(J<<$ zQSNu^>|cq?63|=19f_Z_L!Bk+b2wKSJs_F^Bwj)-{?2BQ`dVJZl0Ym2NWWB$jT~hO zxV03B7(lA-OOQ?Asm+|Y>;v?mjsji;3?S|LUc{F5oN9!dM+2;#K&_NF8v*2>d(*t5 zsblVPfNn0=+rnqmMyUUuK^90FHUmfv`F78t4i!Cg*d$=-18CHJ>and!pB7RhfL5Cj z30VT_)ITx-vN#$XX8`0EIGg1|Zo3vopN477_G#RwbzhV|jWpXOz+3d3>>7#;>`Wk6 zwDdZHh|y@lmI0zfkG2UA-y7Kg8V_Op$u|1z9Vg*Sz{!1XFagN`o}wQWv}ap3fK|hL zJ9)J39X$)cmVgnO!bCU0k^!`$pOXGpgh5NU8Kibi4S>{GF6t@b-j3mNCNgRk7#cuR z{4)AiJ_323y)4jMCeEgaEOv{KtARBwQR{Vv*0%!n-#AAoC`RjM057S3@3^xs6`QSTIQEmk7MS6Mc$`bMD`@={;lz>@kT}i!lr*8EBopwnGk5C5w82~l< gsY66AtOb(h7u{G0ERjgwy8r+H07*qoM6N<$f?z&&{Qv*} literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_26.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_26.png new file mode 100644 index 0000000000000000000000000000000000000000..83bc94316b1b2d3c125a8f82485caceb46a6bf99 GIT binary patch literal 821 zcmV-51Iqk~P)W$4UGXd zI#3wkiIs-}yaBfOx18i)8 zG5}NcGsRj1#sE)AC>sz4*y8&EVStSv?gn74e&*OSt_FBQ0@-mhz!o0^_bGrKb|Zgz z+*C4Yt=qNPh0?;@08&C^;ajiQd;BWIVEDxliR8};sbvJ#FOE?4021P*ltN00RxoMI z3@}@9(v`bS@Pml~(8S-oQ8$2;_!>x#0<7kw=>b?1jE<@SkgGqldM}`60LhAFseYC? zkEj?x@_FBal}7>i9)Pi`m=HGrTlGuBW>`@JNPqa=>)jcL*;rbL8Nl25t>SJIeB$yt zxJHjaqhemIEapbg+sM|47{DuO=kEP34y!nso+_pdU@87pg|1ey(oFRLn(x$0*N!n> zR#!{KosTJ9YbD@%fEvf4^(+6b|_ z=tsH@pp`u7;qop{i%ljB(*XRtfD$te+}>gIrpo{p32VJaJ`TOY&}lY+cePvzJ!697 z=UH(DFwoPxi)8h_#;x@N z=i^ID#@6#cvgwKjKo0tjy)mR6h#nv_`<=fKwz$1q8o;Xhdxl@>4I9=*K^;;582~lV z5Zc}mAjgl$hXGbh9>aRQCT=}I9?p-+KmTnh%&I2%GXPrkAAiW9SDca0Vlfh+~6ib}b00000NkvXXu0mjfnK*Ln literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_27.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..9f4e0ce7b950775716434b1cfafd678a601b7230 GIT binary patch literal 791 zcmV+y1L*vTP)(RCt{2oZD{0AP_|DsQ>@V?n6c65WpDrGK2R>l_JHLH8ZB1vfe(hRXCSd;6koq7H zLHu+bW}&oDH2{kEK^?f6K+P7vo}*qqx{R@>3>k*bi@gy?{%=53gEwXsAeH}f z!la`ww%-T*1SET#=$ReY=M+QF;=A`P--Od24?KZiL3r1)nsWq=g_JZ21#;_G0- z03OBn*ya%?43OxdZh$B=$t$V`Nb&ViGeC-O0~G_L_;xsMfJov;=*#=$!&U;G%5X#xUAlR(Z{+ulp_ zZ5{9C6E(n2$i|3SYyDmy?Uk)lh#5e7W_X`@RuD13?z)ei#jE5pfx(^xawT^VMuO*7 z5eKUQR(pNO?5Bu7dv=;&4cihx8R%Z9yHMVm>iU_r>BO?&*<>scfHUl`1jB42GarCK zHUPE054|25&POK6rC<)p0MJ3@sPjX25Ggc{qn`%Opk(eTJdJGwa1A(B{1l$XmH{Mc z`8!*{-DGf7{h7orXajJG-`)pm0&KXSC4|s+Jpq&fv<7EqoyUsbUh^aqfDOPo zD5G#WyFaBuxZCC@nE+$}iNS!b-_*|rk~O!bU=K5)Jr}@vPb?nBs%L2ciLD+6NK%No zw?~1bnJxjA2Ma1Xb_r?E8TQYyJRTHGR05&g2@g3SGTX1;ZA+sjhC>5b?I>5f?us@e zv3Ckq9{9-tB6V;j^ekGeMD|+kTk+2T+~Qj|Q-7EQ`g+>xI_rJz`5M4m!#Qb<@U(Gw zl@hztoMBs{-k{Cvl=$8U&U^c3sDDS$DJbiEHt)*=L%ce93fIPctix~#hcA? z$d>UkQIws)qar{lzvbTk_oKhHqW5On@E!%&cLA8$--c4HYTNA8Pvq)f%O6cuDNq=tVT=F(002ovPDHLkV1gaih$8?1 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_29.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_29.png new file mode 100644 index 0000000000000000000000000000000000000000..63babe7937426267361e42c5248cbb1ae22bf217 GIT binary patch literal 954 zcmV;r14aCaP)=u9b`?+ytfYUnP0&WZt;>+O1 z0D%lH3=pGV4)X>G@ntb>0IT9#Z5IWz1~{pcEo0IEA-*2w3=rb$V#)v^zCLCQ5aRE_ zgaJH?@3GA-KD%CgN(XvJ)c~dFqr!?XhaM^h$by!lk3`Pbg%RWtH2_xl(S4Ag2YAvW zR1^5VFN;vErJ{aLN-63CiHI1W7Jo$?%(4+Cz=IA0XPt}{f7F4a=cJUre*!va17JE_ zYDCs;$fG~ZfQK~^gcSjLEi`-#@Q7ssp`*WdU4()MV(lghUuOBOV3hQO%tt zERTjEvkmt7w?H;PZ!~jstkyHa1VjUr!q3+H{O>CpwQM%cCE^xH1}HlXA}mI{dXre+ zS1%`V3b+P&23Q@{EimC>-G&tta2UwzrV?Edq*2U?ttKi>Ks`F<5YXdHgqi`gTwYH7 zkgA_jd`yi&ZI&A=jKBuiF~yaeKZ*%P8`Jvln0K87$N(1Bw{p$L-Z!GPkGaVNYy~0Q z^iCZqM(AZGA>k_q3j+nqFcpmbg*c;w(Ns?PK%~CsmJvRq8wF4WJv}i2>v{eKi7_cY#K)!##WBtsXuHTRv0*>-CYp@sqGy17Vx+~1yb5rWKMsx-kb-Z@<%_t|3 z+jAq!Vc=@GM9H+RFw6ekhRUa5Go)q$O#Hv&-4-}95j2A$4)t8XI!gwCPXExahon09 cB*D1(1%!yrKt#6=eEkePcGe4SR{!C5>6_~ZC^0^B`Bug1p{pc**$ zSUs;&CN=&cz#qr$3GjsY9Bxm56`}Pn;r0Z0B0vteC%_W{a=1MKo(Pb`?FsNifE;d5 zfF}ZcBkBmZC&0-F@CoF*1Oa;QKR@%c6(^1G3CGXt^QV#rX=MAk`cc<>#2|yVizYs!=lcELn*-A1^9;G83tLYi>dg*>tV=Doy zMwwnc#`yU+%n$I*vc4T8!Amr}Hf1!AF5DceBSf){01=O*4C^^pt!r728Z480MJ-|^ z&FkShl_c020XznQkUSoK#>TOL%ONb|xd@PjxGs`ZaY04bzdHdx_>()tz>RD@*f zTKIUNjy;bqeJl3Z;2q25P}JPl&Q99t7bEepVY%s+ z?ECa_K#hTr2)aB6(8?oR@8dI`lW7@8AsR8Xc~QA|PIZ|#b(^yBcgJ&p6WG}r_P`UB zB7Fxc0YCmU&|6ne&-bDtL~9Wz8j)J_>xYdA%Tnw7F~&V2K(-vKJfEu@FwETWk=wt&uQ9%+7*^7=QSNxLt_7iy;dB(>bdcmnNga*=k;Obpa}AD+ak%+a zB#06K@6}^`?I@UF3D6u7KoWMkp0&W66J(4Tox@c%-u%WqMvDLt+U7oi1aOQtu2Ls_ z)KS-@?>M=wDSK^11eowF>^aL$qw)q;K?>y>Atafq)6NJmVexXU1gko?SHWZ0og|>v zUTOT+Km>>==7|V*CP8F;1)>D-==!y)NcYqjBMJT zuC$NVV)yl>wQ;SC(vrfJH>{%nz*R!po~{P3PG55pWuQpnVKs6U86bJ*&-~uH2HYdg zZLa9)m3T`6;bvE!aTD1a%SEmT5ikjGWb_c~1fur(RRw6FTm$DZuzIM3mYYB#Sb&cL z9AQ=y0XpCguw`II0Zf8O3%L^BvXvqMMT82#gfqHEMwee@p!8tEEnG=1AtLKU^|R<@ zxt8e;QnI8LJ9m8|+|Yvr$UJ%aC0HTSNdjo|HN#5A(Dbo9JF%eZDE%QorqN@Wb0-p5 zJXML>y=|swa|EG;ln+ zd1OS-cW=p=S*rrB$HA7KzxR4sGIJpkWY*jb*V90$G_h>NQrbjInu^j4%h=HfJAG9C Y1MCQTcMCGG{{R3007*qoM6N<$g4LV0E&u=k literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_30.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_30.png new file mode 100644 index 0000000000000000000000000000000000000000..d062254d931920d1453c1aeb07d47fee8aa6b130 GIT binary patch literal 871 zcmV-t1DO1YP)j0{WOu1l@41?3VCIL&XRT%>!_+{t@}DeOHeVcBn`ueSlijTf>#1 zy8pGn2w@zPTL4u)XBY|bI*0=xx%%GZbHvx7LVrMpN(IoE=ckS!AX*nu0JsuHdhQTE z7j^)w)gQU#d(MJ4xn zuKaSF9mHwz8UPFV(iuYOxgiAqdAtR9mJf932Efx5QmzZo{7ldZz{pUK17-4u^Vyz4 z8-=evfQLq;O6T4}>eb44v|I&XbtAlqrz{ZZJ)*0 z<5=GLq~t@sgr~;uRf{Ld(Pc{bjL7#MoO(v_G`SSqt_s$q48mJzPpbmq18|x!ZPWq~ zUBZmXrU-& zoG>bIT>)CX^&TVTyB-bHKTorB2t)y}xL=m5gSis8!@-ok19VD%K(zw&CRsGzA(VuG zxEO;)fISM}T>ZTc*3ifTP&X0;7mU6MpfKBixRjs-SiI~9{ z@Wv3r6At2ebY3^csPf#^d}+S`2QBg6%rORV@z-#K0bKlbm^XlnzZTO5uq3{vcR^y- z0B`MZYnU{Ei@y(Z25|BBV#)w6{(j6Dz{S_WgaKT9J=6{0;_ISn02f~$H3OU|{)z8A zLd5`Y?8sC3-t{0C-6l&7kc-|csBm-WvBUsL&|LJM$mu#a!hWCzD21mAS~Cpg|IR!y zfNBDt=OK^y?h93>in6U3qt^z4fJA^?e55d~w$k%F63jEb4CIR68$rDY5@Y=Rad4<_ z1yd+RPorPM3~!4%QwBhe{<2-}J&ug+5ujxhUkd6VHUKvSOOiGp{p*25fRV7J_$^^M zJBPjsz_4Ws7zyiQ>LD_KGy|vJ`D1{cnQ}hj zDa9UnG(eT0-Oh&rv^r5@1a2fKrx&j<`r)@IbY_!hRkB7sWz4UY6qQQ%2;N|xuMlj8 z&wS43-q)ih_RWdqmlg3_BUfUC+K5lTN0r96F;x~`X6A^t(%f;xD?_u zsT?{F;AZwz^P#Ux*Pf2KvhZ4_UIP_*ZfD!q!#QxmH^KxrS0tN@06EC>fLfB5N0?ia zK+^_Dw2aVO`?uPuZ2+ZqZj1oaNni-NA#}FfthOO^QzCdo1n5CY8hZ(2ZI9rl>0mku zT-i~ab$|YO@4nhKH?Jm4t^z`+Pb)2Y!0wJ(5t>_5$Su==$s9nM zRL*=`sVqZ76rP-b*Z^AdCQ_8q*~1Mij{w}0Jn87?&flZwNQzEQL}UQY=;s{iqDS`V z^P5I`3upxBb$nUf+q)MbOm9yB^$cV?TCkUav&*GXNJV!jr9|vz;5h)r6uo(O2G085 z`9Wst z(kp~;hlRNBIo-XkYf8Jl`J3GV3A4oCnPm*%;`d<*1GxD8fH#1P-xp{DAc>FE7fWCb zuSY=Gr)@Cuh{1r zQw*@V}q#y?)z2?Nkt4d zLVwH*kc!_bTpC5(1bs270xZQ3;X4BCdKwpnW&=pXmxy^?*XMd96YLg|raudy*lm!J zX)-{s*-zaMDGKbwkd2^-WM)%>d#PxT+5pno-y%NsS+5Xm=$->Sg{p<})Ku}0!Yc$D zx-S7-@g-%X3vY+Y0GioX6JH|y(^S3`=xL4s7W@3%3c`i9N1OvlMQF|3;|xa_>LVv20)t~>A3*Qdpd8v4A5HYN#Jb$NbZeh+*SA( z0A&EV!fVbKJd?(j=z=m{t-M0O_%byiI0r;>&nVK*#%ug}!kZV$7q{w1?LieKHf4%a zfIJh(p(J!JT9iaS^?ZTa0O+%YTJxP>mW=>hQX|w%dtww=+5jgCW{t^_FslTrmcjSp z>LF|Re)8tJo;UO*k+HYv${c4W#Ecm z*y2d}j+iEaJ@*DUd;K-gUNS|19RXHe4_)*;=3Bv*yK0)pw3cn3uqFHtPPb5U!iRDy P00000NkvXXu0mjf1TB{w literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_33.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_33.png new file mode 100644 index 0000000000000000000000000000000000000000..e80a66743e0d2635b95b069b9ec45d9b470c6e60 GIT binary patch literal 933 zcmV;W16urvP)ifSk=SQ_Ehj6it!4AwPMXlQGr8|Q$ z#%T;8r0^xAWOJ)AMw9FA;AMRSRIJ25ov$ze#9xPd13>(>xHbU9UyoY@coN^!y=ddo z04G0iZ@4o6#NUf613>)!xG@04x50%0Aif8#7riI)^FA2CmT3c&!czsU8HVz6pi2xBEnz7o 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_37.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_37.png new file mode 100644 index 0000000000000000000000000000000000000000..9f20081e53b1ec688d552d2faacb1d31f6a7ab31 GIT binary patch literal 769 zcmV+c1OEJpP)ifTP_E#jzau~1&lguZrv`5=iJHy!F zXbd4-;UKOH|8`@H@N1&@?0y3y>=6Iye0RSA2^8KA_X$ApZE>3b6dr>+15kJ@u1WyK z$KxUaDEvOQCjiCY%eDld@IHVWfWrF%S^`cK|H;$9A%GI#MB(3pc>+*;-7JzvTT5wfVc2Hfm4If40xsuuqu4%bw-~{))_W{mhi3D zjxo{%AcmSiOY!sTJbebv3nBwt*98+ly(UdyPuBpF4xe#-E>#1xMm-9qfIa`3K&j{) z{m9%{J(gv_4zv9LxnMJC!O~=p zs|_I(9MAT10iJn7*;z6``VXr8vov~uHwdu{wGz;3)JCG64IdV@5>Q*h?X4xGU$y73 zH?TuB0ZQLXpL25?iK1grNq~FwYHZ|4O8`rOJxd0#8l|@hm;&~=n+YUXObw9M^HC%4 zZUBSYxqwn+mm0&`G;eF_nB~9@le+-P<9cfa9Q_!E4KO22Y1B&$!%=U~bPq5(yB9(z zPWqH!uTfR*de1iFX}@Wu}OEk>K_0ctyW5U>>JqSgc=g`o-b z=o(-o{w2b^t(17j&23;lMO@o4oUSY~1=eXAARYaZLm(l{r9f?&_%;Q_wqxsBKrU*& zZ=iHmAphMP?@%-`DFKoZnFHx7OT?}7hh&aw3Ft+u zBzj9916%vMlv-ax=IHhSy=mE%V_>sTcs>3AHaV+}r9W*_00000NkvXXu0mjfFFj9G literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_38.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_38.png new file mode 100644 index 0000000000000000000000000000000000000000..39e62095499cb68d06e5ea366358812372128e79 GIT binary patch literal 909 zcmV;819JR{P)AyT1^X3rK(C^#Ztwnh35I z!0hf%DY@;~=Q>ta1Xl}SmS-K$jrLYq*MkfG_`3k2@g&MC{@08>v!>ZGEc4(x*a54d zc1S$ST?9fv18+B8LWS1&(P1R#3<31;FoGpnT_Jf3(U#2w-UL2Q1}b)Z^fiv7i60nG*u&zy+~WLkFL$tjn@ign$at z?iw|IRAQ+K6}{Zx6HTE+ac|tPzT!pm9-*iv($+YVcp-oW{7Y+zKxP)4!bMAvReWQ@Iq!Jpa2a{ zwf+iXP-rbb1z2R)h&?(V3ZSsI{<;)ff8_HB5E00d1_f{+UvL$n_tLc}GemX;TnSX5 z`+x5S5NZRdClMon09E;|u3_gyxVuz{@>K~afFknXhZl62N+}dTQeUuTE&g@P={`<+zB6Pl-1vF??dgV@C142Yb07dVGMxF_r1z8S8fD2IT?@{u2qW~j7 z13eg=MUE5d4|>pu5{eO^!Ah&TYVCLFJ~R|SFvmyZnxByY{tIu1|-N38_6rcmD;#bq~FeAY$1c*4uiKzILVh*k9 z^mP=dzgzNt{Hfpy^4y(g4M4Kv#PTR_OBAC34a}`)!v~>cUe@mq>HLTH6%qw#G8({L z&Am@e5mzLj00w$5_Vi${g+<)|#7Gpt#p#>G8jF;&%-jup8J6x!4iH~44Ma0Ds-Ky1 z-HG4Z&52#WFP4p!tB{J$+jAztk`5!lKdifI5v~2|T6tu2;TRyDJ>q>;!Kl+jydDk& zB}`=N<~M>~MpP8|ya3)8+q*zwd2VKDH;T^-aA!Q<6&j@}p#Tf)nDgsAqP|=78UapU z){H=J=15l8EadY!XRZUx(%NB*l&>cNDk8il0wD0-|63Ud&LD3ZA>j7`Qu`X%=z=IZ z-xFPzl<3m}$Wiv}A&)0+0JZqs7@*gWMt|e4#qW;XL76@k0rZVsN$dBW8#EDJK`+f$ pP6My7RN83THA=@LgC);M{sLLhS9M2*b{7Bu002ovPDHLkV1m_3lc4|r literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_4.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..7377c6080366bf2ce64c43910380a48f055a2aba GIT binary patch literal 1442 zcmV;T1zq}yP)IZLpYA(@^1?I>^Kh5hh*b=+XFi0%U{l zJx1GQ`fRm-2=L|Do&b-;$FMyCMo8;7VS55R3J}Bg1b7r6hV2RPC_oI`6W~#R7`7+C zqX0kfE`;p~uu=g&fqdH&U}X?KA^p5p51^F46KT0-Z3UtJcAn?ZE8Cvm)_Z06&BRgy zXm|K%;A)Vi-)AcX9ZTdpgQyP_O9&u+)WLxSQ1dZ*@>(CLWc0~Dkje7f!gK<(g0H=B zZvwaiMDwy_-|Cjg>L#UqGLhG#YLv^4sd*&1UO36>rM(ZF*8(U;F!304wjuEHL)loM5><%J+Ery&z; z350g?koreRkdc$ozG|D^|8?NDV%sv|KZa;QYRMdOO5p8~36c7;*m~P&uvR{;{b=JG zz6a2Plp&$00(jpOAfY_AMj>Xc;AYMp8S5nIcg*Q;WzPu#Rj+5D_`zM;ZmhS;Z zBeP048s7VlNZn!ib+A!N1GEu#c!Uj;9c zviKCh+m(XV*pc=KytbNM_DSS^6^@T*jr-LFSG+8zb&SB=i=9N&suHY0zoT<$khWtW`(1=6P=gEgM;R zU-Ba@h~qrZ6A7v%_*wv>g_l+zQoq`GT1Ua&*pcyBZ8+cx9`Me{QTq7#eNOP{AfqqZ zdnUEfCa5ApP>-FZf6F@~KGPHc89X1PHE@x>te8at3kf~71 zSyqlGm=T+v+F4W(T_~^uP-p|`wOa`^O?gzN+C@ntooX!m8&jL2C;$bT+c|b6fwgUP zOgz=Ko@u}J{EBV30#sNTm${)DjjT;*CNwGU5km3@wJYR%0FPtsSZ47%6P}>XA_DKz z8~jqRvA$|U^u?%BNQD<|Lvja58_b>!x(U7m(0IN^6+U($!7Mu~qN^W;=`P?nj?1dA z|CVrf5|olxg1r+=sqF+?_lgiB&(|XnEys>{ml?Ug;)O67k^5bhppLDo8dWxLb!{cA zV|vrzS<8>SW3BDT4OE80NJromyZ@QEqyVqgac|ZJh!#@~oSc4F~K;h3K^VIUCrJSC6y62D+pcgokb6_$nypxzQ`BuML zFpyl$+j&-SR(igq0RCc;bkHNx%Cag#aLb9t5=?YsIjeT^s{oNg57J)p5lsadDit~Y z*0vRqf=%`b3BkK|j1+pTn<%f3nb5{%sew=%OM7awVs3sv2!R?Kb0hkEwF*c+W&~#S wqA*9}s7ZJf!n@r~m)}07*qoM6N<$f)B%%ng9R* literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_40.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_40.png new file mode 100644 index 0000000000000000000000000000000000000000..2540fd3831d0814f8e1779a204c84e883c5b09ac GIT binary patch literal 918 zcmV;H18Mw;P)hD}6pcXCZfMX&YJ*TrGeQ&Jclx;ahF45CM`vCD~BcLX?DfP2UiJ z0*RN-V43VuKme0?8$ZJp!R%NdfC*L^KjSF^2tdecir%Gob}280uUlKK#b8$2xXgAKmbf;UV2#{$U^X5*46!wKqZh#ZMdBkNXv3DB7ieo zulI}$J)s~1bQl>$y3?m^tJSVHDaCeh1J29Mo;@-Er--v^I%$ms0;u3F_3Dhg_n%(& zzI!(lQ$T^H%IC2q?A2!}#Oot~XK7aAv89*q_UaMkRB(L+2*JkdYQvo}Z;#MKo){yR zfi1Ypwv|O44^m1HfR@{Z009`WO9b)rKmY}GrOe{A z@99E-04lNwW^U{BSh3kN)5rCNAP-ta9qCK1MRRHtKm_P86Ae5riFg7549EuMt!3INAF{fB>c`qrvXG0vbLcfYV-TM!Lmp z*)JE=0V=4McXAkS!HrCxhyWIh=5(p|?(+V63?m?bz&B3)+)RRZ+O_iM1>kjobG7&P zrY_tJGUUXBlNwWbzJYoMNuV)3@3%g}E`eIIJ{ujzu?h-&?gB+wY{%indKTzXd#0#HCDS0{Z-phY=uB!L1bL!P)YC}~YkSpyTd(&y6=ptUrj!n!2EvN%cX(Qd1~Cj}ra=}MMaT6$yAUSDth_c;MjFXDuc zH*ka~`GCb!n?c&sIrV%!ddaP+<3>IsfDyBwep!XJXH$LDJR<-JQt!W+Cj>BSu#u-( zMTyb)>iq_7UnNfnz>;0wKXb_xN5Uqd%Da^Dy#-<2KK23v|KrW2;dh?#>#aCu#`4r& sK)rr$9olGR|0_d1XKJA?q-csme>}omt|Y@$djJ3c07*qoM6N<$f>%Y6&;S4c literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_41.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_41.png new file mode 100644 index 0000000000000000000000000000000000000000..63106c87ae76b2c9bbb94e3e127fe11cef0ea97f GIT binary patch literal 883 zcmV-(1C0EMP)s{jAX^d(e?EQ>E=0%4BSmnH$U9*ox* zu9Q+vo09XkLsxTMS4g`*A*~WPQDUb6HX=d<6u@SnML+=@4h66wNH6dUYl*xuirhVh z%sE5e3@89FwNXX})_nI4gb1#*aULM0b#ej45SHnSE_+DF<_N*dc5a{5# zu9S0ziLh=fD)ZMu0T?O9kDv%aBLeGKC;$VI#;=(n0t!IMEaMlVML+>4m~H&FZz-BF zQUC#<12}7rKmr9IL^Jz~+b(~|F`%IUf~duU5QF9QP$F2Sf(2OD{!b^V${3zRLl2-} zR&u6?HA(a&L<%4fbPV93Cj#yT3_$1S+t|R;HfrN3CB^_^P}{X5QOt%*fB?_F`)ISs zV|>e)+Wxj*0#b^dLG43cA+k@&zYSe}PY`!U;58pZ$*pi91@@Wdn(K!;E6{YycNj{>-12jin)3=l-mEk_B@rNxlrwcow<#C^zrb*tip=7f(pTJXO^vL`#jzQXlKui6T4nY93$zU zso(%+ZXn$WviwgXRtmsC&%m>H4kedRjlOgA5`ZY}TK%-}XgN4q0K3vN=v@hVecQ<0 zZ@2^~Ss^*p8^fsl{LX7o01i->H6q~+A%Uzg`?a)L$X2ivz>6Jf|D zt#JtpLMlcxuKm2X+vYWX%j}@@$XmkhDt(_WUF_uu5LdiCjqwwCha&_~=ozs699_{X z$w7>~i>A&`GoE7v&~v}i@76@R>{*`Rm|MbnYx!zv-jR2T;tyFq5s(yE9|8aX002ov JPDHLkV1mPDf%*Ud literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_42.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_42.png new file mode 100644 index 0000000000000000000000000000000000000000..21038b47a6615a617a7ededd51fdceb3b387f924 GIT binary patch literal 1020 zcmVHt0ui-qM)N3t+z$*YD&-48Jvl>rxkJJlX0T8L5 z{?=I7zVA`Tw+c3|0GN!7X%+lw#loHQ3cwJJ@g!myc_hwQR{+M0jIp}>OC9S9AP9A} z&#U*};|idnj_fs2KGzFasdx_*PTWH+)iEjqIZ;QDQMP58M}}|0ITcglKUbBodCaU zKVAUsp6QKdEAd#Lw*PRv09vTIy;m$yyYA?RRsj;Gfs~X;TXE~14dA3-)@KB3KGhCh z@r3oF^4j>+C*Avih?L{%09a$C-k-6wkW0__oO`A7Vqer5qT}PQar2x25_$Wx^8qeZ%FK7JGJeN;AqXBoqng0)cF!aXANAFy{DtVk;Wfd55Mem281 zhv(p#LZ&+vJfoK$YFo>vG5cJpJwTxs6lOJc3l_Ad_W%X1OO4po<3h<;!U@3hFhvHV zQlX}+;RN8#?t;wWP)S0IGqAr{{IDdz?Ke0E;|*h?`0W@Z2EbAvwM0LIA%z={lo3+Mn2U>pR1alQgz zr8Q$3*h75oAp|&p6{zUm`)B*feF)q|DmvY6cK`(497-Ag>Hy#lYNBTyU}q-61?1=v z9}aLCz2ZL(fDAmJ5%E& zVz3Mxz&NTLpp>cHVc-CDa;t;4cLc#r4$>m-)IIEST;Q9f4j?T)T!0668bBlHV3yYaJFTCh;Tm=$^)6tF15it^*}Db%pAoRjR{_1* zPdkILStQ9xE-)j=r5xZ#{K^PD?P1W4l=#jy{!?gg_NSlCO$ie31}q{ezH@G#1C&IS zT=rR9LQ0CitrJLt1KHmPM|A4s&8vV}OFsxd1~=oEz6)4k&lUrTlkrPCfwei>;v6nT zT1@wd-s}LQ_Mfwznap$ntqwy)PPZbRK(1)HAUG}C>BG_ncNOq#L+0)z*65`sP#OeM z4|i5@oia_e7J?~X0g!%qlsBPk9YU!wL7l0Mpj-zq9s@e~R|B+IMh~gbA@u)%6v#sh zMvMLc*a@FhA#FA+VTQqM zx0X_#vfz1^Z`q>OYSQn8&d=!qT+XsIfYs`^?)yUHCcwv;8o*8rMDfGbEmG(``W0GByd;!kS;Z*J=Ju@t|X z09Ob;R&!=8ey9W}C8zn?qm@O!ZV8Z4fYac~)WS>ra0!r=tm6Nd08v)I=QyG~GsaW= z&nG~{-y#wbuOAoKue-2T+1ky6dcz; z1krojNhwSNtc34P#@9@sRmgd@WCKu(ixzDYNVXM^+Qg!&w`^Tb0TGgbQz1*>R_Uir zKr#S#;!ca*gHjP&d{*>CP)~r_1mg#Vza4g0v8OS23bUR2+-d+0tw4CKauOtD-v+$D3}Pn$+`Pl+-dbfp z5wpbGP$3ckSN}axs3us63fG}Xl$gs3&+&MRFRB4nV&QA;`&!|($+``Ai{DKEd=bCK zeGs50hS#w$8>tci4;a%EpK2()^+?HRu;U5PTmAUjY~byCRs!^llTkEHP9(rDzQNI< TaGPU|00000NkvXXu0mjfZcWF6 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_45.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_45.png new file mode 100644 index 0000000000000000000000000000000000000000..0501b735ac7c723940e09c8415bcdcf91b6e627e GIT binary patch literal 1061 zcmV+=1ls$FP)Jz~dfW;2IF2F}iW+`Rs ziT$qwB!L9!z2{T_cW3%Tn)LP=$^ov}{)Z;{QwO+&_(Gffa1M|a8XLue{89=OAZ`LZ zcmKEF3|f(Y9Uveq$tP<7@7(YG`5aJVJQ=$+`PTssK&2^sow|V=3CEiJt^;J)ebK>L zd~DZi4*=HzG6}IA>nSNxakfqY(H1@HX&?*vwbm;DY3!0Zi=!o;mjI%XkH{o~8L3n| zDG_{B4UiEW@}KRVg&|_c0k&&^sJkDL#RWLz_u5O@`A)y<0D|c4WKMg^308OXPQRb% zyFU|F9RR>z1SuRKi%3@;0AOs_v-c%LP{J1gf5=h?0Law>tGS>0{PYe0kjDii)4&e- zZM=3oWDRgU& zh~VT@fc6h)KfBc;UxlAf35&li9M}^x#Nir&duFU94Qg71aMl+wBJ7`b%Jan zkczV_f?gceBJicMzpq;B+m{7_l*lYk6H;y{aWq`M2ap=P*YS-KnYDX(i-3BHI>2*L zwF|;5cLIorA{YulV!)%*f;EBOg5g}!a1qV{-p-bcqoNcUD{;JwMv2n^6;H(hR>7H^ z$Ig$0(foS_8#kV-18_{wMa2&?H~2257;l12jG}0<>)(Ajr=G zxQh{m7z><`)b`hQ2=l)SzzIgl(^^<1(ZM5uuHNJ=gNblAWIY>OF zjI2&EBXP&>5vHdAO9EQrvtyLosrNbL0~r5KU^^i#w7Xsso|}iJ>71OYD+vMgGtMW=iDb=$7D7KGTG^fFwvqNRv<202~{7wx?cOafsd+J^{S~ fuzjw?kru%})x7q7P*DQF00000NkvXXu0mjfNch_t literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_46.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_46.png new file mode 100644 index 0000000000000000000000000000000000000000..1edca6aed37b513c9fbcc890ca92b5fbd54f6a2a GIT binary patch literal 949 zcmV;m14{gfP)uK%btd zr4&|+udkd0Sb!O^DP{m*7~rf-UnDD?V}g_k@OHCVir%&)1H4@jPdvphrQ}S2x8f~6 zIRPSgTk1rJPfCEd2@ye*_^A^B;2q|R6#p>+-XK6E(c-61fZd4}|26@#to|(HupnCe z-vroslCx4p0EkV30-aw3Wiv#bs9l*APkQ|_H9$5)YEYj^BA)d6ZVj;GWM@Mwz5(Rh z$45J`tYjV=2hBaaVsZ*N*q~kb;eJI4u*(Wti3}!KDgkygX^QROdmQUb>~2Rw?IR$! z-=z`&V2u>ON3>pxk%-^BzTSsi1FSHiS)j}nHLfMG-EvL=PK>snnUyEqyGLW2J_W2q zrqqfo;6|JTa6-F^$9uen2`K#>0|?L(Us}jf#yJ}AvIfYo4OoaTH4S&XpW2hsE0i*b zrA-FWDecpE%cZ1Fz1dxbYGH&?Wgbbi=&V+10FJoSJ!|IvQOvHqsFmM-E~)`)$5c_< zL97m?FpI1SdiVb~?%4#8tY+;bh%}YfUTmENqx*-P0(MFQ>5BA7g6_)yX{QXpUBSDE zJ%TGnosaMW&?5ov#K#j+|7OrGEN9T#qrF>~Eua+$3yllO)whP(c#cd2wgEWtkH0%A zNZP(;O^_i40Om!+*Gu|Il>j~Q@rvISje@QABk3=#6Lt@_I#Jjiz7GvC>T$U^U~H8a4h@SGxZbl>dTNVMoK2{3wa0RK0J z8Ob2NX#$KMxc7PGE#g-)YzuDh0zAcE(Ma1TK>M(m6$P^(A`wDrfWtFhW^mgAM&eto z{+&|A(hMvENRDXN_4mT(a8I41Ub}1pv=8WE1dV%GCzOZCDNX^aVpX?9pZ`M>;P6ef z_^bpNt%U5Aedd}YT6~oRsGVtETZ-%_Vyh4>J~;t!_1_bPYJw+G>;LhIVP_LWi?3P( zJc)(>*M6@RUYo4jK(zRz1i)X$pK%|w(i6i<_!WpYf$AxsmjXTUsfNN^kH*+C5}g2F XbrdPN^_tTy00000NkvXXu0mjf@LsB9 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_47.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_47.png new file mode 100644 index 0000000000000000000000000000000000000000..467fe93e0c9e66258eacea93bc7f839f18e85802 GIT binary patch literal 1084 zcmV-C1jGA@P)k1+Pc&yOt0-t1- zQbbo=r-$nR$)G_xM+|HM+?8nu3({v|07&itZxWj)`B(%j0lbM2PehPkuc;m24TvPa zltLFk)aw6c*NG))8 z4ofLNuGaww$RY?cCMx znFU6B?ruGe?Lb`bX!apK3}zDnx9;5nHX+>x0Brx_=QSB1+Q2A#^a#5FP9qx}FMw!p zYOI)QxC41iG^oaKV{-qWBF{3b+T^wdZ?~9+m+xyuzQyHn6+*w{<+P;(r(+ozzl_8~I+?vHyw)^4&531|
hBUMP7KqxZ| zj&=(XU8LZ;RNcFjQe4J;`Q%UZ1YTl!4e)SO2Jni1@3CKgcoQH2bu+BC61EL+VuwCC z1-O`{l&zMJ)8kD5AS^^5nW4p&0iL`wz3>oSk_hk#@C37+5WklQ%LaIYASa0spPK+a z0e&-);@38X0nk%tXMPCcmr~XYfG$J?l1h8jC%R3M07SUv;|nSQLG-NzKotOitm3Ci zfb1ZM-D(R7eh+}O`=z@LiTe=|E&7`P2SFBJR0AOG{)`}tpE3cm8d5735xJOPXn?GX zjY3^N3$ZGXPXMAk#zV)=c7MbiHP5u#`E?#| z0t8US*uiV(_dYjy3qS`Ysrb(1Ek~OGy>YbT|7QS{MMa^v9+Eh|_utvEEiB&-QYXL( zS|aSm_|A?U&C8?~AogWY*4?i;n^&IusS|3AJGy4|Tx({97>fAP z&p&DaOCd@r-vjr?t=GK7@}O0S?G$pJ$H8umVO9gogufGE0*%=vi$A(ADj8dq<3& zeka?b2{00X@0j|xgXWxYXq|zV1>ItT=?|9xEAh|oZ3$aN zzkjx40`C+c(wP7$5soE5OMJfLGolrdDBcXAHNZ%CO&t({Rt%yTGaNGj7gHjtv2MlfRo~*83`>B!j6E-{ftHv%o7ANgnb)WN{;vZQ-+K( zLx=%-!h0>YX50vqrbvKZAt&Lwe!lQ|GWGv&wZA$6IH!!T@{HcSnt4Ww3|Rvp(~Eu0 zaeyE;WCA<|1hLH$z+;D;U?;+@jwb}M-4mb=Y}vQIE=HNl>bVGFnN<7b?ET#5s5w9+lB1wU>ZrEY z;5dj`571lqe-=hBqpAC}%0Mr!+6brw^sFlytjfR)`K|l4?3_Dq*#Q9FPq12`TybE;h?xBa7J^-MfO=kZCwRxe0ahIVpik21b8{zvjsrx_egNB&rPT|pW<2%$ z=Kx?QP)-@pBp=7a>(LlV0YuM!OK{2np2xL&t$5cu0DzrPZxumP05ALhf_oYOh%JKS z9l#1)2dL+3ia-)~*aMt|=w$#v*0`1Vqjjk`z_WiEGnq+2%eJ-SU*P~&7#-DC=5xn_ zmKekiaB}vaPzLaPX*rkT8GWvW9wAb~cEpm3KpH1~&z0F(S-|Z5J4ImO00rJ6P;&q-A<`oe{=41~37!b% z$4`&We{`hLbcvB=N}!Gcsm+K8jm9qm`8j8v0HkPbf^~l&fu`+ykS=xr>E_{0v;dWH z(s-wnZ|(pqq({wuY3;A$c#kOyc3GRGQAyB>EyWF)@7e=kP|c7ww93G4@&OL|Uw~v& zQ%Yv!Ye7O1o*&5}+D6}X^BM914*X4ES+pj4 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_5.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_5.png new file mode 100644 index 0000000000000000000000000000000000000000..70ed4f55900322b30ba524f45b48dc81abd88ea6 GIT binary patch literal 1453 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 KMNUMnLSTY%ti=xi literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_51.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_51.png new file mode 100644 index 0000000000000000000000000000000000000000..a81457ddc24d2fe1497619616898667439bf4aa5 GIT binary patch literal 1159 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 IM6N<$g4?E4Y5)KL literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_54.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_54.png new file mode 100644 index 0000000000000000000000000000000000000000..abab31964359435749bdb0c6a4579e9cd469cbcb GIT binary patch literal 830 zcmV-E1Ht@>P)V57)f|3AMIKJcv;XmR6u;BQTBZLoMl8sw*K45oTD-aWabt79J zpBADKz&b|+LIT*yuse-a0(JrHXwV(U%th!Fa3`t=SOpN(h&#h301wXhFo#}F3JA^- zKtZ%9z#;(4MmyWSZB7KJU|G=IxD>!y^>n5MV0pEql!nJ=d0w2jivVZ!_u)zarSp4N zaYXUB5I{=lkMp>~cZ4+XJ@e&N2CA)8uCdJ24LdR=fK^0tkTfn*02f(Uk!pb>fP!ez zW%yLEmZ4@Vd#--HvZMy+F>dK_EkRt@6+#560vIvqst6DT^%PJ(W<805x&oyI;XMkd z7lEZ?vjgvreEMDfxg+Ty@6HDXBLK&$dy52jq15{0+_0(j6T$hwLID7@bRg~gr+%Zb zQ~*GI^|k(vyZ>7IkyySJOx=B(DLOgY@yhZDz|C7kX+@HVQDAul;O0%7WzAt(%o=n@ z0PhBi76s1Bpx_(<{6c*i@rJt^V3a5?e@adSxDjpj14jV=h$ezA0YouQlz9Rt0z48$ z1SX3>$z$I=L?{eR37}6bxKA*TS1lQ+~p%9}uvco!RaD_GkQGY3bx@tg+C7J;Qx zpN@n9z8xfu*$9w%OAQ+D`#lTHYy`->RpMn-(5w$UK#BkwtDmycDdUiMg!C*ZGXel; zr+Ipw6l#pjiMnaH3h6R5cL^<ZfQ8BQT;9kcklqZs zJ3AMn z<}C$qw|Q@S#`#u7;7S01?EeSub>0x$iEP)hy<_* zJ_RD^!V*o7u#%&O03hc`0Z;|_d<5`()3EJ3k0UDpk#C_r z;Z^|j`Zc_WeD{_RLU;Q-e=aY$tN`f!>u@JP8osm_LmnA!1PCE~g{M`%F$~msTiu{s zPt|J6$h_fcs2&TKfPj49W(d$CiiNoVkK+U&LUoa_WC$3Y+4pc=7pee*ZUH1WHw}E1 z5RjmaSb&}zYEc0Ik8pLQkuI}JQgD0VY#~qu%5+Tv(_mwO_aIBZi$8Z}288hIbX9=Q zA2%w&U6kBqG&gK=*FpGgh6Etu4F|KsFI^Y385V#j-}`E>;^hlU?kdpU+0C~*j0KMhC_Xs-3v;0u#>@8x1EyLtgaP7NpKLJYXIZ#1#klzT{(a7NahU)Ud9jeWpgadSBJL%0000Ax&5g|9SQvf35M1BlJf?R~m0gxaMVS^w_LJqXk~x6% zAAMQO_>pJkRRZ>Ey8;A^!|lgY!jz?<<&**c=cfRB^b&($>sc~2fZQyiXE8f>MJob? z2s&Paprg9mJ0R{(&;zJW0;2-4inkp-LqGodh?w^PzQ11C6KnfEg0G|7GlFWbw}L=C z(tUunx#J4YLy8APkP+fYy%7ZcNh1JRAciyoKnx!Otp5nAn>0=baXifG0Z;~olR+GZ z8$l`&5FiF`-3L?5>XATCa}%gY)ARs(PMrd%)%WAlT6j+h$O2j}%>l>+_u)%`*1#gc zJ@^vfOkjISKpD`nTLR8XKsFeYssKkfg4W(oNf?ve46q#ww~BxSWAOw8F7rO*vnWIW O0000 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_57.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_57.png new file mode 100644 index 0000000000000000000000000000000000000000..d142a38db7a8c8f5b19c2cb7c8aaa779f3baad7d GIT binary patch literal 398 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!3HERU8}DLQk(@Ik;M!Q+`=Ht$S`Y;1Oo%3 zgQtsQNX4ADcMbavIf$@aoVWA;+;m}u2*yL-Jg)r@)mLQQnbB-uFlh}(_Ptv65`E?d zV+IQniP@f;riHf!pWPaEOkzDpPh+Yb|G)pyhG)vz@4G(uv4cU6>F{+{gQkCVaSX~Y zUS435aI|k1KfqCCxs}1~!ts|tDLFme2Ei-km5e9IFK)8bQBXNOc%YUOT*N^#bP z#S8@^41G)o0vRm07~B~jXfVhOjG(uR%i7=e$G@JNe{5P{<>tq6Z?9M9mHoYG<#t5q zpSi%+kpOt;v-(y?1$6+Vj)v zj&C>H@UF6Z(%dg^T<3>gzdL=V>~nSblPiwy*%h%^?fW*C#lO$j@#pGny%y!wdA@SK z&;0zqx2l&;x%=1W-|o`bvm39KnU-5GS9a?)ZAy*)P*o$6y>|I`+}|JyM)Q&I~USqz@8 KelF{r5}E*`z{1!7 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_59.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_59.png new file mode 100644 index 0000000000000000000000000000000000000000..4ab56d31ed0c210026f3eb0bf636e5e530d9a94d GIT binary patch literal 970 zcmeAS@N?(olHy`uVBq!ia0vp^4M6O`!3HERU8}DLQk(@Ik;M!Q+`=Ht$S`Y;1Oo$e zji-xaNX4ADF|YgHC~z#BT=_q|tlr0}kmH2wmAnVuem1!iyX%^E-Q3Htz)?{*Zn><+ z99KaF%PxkKfh-<(m--v9hTD{XN2d>F=x8_n#2Ee(k2%e|i5Jc^x&T zrTxF`CdFS2n((IhV{qZu2hWl{IXl+PobzYR-kTO-Z&~+z-1X?^p7`0{G(9vYdtJQZ zkdYhMta!3A=$DVY|H~;}jLY92TP@z_kr@{G?*3bgsX{xy9N${Ou%pC-_t82d1FzS2 zemp8%^d;zTT~t5{Lyzd{batD_Q%dhy&oSLOR`$j3(&t8&hxXoiVe9p*BYU-6Mz3Hc?;aB>PwX%m0_y^DFqb#k-%Ibn4IO z@!p;{hcj*SfmKBtvcEDj?Ah!0watw$D}SXQ!+qldqj~wu6TZG&n>bxX`7Qg3xj8Cn zn|eyRe}36}Nax-x#tSU^6Avs7X7&X9fL3wAP%G-pn}b-0o9B?mv&-Ep_p^kP)+`*wESQlZe2< zqMGhwvgV6(GM=i=p7!^^l>@@N+-7=PmF*VSp3bPTIPK}7u3Z~{OQ`wINjq9N`Qb^U zd6y(YO*Pv3dvfe|_4_;zWO>&9*(4%1VQHG5{q{G# zGr6T#&*S*>$D%?*qToohV^hc38yT0guC6_q6Sz$&;aB2>t!7^L=TBm_khNAzyI$De z{L;MHZELjfi4|q{tZX`N1P54!`JZ`tlAYlPv)KB_b-ZOw(}0^XQAsmfBhbQi;RFbiTkZBNL zCr4}TY>RQ8|5Z4SrV~IiK7S!Z8fQfdZ>8QH`ktKs}#&@AWgy5BSZZ^0H6sB1EHWOGdNgS@I<^j3UA`0-(mHkY4+; z&ee?Zk)uXh&x>d%Jg=wgRFYt71V9aBz3<0KpydE4qVu`ekMK;(>#K8{rB%QMExbnO z_MZ9CdHh%!U1fO3UIys-m5!D!^jc*fz#^jL^;PRHj{sIkLpE4t zd@swFKqEoMks{?TKOUqUD5A@70$fT*7;YJrR|gSK@G@;pFJ#P&o&&FrG@ySo(OPTo zneEI~FGgy;1q4X;oe9TWE*f>bF?neMX~L4@N~1t5jW{ z1I)&54{&;;Qcv|GCE&}S21e^@@BHJaXcW=4z1H(Af^lQgvJ{yg`o9$Ri30XVWovpjxg`NpxjFL4_Ja6}9_ zvTR#P9tr3^OB+En>Clv@x=B)LsO1T>9@0b+seZEX93o5r$=Li1=2?GL{Y@bmR|@mU znxi{}2r>_oB65D*v)1%~6Q>zB^4(Ja1t*tOGDf>%(#@LrFekGZFkXC4?roHG2;)&FHkGy1uNCy;C#90&?SMDY@Ieq7lX$`?8EK36tth zYu;`*@B*UOY7!vF13Fj3%QHHMMi@SZE3*?KhNaPO)IN~JI)aF-coS!|_qP^o1@mz4dByILptZP8BT0-#VGGgyz!AV}LPR=KJ4J%t%4KvUnBo1c z{mTKeLcKF==SXnr#;YA9y6v=&*81-2n{Fi9ex#7jE$R9QRP-OT+TYvLZiwpix+ZBQ zUbN;!ja)?rN?u;SqifJT^4w-ePp`zcBv2~xMk0G-waC>&1Wp1T8Aq0Z73j6sk1D_m zdd!r8kRt3IZ34;a$J^$$SvSel>5@gn#jnLCTt29A2 pf|NGBB~3+X#%0WCgsna*{{Si6l5o!t=uZFu002ovPDHLkV1iowx`qG% literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_60.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_60.png new file mode 100644 index 0000000000000000000000000000000000000000..0cb8722e3ff732af6e1d70e2fe64d9fc6bfade9d GIT binary patch literal 1370 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/L1_Sad_song_128x64/frame_9.png b/assets/dolphin/external/L1_Sad_song_128x64/frame_9.png new file mode 100644 index 0000000000000000000000000000000000000000..6062721194053f604d5429f76901e30549cfbbe8 GIT binary patch literal 1480 zcmV;(1vmPMP)78LzU4Q3Gm@~djecNM9;?C6QCP3 z_n1AeQzke5CcuZ|@&vd;d<~Z;z>LuPNw_=#?g&uB{fEq4OfI9-zaCrjU5uk?4 z6X1>jU&wBR%M)N{1b7GXU7i3to$wCF&ug~=Rd7xi*N)?u`pDLjOLg-SW+kalK>*HB zULy=AOV?LN2&_xxJ%g$YFSZgudx^vAJZ)FaHXiqaO2?Y?1(`0n9JUZZdaH-RLjoEB zs^_vapVI@ct&!OSY5oj?VJs&A=X{Ovo>#iKzoYOv()0RSCWKpay!MDo5q^66ErRCcah)}WgvGmnX|J3sg#t^xeG05M@}^wZ6$!$=yb=^ z?Az-IE%Ee3CQjE#$!h85S!;~(CdSs-*Xnkrbgo;D=iqfdS6NUwmV9f>iZ7T%6ILvr zyAH4*2avL6s+jhzA5I>g;~_!zK0W2v?&H8S2Y`=&mwy=4@oKaOmXot2uoB=-jE#e% z)>Z;YXSv1hNV3z}C7g}uQGhFvCyfHURhV5k_*Y)E%fOvdm zttz@ZaaBYTJ`PK(fJ(U1K8rlS>3~Lpl?36|ias+~QUwf1Y#o1dQ9v5c$*btL8=XA` zEYFfP{Rp7tn#NF1`Nv(0?mDh7kZlK7YJaRl1(IDoI&NzON#T=@<37XJDvqCph+dq- zGHYdzq~%qB*Z8<*#mHv6(y>xOHvgb=x*l$2=1nJn6t<%>sN=aVqGSMXpM58^TxsTd zEd;Y!j6L8E4aQ zOn+BF&vEM^jfF}(H4Ca2wR1!tEuWy**-Gj-jw9mKi1RrIR7)pnB#8`G#xL!>l}C*< z$D@lhhUP{X>{?&f`<(7kJY!^yTpnXcI&eFtqF>j%4su|K8=)%#SPOY>PP>_|onloC z1FJ~D+0H{;O%4Zf9yCXQh-0gci;@UKBUDsAo?n@hIXbQAC&~LBmyG}#DvqHtET=@B z>y1#FENxzuG;E?KT(k6ZjR8jlpqR0HGSCde>5z$1k?~p3D3Bq9C086HMF5KE&hgI4 z!h3{JJG- zc0Y%++z*G)(H7bMTy1VGI5QGd!*MkV%sLKM?S6p3D`A`xo^!El$IZad&C`#Fezpry zot_@&c*4Ech@E8UAQ#qcB#8LOBFNS8v~JQHej?5|D+z`jusyCqgzRzj1dvOks8G!r zGs9&k38dY>iU>pYDzg1a0zD<3Db#4u4xFVlv}8!Vjt>1byLf$3NoLt9prb~X`rBu& z3M@I9Do9&{%F-Ha(&q!|%_>CzZqcxCrN51FB@%F-X%C>dxpZ76+;`dR7Xd0yp8g0n zh!ineW$jsVtcOh!tTwtNXI2jNO@IjFO`#4VowZfW$pP>;yDANvnVUk? Date: Thu, 10 Aug 2023 19:34:51 +0200 Subject: [PATCH 20/99] feat(infrared): add universal remote (#2818) * feat(infrared): add universal remote * fix: fix channel commands names Co-authored-by: Aleksandr Kutuzov --- assets/resources/infrared/assets/tv.ir | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index c4b6f0c42..a223c97c9 100644 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1717,3 +1717,41 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3981 3992 495 1994 495 1995 496 1996 494 1996 495 1005 495 1006 494 1995 495 1997 494 1996 495 1996 494 1997 494 1996 495 1006 494 1005 495 1004 496 1005 495 1995 496 1994 496 1005 495 1004 496 1005 495 1006 494 1004 496 1006 494 8466 3978 3991 495 1994 495 1997 493 1994 496 1995 495 1004 496 1004 496 1996 494 1997 493 1996 494 1995 495 1995 495 1997 493 1004 495 1004 495 1006 494 1005 494 1998 491 1996 494 1006 494 1004 496 1006 494 1006 493 1005 495 1005 571 +# +# Thomson Remote +# Model RC3000E02 +name: Power +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 54 00 00 00 +# +name: Vol_up +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: F4 00 00 00 +# +name: Vol_dn +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 74 00 00 00 +# +name: Ch_next +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: B4 00 00 00 +# +name: Ch_prev +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 34 00 00 00 +# +name: Mute +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: FC 00 00 00 From 5f48968a0558853c3767fd2d3d58739d99aa1fd0 Mon Sep 17 00:00:00 2001 From: 47LeCoste <82815207+grugnoymeme@users.noreply.github.com> Date: Wed, 9 Aug 2023 23:10:04 +0200 Subject: [PATCH 21/99] BadUSB: qFlipper install script for MacOS (#2915) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create Install_qFlipper_macOS.txt * BadUSB: qFlipper mac install routine improvements Co-authored-by: あく --- .../resources/badusb/Install_qFlipper_macOS.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 assets/resources/badusb/Install_qFlipper_macOS.txt diff --git a/assets/resources/badusb/Install_qFlipper_macOS.txt b/assets/resources/badusb/Install_qFlipper_macOS.txt new file mode 100644 index 000000000..252954ecb --- /dev/null +++ b/assets/resources/badusb/Install_qFlipper_macOS.txt @@ -0,0 +1,16 @@ +ID 05ac:021e Apple:Keyboard +REM Keep these 3 lines IF (and only if) it's the first time you are performing a badKB attack against a specific macOS target. +REM In fact, it helps Flipper Zero bypass the macOS keyboard setup assistant. Otherwise the attack will not start. +REM Author: 47LeCoste +REM Version 1.0 (Flipper Ducky) +REM Target: macOS +DELAY 3000 +F4 +DELAY 2500 +STRING Terminal +DELAY 2500 +ENTER +DELAY 1500 +STRING (cd /tmp && curl -L -o qFlipper.dmg https://update.flipperzero.one/qFlipper/release/macos-amd64/dmg && hdiutil attach qFlipper.dmg && app_volume=$(ls /Volumes | grep -i "qFlipper") && (test -e /Applications/qFlipper.app && rm -rf /Applications/qFlipper.app ); cp -R "/Volumes/$app_volume/qFlipper.app" /Applications/ && hdiutil detach "/Volumes/$app_volume" && rm qFlipper.dmg && open /Applications/qFlipper.app) +DELAY 1000 +ENTER From 09d5b3b1ed6affe38507471fe875c0e9d4e03ecf Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 11 Aug 2023 17:55:40 +0300 Subject: [PATCH 22/99] Expose additional functions of the crypto engine to user (#2923) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow loading user supplied keys and add CTR mode * Add GCM mode to furi_hal_crypto * Split up CTR and GCM code, add flag for adv crypto * Add convenience functions for GCM crypto * Run fbt format * Update GCM to support additional auth data * Update APIs * FuriHal: update crypto documentation, method names and usage * Clean up code for key (un)loading, GCM and CTR - get rid of goto - do not use furi_hal_bt_is_alive() when not using secure enclave - give defines a type and wrap in () * Add unit test for CTR and GCM crypto * FuriHal: const in crypto unit tests, cortex timer for crypto operations timeouts * FuriHal: update crypto docs Co-authored-by: twisted_pear Co-authored-by: hedger Co-authored-by: あく --- .../furi_hal/furi_hal_crypto_tests.c | 602 ++++++++++++++++++ applications/debug/unit_tests/test_index.c | 2 + applications/main/u2f/u2f_data.c | 30 +- applications/services/crypto/crypto_cli.c | 22 +- firmware/targets/f18/api_symbols.csv | 18 +- firmware/targets/f7/api_symbols.csv | 18 +- .../targets/f7/furi_hal/furi_hal_crypto.c | 446 ++++++++++++- firmware/targets/f7/furi_hal/furi_hal_info.c | 2 +- .../furi_hal_include/furi_hal_crypto.h | 239 ++++++- lib/subghz/subghz_keystore.c | 16 +- 10 files changed, 1302 insertions(+), 93 deletions(-) create mode 100644 applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c diff --git a/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c b/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c new file mode 100644 index 000000000..b06d51130 --- /dev/null +++ b/applications/debug/unit_tests/furi_hal/furi_hal_crypto_tests.c @@ -0,0 +1,602 @@ +#include +#include +#include +#include "../minunit.h" + +static const uint8_t key_ctr_1[32] = { + 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, + 0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04, +}; +static const uint8_t iv_ctr_1[16] = { + 0x00, + 0x00, + 0x00, + 0x60, + 0xDB, + 0x56, + 0x72, + 0xC9, + 0x7A, + 0xA8, + 0xF0, + 0xB2, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_1[16] = { + 0x53, + 0x69, + 0x6E, + 0x67, + 0x6C, + 0x65, + 0x20, + 0x62, + 0x6C, + 0x6F, + 0x63, + 0x6B, + 0x20, + 0x6D, + 0x73, + 0x67, +}; +static const uint8_t tv_ctr_ct_1[16] = { + 0x14, + 0x5A, + 0xD0, + 0x1D, + 0xBF, + 0x82, + 0x4E, + 0xC7, + 0x56, + 0x08, + 0x63, + 0xDC, + 0x71, + 0xE3, + 0xE0, + 0xC0, +}; + +static const uint8_t key_ctr_2[32] = { + 0x77, 0x6B, 0xEF, 0xF2, 0x85, 0x1D, 0xB0, 0x6F, 0x4C, 0x8A, 0x05, 0x42, 0xC8, 0x69, 0x6F, 0x6C, + 0x6A, 0x81, 0xAF, 0x1E, 0xEC, 0x96, 0xB4, 0xD3, 0x7F, 0xC1, 0xD6, 0x89, 0xE6, 0xC1, 0xC1, 0x04, +}; +static const uint8_t iv_ctr_2[16] = { + 0x00, + 0x00, + 0x00, + 0x60, + 0xDB, + 0x56, + 0x72, + 0xC9, + 0x7A, + 0xA8, + 0xF0, + 0xB2, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_2[0] = {}; +//static const uint8_t tv_ctr_ct_2[0] = {}; + +static const uint8_t key_ctr_3[32] = { + 0xF6, 0xD6, 0x6D, 0x6B, 0xD5, 0x2D, 0x59, 0xBB, 0x07, 0x96, 0x36, 0x58, 0x79, 0xEF, 0xF8, 0x86, + 0xC6, 0x6D, 0xD5, 0x1A, 0x5B, 0x6A, 0x99, 0x74, 0x4B, 0x50, 0x59, 0x0C, 0x87, 0xA2, 0x38, 0x84, +}; +static const uint8_t iv_ctr_3[16] = { + 0x00, + 0xFA, + 0xAC, + 0x24, + 0xC1, + 0x58, + 0x5E, + 0xF1, + 0x5A, + 0x43, + 0xD8, + 0x75, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_3[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, +}; +static const uint8_t tv_ctr_ct_3[32] = { + 0xF0, 0x5E, 0x23, 0x1B, 0x38, 0x94, 0x61, 0x2C, 0x49, 0xEE, 0x00, 0x0B, 0x80, 0x4E, 0xB2, 0xA9, + 0xB8, 0x30, 0x6B, 0x50, 0x8F, 0x83, 0x9D, 0x6A, 0x55, 0x30, 0x83, 0x1D, 0x93, 0x44, 0xAF, 0x1C, +}; + +static const uint8_t key_ctr_4[32] = { + 0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2, + 0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D, +}; +static const uint8_t iv_ctr_4[16] = { + 0x00, + 0x1C, + 0xC5, + 0xB7, + 0x51, + 0xA5, + 0x1D, + 0x70, + 0xA1, + 0xC1, + 0x11, + 0x48, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_4[36] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, +}; +static const uint8_t tv_ctr_ct_4[36] = { + 0xEB, 0x6C, 0x52, 0x82, 0x1D, 0x0B, 0xBB, 0xF7, 0xCE, 0x75, 0x94, 0x46, + 0x2A, 0xCA, 0x4F, 0xAA, 0xB4, 0x07, 0xDF, 0x86, 0x65, 0x69, 0xFD, 0x07, + 0xF4, 0x8C, 0xC0, 0xB5, 0x83, 0xD6, 0x07, 0x1F, 0x1E, 0xC0, 0xE6, 0xB8, +}; + +static const uint8_t key_ctr_5[32] = { + 0xFF, 0x7A, 0x61, 0x7C, 0xE6, 0x91, 0x48, 0xE4, 0xF1, 0x72, 0x6E, 0x2F, 0x43, 0x58, 0x1D, 0xE2, + 0xAA, 0x62, 0xD9, 0xF8, 0x05, 0x53, 0x2E, 0xDF, 0xF1, 0xEE, 0xD6, 0x87, 0xFB, 0x54, 0x15, 0x3D, +}; +static const uint8_t iv_ctr_5[16] = { + 0x00, + 0x1C, + 0xC5, + 0xB7, + 0x51, + 0xA5, + 0x1D, + 0x70, + 0xA1, + 0xC1, + 0x11, + 0x48, + 0x00, + 0x00, + 0x00, + 0x01, +}; +static const uint8_t pt_ctr_5[0] = {}; +//static const uint8_t tv_ctr_ct_5[0] = {}; + +static const uint8_t key_gcm_1[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const uint8_t iv_gcm_1[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_1[0] = {}; +//static const uint8_t tv_gcm_ct_1[0] = {}; +static const uint8_t aad_gcm_1[0] = {}; +static const uint8_t tv_gcm_tag_1[16] = { + 0x53, + 0x0F, + 0x8A, + 0xFB, + 0xC7, + 0x45, + 0x36, + 0xB9, + 0xA9, + 0x63, + 0xB4, + 0xF1, + 0xC4, + 0xCB, + 0x73, + 0x8B, +}; + +static const uint8_t key_gcm_2[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const uint8_t iv_gcm_2[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_2[16] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t tv_gcm_ct_2[16] = { + 0xCE, + 0xA7, + 0x40, + 0x3D, + 0x4D, + 0x60, + 0x6B, + 0x6E, + 0x07, + 0x4E, + 0xC5, + 0xD3, + 0xBA, + 0xF3, + 0x9D, + 0x18, +}; +static const uint8_t aad_gcm_2[0] = {}; +static const uint8_t tv_gcm_tag_2[16] = { + 0xD0, + 0xD1, + 0xC8, + 0xA7, + 0x99, + 0x99, + 0x6B, + 0xF0, + 0x26, + 0x5B, + 0x98, + 0xB5, + 0xD4, + 0x8A, + 0xB9, + 0x19, +}; + +static const uint8_t key_gcm_3[32] = { + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, +}; +static const uint8_t iv_gcm_3[16] = { + 0xCA, + 0xFE, + 0xBA, + 0xBE, + 0xFA, + 0xCE, + 0xDB, + 0xAD, + 0xDE, + 0xCA, + 0xF8, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_3[64] = { + 0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, 0x9A, + 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, 0x8A, 0x72, + 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, 0xA6, 0xB5, 0x25, + 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, 0x1A, 0xAF, 0xD2, 0x55, +}; +static const uint8_t tv_gcm_ct_3[64] = { + 0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, 0x7D, + 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, 0xD1, 0xAA, + 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, 0x82, 0x88, 0x38, + 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, 0x89, 0x80, 0x15, 0xAD, +}; +static const uint8_t aad_gcm_3[0] = {}; +static const uint8_t tv_gcm_tag_3[16] = { + 0xB0, + 0x94, + 0xDA, + 0xC5, + 0xD9, + 0x34, + 0x71, + 0xBD, + 0xEC, + 0x1A, + 0x50, + 0x22, + 0x70, + 0xE3, + 0xCC, + 0x6C, +}; + +static const uint8_t key_gcm_4[32] = { + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, + 0xFE, 0xFF, 0xE9, 0x92, 0x86, 0x65, 0x73, 0x1C, 0x6D, 0x6A, 0x8F, 0x94, 0x67, 0x30, 0x83, 0x08, +}; +static const uint8_t iv_gcm_4[16] = { + 0xCA, + 0xFE, + 0xBA, + 0xBE, + 0xFA, + 0xCE, + 0xDB, + 0xAD, + 0xDE, + 0xCA, + 0xF8, + 0x88, + 0x00, + 0x00, + 0x00, + 0x00, +}; +static const uint8_t pt_gcm_4[60] = { + 0xD9, 0x31, 0x32, 0x25, 0xF8, 0x84, 0x06, 0xE5, 0xA5, 0x59, 0x09, 0xC5, 0xAF, 0xF5, 0x26, + 0x9A, 0x86, 0xA7, 0xA9, 0x53, 0x15, 0x34, 0xF7, 0xDA, 0x2E, 0x4C, 0x30, 0x3D, 0x8A, 0x31, + 0x8A, 0x72, 0x1C, 0x3C, 0x0C, 0x95, 0x95, 0x68, 0x09, 0x53, 0x2F, 0xCF, 0x0E, 0x24, 0x49, + 0xA6, 0xB5, 0x25, 0xB1, 0x6A, 0xED, 0xF5, 0xAA, 0x0D, 0xE6, 0x57, 0xBA, 0x63, 0x7B, 0x39, +}; +static const uint8_t tv_gcm_ct_4[60] = { + 0x52, 0x2D, 0xC1, 0xF0, 0x99, 0x56, 0x7D, 0x07, 0xF4, 0x7F, 0x37, 0xA3, 0x2A, 0x84, 0x42, + 0x7D, 0x64, 0x3A, 0x8C, 0xDC, 0xBF, 0xE5, 0xC0, 0xC9, 0x75, 0x98, 0xA2, 0xBD, 0x25, 0x55, + 0xD1, 0xAA, 0x8C, 0xB0, 0x8E, 0x48, 0x59, 0x0D, 0xBB, 0x3D, 0xA7, 0xB0, 0x8B, 0x10, 0x56, + 0x82, 0x88, 0x38, 0xC5, 0xF6, 0x1E, 0x63, 0x93, 0xBA, 0x7A, 0x0A, 0xBC, 0xC9, 0xF6, 0x62, +}; +static const uint8_t aad_gcm_4[20] = { + 0xFE, 0xED, 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED, + 0xFA, 0xCE, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0xAD, 0xDA, 0xD2, +}; +static const uint8_t tv_gcm_tag_4[16] = { + 0x76, + 0xFC, + 0x6E, + 0xCE, + 0x0F, + 0x4E, + 0x17, + 0x68, + 0xCD, + 0xDF, + 0x88, + 0x53, + 0xBB, + 0x2D, + 0x55, + 0x1B, +}; + +static void furi_hal_crypto_ctr_setup() { +} + +static void furi_hal_crypto_ctr_teardown() { +} + +static void furi_hal_crypto_gcm_setup() { +} + +static void furi_hal_crypto_gcm_teardown() { +} + +MU_TEST(furi_hal_crypto_ctr_1) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_1)]; + + ret = furi_hal_crypto_ctr(key_ctr_1, iv_ctr_1, pt_ctr_1, ct, sizeof(pt_ctr_1)); + mu_assert(ret, "CTR 1 failed"); + mu_assert_mem_eq(tv_ctr_ct_1, ct, sizeof(pt_ctr_1)); +} + +MU_TEST(furi_hal_crypto_ctr_2) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_2)]; + + ret = furi_hal_crypto_ctr(key_ctr_2, iv_ctr_2, pt_ctr_2, ct, sizeof(pt_ctr_2)); + mu_assert(ret, "CTR 2 failed"); + //mu_assert_mem_eq(tv_ctr_ct_2, ct, sizeof(pt_ctr_2)); +} + +MU_TEST(furi_hal_crypto_ctr_3) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_3)]; + + ret = furi_hal_crypto_ctr(key_ctr_3, iv_ctr_3, pt_ctr_3, ct, sizeof(pt_ctr_3)); + mu_assert(ret, "CTR 3 failed"); + mu_assert_mem_eq(tv_ctr_ct_3, ct, sizeof(pt_ctr_3)); +} + +MU_TEST(furi_hal_crypto_ctr_4) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_4)]; + + ret = furi_hal_crypto_ctr(key_ctr_4, iv_ctr_4, pt_ctr_4, ct, sizeof(pt_ctr_4)); + mu_assert(ret, "CTR 4 failed"); + mu_assert_mem_eq(tv_ctr_ct_4, ct, sizeof(pt_ctr_4)); +} + +MU_TEST(furi_hal_crypto_ctr_5) { + bool ret = false; + uint8_t ct[sizeof(pt_ctr_5)]; + + ret = furi_hal_crypto_ctr(key_ctr_5, iv_ctr_5, pt_ctr_5, ct, sizeof(pt_ctr_5)); + mu_assert(ret, "CTR 5 failed"); + //mu_assert_mem_eq(tv_ctr_ct_5, ct, sizeof(pt_ctr_5)); +} + +MU_TEST(furi_hal_crypto_gcm_1) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_1)]; + uint8_t ct[sizeof(pt_gcm_1)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_1, + iv_gcm_1, + aad_gcm_1, + sizeof(aad_gcm_1), + pt_gcm_1, + ct, + sizeof(pt_gcm_1), + tag_enc, + false); + mu_assert(ret, "GCM 1 encryption failed"); + //mu_assert_mem_eq(tv_gcm_ct_1, ct, sizeof(pt_gcm_1)); + mu_assert_mem_eq(tv_gcm_tag_1, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_1, iv_gcm_1, aad_gcm_1, sizeof(aad_gcm_1), ct, pt, sizeof(pt_gcm_1), tag_dec, true); + mu_assert(ret, "GCM 1 decryption failed"); + //mu_assert_mem_eq(pt_gcm_1, pt, sizeof(pt_gcm_1)); + mu_assert_mem_eq(tv_gcm_tag_1, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_2) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_2)]; + uint8_t ct[sizeof(pt_gcm_2)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_2, + iv_gcm_2, + aad_gcm_2, + sizeof(aad_gcm_2), + pt_gcm_2, + ct, + sizeof(pt_gcm_2), + tag_enc, + false); + mu_assert(ret, "GCM 2 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_2, ct, sizeof(pt_gcm_2)); + mu_assert_mem_eq(tv_gcm_tag_2, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_2, iv_gcm_2, aad_gcm_2, sizeof(aad_gcm_2), ct, pt, sizeof(pt_gcm_2), tag_dec, true); + mu_assert(ret, "GCM 2 decryption failed"); + mu_assert_mem_eq(pt_gcm_2, pt, sizeof(pt_gcm_2)); + mu_assert_mem_eq(tv_gcm_tag_2, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_3) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_3)]; + uint8_t ct[sizeof(pt_gcm_3)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_3, + iv_gcm_3, + aad_gcm_3, + sizeof(aad_gcm_3), + pt_gcm_3, + ct, + sizeof(pt_gcm_3), + tag_enc, + false); + mu_assert(ret, "GCM 3 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_3, ct, sizeof(pt_gcm_3)); + mu_assert_mem_eq(tv_gcm_tag_3, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_3, iv_gcm_3, aad_gcm_3, sizeof(aad_gcm_3), ct, pt, sizeof(pt_gcm_3), tag_dec, true); + mu_assert(ret, "GCM 3 decryption failed"); + mu_assert_mem_eq(pt_gcm_3, pt, sizeof(pt_gcm_3)); + mu_assert_mem_eq(tv_gcm_tag_3, tag_dec, 16); +} + +MU_TEST(furi_hal_crypto_gcm_4) { + bool ret = false; + uint8_t pt[sizeof(pt_gcm_4)]; + uint8_t ct[sizeof(pt_gcm_4)]; + uint8_t tag_enc[16]; + uint8_t tag_dec[16]; + + ret = furi_hal_crypto_gcm( + key_gcm_4, + iv_gcm_4, + aad_gcm_4, + sizeof(aad_gcm_4), + pt_gcm_4, + ct, + sizeof(pt_gcm_4), + tag_enc, + false); + mu_assert(ret, "GCM 4 encryption failed"); + mu_assert_mem_eq(tv_gcm_ct_4, ct, sizeof(pt_gcm_4)); + mu_assert_mem_eq(tv_gcm_tag_4, tag_enc, 16); + + ret = furi_hal_crypto_gcm( + key_gcm_4, iv_gcm_4, aad_gcm_4, sizeof(aad_gcm_4), ct, pt, sizeof(pt_gcm_4), tag_dec, true); + mu_assert(ret, "GCM 4 decryption failed"); + mu_assert_mem_eq(pt_gcm_4, pt, sizeof(pt_gcm_4)); + mu_assert_mem_eq(tv_gcm_tag_4, tag_dec, 16); +} + +MU_TEST_SUITE(furi_hal_crypto_ctr_test) { + MU_SUITE_CONFIGURE(&furi_hal_crypto_ctr_setup, &furi_hal_crypto_ctr_teardown); + MU_RUN_TEST(furi_hal_crypto_ctr_1); + MU_RUN_TEST(furi_hal_crypto_ctr_2); + MU_RUN_TEST(furi_hal_crypto_ctr_3); + MU_RUN_TEST(furi_hal_crypto_ctr_4); + MU_RUN_TEST(furi_hal_crypto_ctr_5); +} + +MU_TEST_SUITE(furi_hal_crypto_gcm_test) { + MU_SUITE_CONFIGURE(&furi_hal_crypto_gcm_setup, &furi_hal_crypto_gcm_teardown); + MU_RUN_TEST(furi_hal_crypto_gcm_1); + MU_RUN_TEST(furi_hal_crypto_gcm_2); + MU_RUN_TEST(furi_hal_crypto_gcm_3); + MU_RUN_TEST(furi_hal_crypto_gcm_4); +} + +int run_minunit_test_furi_hal_crypto() { + MU_RUN_SUITE(furi_hal_crypto_ctr_test); + MU_RUN_SUITE(furi_hal_crypto_gcm_test); + return MU_EXIT_CODE; +} diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index 9d7631bfe..f7a53d7c8 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -10,6 +10,7 @@ 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(); @@ -39,6 +40,7 @@ typedef struct { const UnitTest unit_tests[] = { {.name = "furi", .entry = run_minunit_test_furi}, {.name = "furi_hal", .entry = run_minunit_test_furi_hal}, + {.name = "furi_hal_crypto", .entry = run_minunit_test_furi_hal_crypto}, {.name = "furi_string", .entry = run_minunit_test_furi_string}, {.name = "storage", .entry = run_minunit_test_storage}, {.name = "stream", .entry = run_minunit_test_stream}, diff --git a/applications/main/u2f/u2f_data.c b/applications/main/u2f/u2f_data.c index f6a8d84d3..8489ed91e 100644 --- a/applications/main/u2f/u2f_data.c +++ b/applications/main/u2f/u2f_data.c @@ -14,7 +14,7 @@ #define U2F_CNT_FILE U2F_DATA_FOLDER "cnt.u2f" #define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_FACTORY 2 -#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE 11 +#define U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT #define U2F_CERT_STOCK 0 // Stock certificate, private key is encrypted with factory key #define U2F_CERT_USER 1 // User certificate, private key is encrypted with unique key @@ -136,7 +136,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { // Generate random IV furi_hal_random_fill_buf(iv, 16); - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -145,7 +145,7 @@ static bool u2f_data_cert_key_encrypt(uint8_t* cert_key) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -178,8 +178,8 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { uint8_t key_slot = 0; uint32_t version = 0; - // Check if unique key exists in secure eclave(typo?) and generate it if missing - if(!furi_hal_crypto_verify_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; + // Check if unique key exists in secure eclave and generate it if missing + if(!furi_hal_crypto_enclave_ensure_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE)) return false; FuriString* filetype; filetype = furi_string_alloc(); @@ -226,7 +226,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -237,7 +237,7 @@ bool u2f_data_cert_key_load(uint8_t* cert_key) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } else { if(!flipper_format_read_hex(flipper_format, "Data", cert_key, 32)) { FURI_LOG_E(TAG, "Missing data"); @@ -292,7 +292,7 @@ bool u2f_data_key_load(uint8_t* device_key) { FURI_LOG_E(TAG, "Missing data"); break; } - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -302,7 +302,7 @@ bool u2f_data_key_load(uint8_t* device_key) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); state = true; } while(0); } @@ -324,7 +324,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { furi_hal_random_fill_buf(iv, 16); furi_hal_random_fill_buf(key, 32); - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -333,7 +333,7 @@ bool u2f_data_key_generate(uint8_t* device_key) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); @@ -398,7 +398,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { FURI_LOG_E(TAG, "Missing data"); break; } - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -408,7 +408,7 @@ bool u2f_data_cnt_read(uint32_t* cnt_val) { FURI_LOG_E(TAG, "Decryption failed"); break; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); if(cnt.control == U2F_COUNTER_CONTROL_VAL) { *cnt_val = cnt.counter; state = true; @@ -440,7 +440,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { cnt.control = U2F_COUNTER_CONTROL_VAL; cnt.counter = cnt_val; - if(!furi_hal_crypto_store_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { + if(!furi_hal_crypto_enclave_load_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); return false; } @@ -449,7 +449,7 @@ bool u2f_data_cnt_write(uint32_t cnt_val) { FURI_LOG_E(TAG, "Encryption failed"); return false; } - furi_hal_crypto_store_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); + furi_hal_crypto_enclave_unload_key(U2F_DATA_FILE_ENCRYPTION_KEY_SLOT_UNIQUE); Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* flipper_format = flipper_format_file_alloc(storage); diff --git a/applications/services/crypto/crypto_cli.c b/applications/services/crypto/crypto_cli.c index 511a9d2a8..bfd2003e1 100644 --- a/applications/services/crypto/crypto_cli.c +++ b/applications/services/crypto/crypto_cli.c @@ -33,7 +33,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } @@ -88,7 +88,7 @@ void crypto_cli_encrypt(Cli* cli, FuriString* args) { } while(0); if(key_loaded) { - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } } @@ -108,7 +108,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } @@ -160,7 +160,7 @@ void crypto_cli_decrypt(Cli* cli, FuriString* args) { } while(0); if(key_loaded) { - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } } @@ -175,14 +175,14 @@ void crypto_cli_has_key(Cli* cli, FuriString* args) { break; } - if(!furi_hal_crypto_store_load_key(key_slot, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot, iv)) { printf("Unable to load key from slot %d", key_slot); break; } printf("Successfully loaded key from slot %d", key_slot); - furi_hal_crypto_store_unload_key(key_slot); + furi_hal_crypto_enclave_unload_key(key_slot); } while(0); } @@ -251,25 +251,25 @@ void crypto_cli_store_key(Cli* cli, FuriString* args) { if(key_slot > 0) { uint8_t iv[16] = {0}; if(key_slot > 1) { - if(!furi_hal_crypto_store_load_key(key_slot - 1, iv)) { + if(!furi_hal_crypto_enclave_load_key(key_slot - 1, iv)) { printf( "Slot %d before %d is empty, which is not allowed", key_slot - 1, key_slot); break; } - furi_hal_crypto_store_unload_key(key_slot - 1); + furi_hal_crypto_enclave_unload_key(key_slot - 1); } - if(furi_hal_crypto_store_load_key(key_slot, iv)) { - furi_hal_crypto_store_unload_key(key_slot); + if(furi_hal_crypto_enclave_load_key(key_slot, iv)) { + furi_hal_crypto_enclave_unload_key(key_slot); printf("Key slot %d is already used", key_slot); break; } } uint8_t slot; - if(furi_hal_crypto_store_add_key(&key, &slot)) { + if(furi_hal_crypto_enclave_store_key(&key, &slot)) { printf("Success. Stored to slot: %d", slot); } else { printf("Failure"); diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 17fbe82c4..792e44e59 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,34.4,, +Version,+,35.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1035,14 +1035,20 @@ Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" +Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" +Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" Function,-,furi_hal_crypto_init,void, -Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" -Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" -Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t -Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" -Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t +Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_unload_key,_Bool, +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 2ac393ab4..12e8fd7ed 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,34.4,, +Version,+,35.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, @@ -1132,14 +1132,20 @@ Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_ctr,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_gcm,_Bool,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*, _Bool" +Function,+,furi_hal_crypto_gcm_decrypt_and_verify,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, const uint8_t*" +Function,+,furi_hal_crypto_gcm_encrypt_and_tag,FuriHalCryptoGCMState,"const uint8_t*, const uint8_t*, const uint8_t*, size_t, const uint8_t*, uint8_t*, size_t, uint8_t*" Function,-,furi_hal_crypto_init,void, -Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" -Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" -Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t -Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" -Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t +Function,+,furi_hal_crypto_load_key,_Bool,"const uint8_t*, const uint8_t*" +Function,+,furi_hal_crypto_enclave_store_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_enclave_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_enclave_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_unload_key,_Bool, +Function,+,furi_hal_crypto_enclave_verify,_Bool,"uint8_t*, uint8_t*" +Function,+,furi_hal_crypto_enclave_ensure_key,_Bool,uint8_t Function,+,furi_hal_debug_disable,void, Function,+,furi_hal_debug_enable,void, Function,+,furi_hal_debug_is_gdb_session_active,_Bool, diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index eb5c3b782..b9a0feec9 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -13,7 +14,7 @@ #define ENCLAVE_SIGNATURE_SIZE 16 #define CRYPTO_BLK_LEN (4 * sizeof(uint32_t)) -#define CRYPTO_TIMEOUT (1000) +#define CRYPTO_TIMEOUT_US (1000000) #define CRYPTO_MODE_ENCRYPT 0U #define CRYPTO_MODE_INIT (AES_CR_MODE_0) @@ -24,6 +25,19 @@ #define CRYPTO_KEYSIZE_256B (AES_CR_KEYSIZE) #define CRYPTO_AES_CBC (AES_CR_CHMOD_0) +#define CRYPTO_AES_CTR (AES_CR_CHMOD_1) +#define CRYPTO_CTR_IV_LEN (12U) +#define CRYPTO_CTR_CTR_LEN (4U) + +#define CRYPTO_AES_GCM (AES_CR_CHMOD_1 | AES_CR_CHMOD_0) +#define CRYPTO_GCM_IV_LEN (12U) +#define CRYPTO_GCM_CTR_LEN (4U) +#define CRYPTO_GCM_TAG_LEN (16U) +#define CRYPTO_GCM_PH_INIT (0x0U << AES_CR_GCMPH_Pos) +#define CRYPTO_GCM_PH_HEADER (AES_CR_GCMPH_0) +#define CRYPTO_GCM_PH_PAYLOAD (AES_CR_GCMPH_1) +#define CRYPTO_GCM_PH_FINAL (AES_CR_GCMPH_1 | AES_CR_GCMPH_0) + static FuriMutex* furi_hal_crypto_mutex = NULL; static bool furi_hal_crypto_mode_init_done = false; @@ -80,7 +94,7 @@ static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end key.size = FuriHalCryptoKeySize256; key.data = key_data; furi_hal_random_fill_buf(key_data, 32); - if(!furi_hal_crypto_store_add_key(&key, &slot)) { + if(!furi_hal_crypto_enclave_store_key(&key, &slot)) { FURI_LOG_E(TAG, "Error writing key to slot %u", slot); return false; } @@ -88,21 +102,21 @@ static bool furi_hal_crypto_generate_unique_keys(uint8_t start_slot, uint8_t end return true; } -bool furi_hal_crypto_verify_key(uint8_t key_slot) { +bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot) { uint8_t keys_nb = 0; uint8_t valid_keys_nb = 0; uint8_t last_valid_slot = ENCLAVE_FACTORY_KEY_SLOTS; uint8_t empty_iv[16] = {0}; - furi_hal_crypto_verify_enclave(&keys_nb, &valid_keys_nb); + furi_hal_crypto_enclave_verify(&keys_nb, &valid_keys_nb); if(key_slot <= ENCLAVE_FACTORY_KEY_SLOTS) { // It's a factory key if(key_slot > keys_nb) return false; } else { // Unique key if(keys_nb < ENCLAVE_FACTORY_KEY_SLOTS) // Some factory keys are missing return false; for(uint8_t i = key_slot; i > ENCLAVE_FACTORY_KEY_SLOTS; i--) { - if(furi_hal_crypto_store_load_key(i, empty_iv)) { + if(furi_hal_crypto_enclave_load_key(i, empty_iv)) { last_valid_slot = i; - furi_hal_crypto_store_unload_key(i); + furi_hal_crypto_enclave_unload_key(i); break; } } @@ -114,14 +128,14 @@ bool furi_hal_crypto_verify_key(uint8_t key_slot) { return true; } -bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { +bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb) { furi_assert(keys_nb); furi_assert(valid_keys_nb); uint8_t keys = 0; uint8_t keys_valid = 0; uint8_t buffer[ENCLAVE_SIGNATURE_SIZE]; for(size_t key_slot = 0; key_slot < ENCLAVE_FACTORY_KEY_SLOTS; key_slot++) { - if(furi_hal_crypto_store_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { + if(furi_hal_crypto_enclave_load_key(key_slot + 1, enclave_signature_iv[key_slot])) { keys++; if(furi_hal_crypto_encrypt( enclave_signature_input[key_slot], buffer, ENCLAVE_SIGNATURE_SIZE)) { @@ -129,7 +143,7 @@ bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { memcmp(buffer, enclave_signature_expected[key_slot], ENCLAVE_SIGNATURE_SIZE) == 0; } - furi_hal_crypto_store_unload_key(key_slot + 1); + furi_hal_crypto_enclave_unload_key(key_slot + 1); } } *keys_nb = keys; @@ -140,7 +154,7 @@ bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb) { return false; } -bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot) { +bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot) { furi_assert(key); furi_assert(slot); @@ -205,6 +219,16 @@ static void crypto_key_init(uint32_t* key, uint32_t* iv) { AES1->IVR0 = iv[3]; } +static bool furi_hal_crypto_wait_flag(uint32_t flag) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(CRYPTO_TIMEOUT_US); + while(!READ_BIT(AES1->SR, flag)) { + if(furi_hal_cortex_timer_is_expired(timer)) { + return false; + } + } + return true; +} + static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { furi_check((blk_len <= 4) && (blk_len > 0)); @@ -216,14 +240,8 @@ static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { } } - uint32_t countdown = CRYPTO_TIMEOUT; - while(!READ_BIT(AES1->SR, AES_SR_CCF)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; } SET_BIT(AES1->CR, AES_CR_CCFC); @@ -237,7 +255,7 @@ static bool crypto_process_block(uint32_t* in, uint32_t* out, uint8_t blk_len) { return true; } -bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { +bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv) { furi_assert(slot > 0 && slot <= 100); furi_assert(furi_hal_crypto_mutex); furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); @@ -260,7 +278,7 @@ bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv) { } } -bool furi_hal_crypto_store_unload_key(uint8_t slot) { +bool furi_hal_crypto_enclave_unload_key(uint8_t slot) { if(!furi_hal_bt_is_alive()) { return false; } @@ -276,6 +294,27 @@ bool furi_hal_crypto_store_unload_key(uint8_t slot) { return (shci_state == SHCI_Success); } +bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv) { + furi_assert(furi_hal_crypto_mutex); + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + furi_hal_bus_enable(FuriHalBusAES1); + + furi_hal_crypto_mode_init_done = false; + crypto_key_init((uint32_t*)key, (uint32_t*)iv); + + return true; +} + +bool furi_hal_crypto_unload_key(void) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + + furi_hal_bus_disable(FuriHalBusAES1); + + furi_check(furi_mutex_release(furi_hal_crypto_mutex) == FuriStatusOk); + return true; +} + bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) { bool state = false; @@ -307,14 +346,8 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) SET_BIT(AES1->CR, AES_CR_EN); - uint32_t countdown = CRYPTO_TIMEOUT; - while(!READ_BIT(AES1->SR, AES_SR_CCF)) { - if(LL_SYSTICK_IsActiveCounterFlag()) { - countdown--; - } - if(countdown == 0) { - return false; - } + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; } SET_BIT(AES1->CR, AES_CR_CCFC); @@ -340,3 +373,360 @@ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size) return state; } + +static void crypto_key_init_bswap(uint32_t* key, uint32_t* iv, uint32_t chaining_mode) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + MODIFY_REG( + AES1->CR, + AES_CR_DATATYPE | AES_CR_KEYSIZE | AES_CR_CHMOD, + CRYPTO_DATATYPE_32B | CRYPTO_KEYSIZE_256B | chaining_mode); + + if(key != NULL) { + AES1->KEYR7 = __builtin_bswap32(key[0]); + AES1->KEYR6 = __builtin_bswap32(key[1]); + AES1->KEYR5 = __builtin_bswap32(key[2]); + AES1->KEYR4 = __builtin_bswap32(key[3]); + AES1->KEYR3 = __builtin_bswap32(key[4]); + AES1->KEYR2 = __builtin_bswap32(key[5]); + AES1->KEYR1 = __builtin_bswap32(key[6]); + AES1->KEYR0 = __builtin_bswap32(key[7]); + } + + AES1->IVR3 = __builtin_bswap32(iv[0]); + AES1->IVR2 = __builtin_bswap32(iv[1]); + AES1->IVR1 = __builtin_bswap32(iv[2]); + AES1->IVR0 = __builtin_bswap32(iv[3]); +} + +static bool + furi_hal_crypto_load_key_bswap(const uint8_t* key, const uint8_t* iv, uint32_t chaining_mode) { + furi_assert(furi_hal_crypto_mutex); + furi_check(furi_mutex_acquire(furi_hal_crypto_mutex, FuriWaitForever) == FuriStatusOk); + + furi_hal_bus_enable(FuriHalBusAES1); + + crypto_key_init_bswap((uint32_t*)key, (uint32_t*)iv, chaining_mode); + + return true; +} + +static bool wait_for_crypto(void) { + if(!furi_hal_crypto_wait_flag(AES_SR_CCF)) { + return false; + } + + SET_BIT(AES1->CR, AES_CR_CCFC); + + return true; +} + +static bool furi_hal_crypto_process_block_bswap(const uint8_t* in, uint8_t* out, size_t bytes) { + uint32_t block[CRYPTO_BLK_LEN / 4]; + memset(block, 0, sizeof(block)); + + memcpy(block, in, bytes); + + block[0] = __builtin_bswap32(block[0]); + block[1] = __builtin_bswap32(block[1]); + block[2] = __builtin_bswap32(block[2]); + block[3] = __builtin_bswap32(block[3]); + + if(!crypto_process_block(block, block, CRYPTO_BLK_LEN / 4)) { + return false; + } + + block[0] = __builtin_bswap32(block[0]); + block[1] = __builtin_bswap32(block[1]); + block[2] = __builtin_bswap32(block[2]); + block[3] = __builtin_bswap32(block[3]); + + memcpy(out, block, bytes); + + return true; +} + +static bool furi_hal_crypto_process_block_no_read_bswap(const uint8_t* in, size_t bytes) { + uint32_t block[CRYPTO_BLK_LEN / 4]; + memset(block, 0, sizeof(block)); + + memcpy(block, in, bytes); + + AES1->DINR = __builtin_bswap32(block[0]); + AES1->DINR = __builtin_bswap32(block[1]); + AES1->DINR = __builtin_bswap32(block[2]); + AES1->DINR = __builtin_bswap32(block[3]); + + return wait_for_crypto(); +} + +static void furi_hal_crypto_ctr_prep_iv(uint8_t* iv) { + /* append counter to IV */ + iv[CRYPTO_CTR_IV_LEN] = 0; + iv[CRYPTO_CTR_IV_LEN + 1] = 0; + iv[CRYPTO_CTR_IV_LEN + 2] = 0; + iv[CRYPTO_CTR_IV_LEN + 3] = 1; +} + +static bool furi_hal_crypto_ctr_payload(const uint8_t* input, uint8_t* output, size_t length) { + SET_BIT(AES1->CR, AES_CR_EN); + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); + + size_t last_block_bytes = length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + CLEAR_BIT(AES1->CR, AES_CR_EN); + return true; +} + +bool furi_hal_crypto_ctr( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* input, + uint8_t* output, + size_t length) { + /* prepare IV and counter */ + uint8_t iv_and_counter[CRYPTO_CTR_IV_LEN + CRYPTO_CTR_CTR_LEN]; + memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); + furi_hal_crypto_ctr_prep_iv(iv_and_counter); + + /* load key and IV and set the mode to CTR */ + if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_CTR)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* process the input and write to output */ + bool state = furi_hal_crypto_ctr_payload(input, output, length); + + furi_hal_crypto_unload_key(); + + return state; +} + +static void furi_hal_crypto_gcm_prep_iv(uint8_t* iv) { + /* append counter to IV */ + iv[CRYPTO_GCM_IV_LEN] = 0; + iv[CRYPTO_GCM_IV_LEN + 1] = 0; + iv[CRYPTO_GCM_IV_LEN + 2] = 0; + iv[CRYPTO_GCM_IV_LEN + 3] = 2; +} + +static bool furi_hal_crypto_gcm_init(bool decrypt) { + /* GCM init phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_INIT); + if(decrypt) { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_DECRYPT); + } else { + MODIFY_REG(AES1->CR, AES_CR_MODE, CRYPTO_MODE_ENCRYPT); + } + + SET_BIT(AES1->CR, AES_CR_EN); + + if(!wait_for_crypto()) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + + return true; +} + +static bool furi_hal_crypto_gcm_header(const uint8_t* aad, size_t aad_length) { + /* GCM header phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_HEADER); + SET_BIT(AES1->CR, AES_CR_EN); + + size_t last_block_bytes = aad_length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < aad_length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!furi_hal_crypto_process_block_no_read_bswap(&aad[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + return true; +} + +static bool furi_hal_crypto_gcm_payload( + const uint8_t* input, + uint8_t* output, + size_t length, + bool decrypt) { + /* GCM payload phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_PAYLOAD); + SET_BIT(AES1->CR, AES_CR_EN); + + size_t last_block_bytes = length % CRYPTO_BLK_LEN; + + size_t i; + for(i = 0; i < length - last_block_bytes; i += CRYPTO_BLK_LEN) { + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + if(last_block_bytes > 0) { + if(!decrypt) { + MODIFY_REG( + AES1->CR, AES_CR_NPBLB, (CRYPTO_BLK_LEN - last_block_bytes) << AES_CR_NPBLB_Pos); + } + if(!furi_hal_crypto_process_block_bswap(&input[i], &output[i], last_block_bytes)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + } + + return true; +} + +static bool furi_hal_crypto_gcm_finish(size_t aad_length, size_t payload_length, uint8_t* tag) { + /* GCM final phase */ + + MODIFY_REG(AES1->CR, AES_CR_GCMPH, CRYPTO_GCM_PH_FINAL); + + uint32_t last_block[CRYPTO_BLK_LEN / 4]; + memset(last_block, 0, sizeof(last_block)); + last_block[1] = __builtin_bswap32((uint32_t)(aad_length * 8)); + last_block[3] = __builtin_bswap32((uint32_t)(payload_length * 8)); + + if(!furi_hal_crypto_process_block_bswap((uint8_t*)&last_block[0], tag, CRYPTO_BLK_LEN)) { + CLEAR_BIT(AES1->CR, AES_CR_EN); + return false; + } + + return true; +} + +static bool furi_hal_crypto_gcm_compare_tag(const uint8_t* tag1, const uint8_t* tag2) { + uint8_t diff = 0; + + size_t i; + for(i = 0; i < CRYPTO_GCM_TAG_LEN; i++) { + diff |= tag1[i] ^ tag2[i]; + } + + return (diff == 0); +} + +bool furi_hal_crypto_gcm( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag, + bool decrypt) { + /* GCM init phase */ + + /* prepare IV and counter */ + uint8_t iv_and_counter[CRYPTO_GCM_IV_LEN + CRYPTO_GCM_CTR_LEN]; + memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); + furi_hal_crypto_gcm_prep_iv(iv_and_counter); + + /* load key and IV and set the mode to CTR */ + if(!furi_hal_crypto_load_key_bswap(key, iv_and_counter, CRYPTO_AES_GCM)) { + furi_hal_crypto_unload_key(); + return false; + } + + if(!furi_hal_crypto_gcm_init(decrypt)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* GCM header phase */ + + if(aad_length > 0) { + if(!furi_hal_crypto_gcm_header(aad, aad_length)) { + furi_hal_crypto_unload_key(); + return false; + } + } + + /* GCM payload phase */ + + if(!furi_hal_crypto_gcm_payload(input, output, length, decrypt)) { + furi_hal_crypto_unload_key(); + return false; + } + + /* GCM final phase */ + + if(!furi_hal_crypto_gcm_finish(aad_length, length, tag)) { + furi_hal_crypto_unload_key(); + return false; + } + + furi_hal_crypto_unload_key(); + return true; +} + +FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag) { + if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, tag, false)) { + memset(output, 0, length); + memset(tag, 0, CRYPTO_GCM_TAG_LEN); + return FuriHalCryptoGCMStateError; + } + + return FuriHalCryptoGCMStateOk; +} + +FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + const uint8_t* tag) { + uint8_t dtag[CRYPTO_GCM_TAG_LEN]; + + if(!furi_hal_crypto_gcm(key, iv, aad, aad_length, input, output, length, dtag, true)) { + memset(output, 0, length); + return FuriHalCryptoGCMStateError; + } + + if(!furi_hal_crypto_gcm_compare_tag(dtag, tag)) { + memset(output, 0, length); + return FuriHalCryptoGCMStateAuthFailure; + } + + return FuriHalCryptoGCMStateOk; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_info.c b/firmware/targets/f7/furi_hal/furi_hal_info.c index cefb6a11b..4908cca69 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_info.c +++ b/firmware/targets/f7/furi_hal/furi_hal_info.c @@ -283,7 +283,7 @@ void furi_hal_info_get(PropertyValueCallback out, char sep, void* context) { // Signature verification uint8_t enclave_keys = 0; uint8_t enclave_valid_keys = 0; - bool enclave_valid = furi_hal_crypto_verify_enclave(&enclave_keys, &enclave_valid_keys); + bool enclave_valid = furi_hal_crypto_enclave_verify(&enclave_keys, &enclave_valid_keys); if(sep == '.') { property_value_out( &property_context, "%d", 3, "enclave", "keys", "valid", enclave_valid_keys); diff --git a/firmware/targets/furi_hal_include/furi_hal_crypto.h b/firmware/targets/furi_hal_include/furi_hal_crypto.h index 81788e96e..5e3b41040 100644 --- a/firmware/targets/furi_hal_include/furi_hal_crypto.h +++ b/firmware/targets/furi_hal_include/furi_hal_crypto.h @@ -1,6 +1,40 @@ /** * @file furi_hal_crypto.h + * * Cryptography HAL API + * + * !!! READ THIS FIRST !!! + * + * Flipper was never designed to be secure, nor it passed cryptography audit. + * Despite of the fact that keys are stored in secure enclave there are some + * types of attack that can be performed against AES engine to recover + * keys(theoretical). Also there is no way to securely deliver user keys to + * device and never will be. In addition device is fully open and there is no + * way to guarantee safety of your data, it can be easily dumped with debugger + * or modified code. + * + * Secure enclave on WB series is implemented on core2 FUS side and can be used + * only if core2 alive. Enclave is responsible for storing, loading and + * unloading keys to and from enclave/AES in secure manner(AES engine key + * registers will be locked when key from enclave loaded) + * + * There are 11 keys that we provision at factory: + * - 0 - Master key for secure key delivery. Impossible to use for anything but + * key provisioning. We don't plan to use it too. + * - 1 - 10 - Keys used by firmware. All devices got the same set of keys. You + * also can use them in your applications. + * + * Also there is a slot 11 that we use for device unique key. This slot is + * intentionally left blank till the moment of first use, so you can ensure that + * we don't know your unique key. Also you can provision this key by your self + * with crypto cli or API. + * + * Other slots can be used for your needs. But since enclave is sequential + * append only, we can not guarantee you that slots you want are free. NEVER USE + * THEM FOR PUBLIC APPLICATIONS. + * + * Also you can directly load raw keys into AES engine and use it for your + * needs. */ #pragma once @@ -12,10 +46,27 @@ extern "C" { #endif +/** Factory provisioned master key slot. Should never be used. */ +#define FURI_HAL_CRYPTO_ENCLAVE_MASTER_KEY_SLOT (0u) + +/** Factory provisioned keys slot range. All of them are exactly same on all flippers. */ +#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_START (1u) +#define FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END (10u) + +/** Device unique key slot. This key generated on first use or provisioned by user. Use furi_hal_crypto_enclave_ensure_key before using this slot. */ +#define FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT (11u) + +/** User key slot range. This slots can be used for your needs, but never use them in public apps. */ +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START (12u) +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END (100u) + +/** [Deprecated] Indicates availability of advanced crypto functions, will be dropped before v1.0 */ +#define FURI_HAL_CRYPTO_ADVANCED_AVAIL 1 + /** FuriHalCryptoKey Type */ typedef enum { FuriHalCryptoKeyTypeMaster, /**< Master key */ - FuriHalCryptoKeyTypeSimple, /**< Simple enencrypted key */ + FuriHalCryptoKeyTypeSimple, /**< Simple unencrypted key */ FuriHalCryptoKeyTypeEncrypted, /**< Encrypted with Master key */ } FuriHalCryptoKeyType; @@ -32,40 +83,89 @@ typedef struct { uint8_t* data; } FuriHalCryptoKey; -/** Initialize cryptography layer This includes AES engines, PKA and RNG - */ +/** FuriHalCryptoGCMState Result of a GCM operation */ +typedef enum { + FuriHalCryptoGCMStateOk, /**< operation successful */ + FuriHalCryptoGCMStateError, /**< error during encryption/decryption */ + FuriHalCryptoGCMStateAuthFailure, /**< tags do not match, auth failed */ +} FuriHalCryptoGCMState; + +/** Initialize cryptography layer(includes AES engines, PKA and RNG) */ void furi_hal_crypto_init(); -bool furi_hal_crypto_verify_enclave(uint8_t* keys_nb, uint8_t* valid_keys_nb); - -bool furi_hal_crypto_verify_key(uint8_t key_slot); - -/** Store key in crypto storage +/** Verify factory provisioned keys * - * @param key FuriHalCryptoKey to store. Only Master, Simple or - * Encrypted - * @param slot pinter to int where store slot number will be saved + * @param keys_nb The keys number of + * @param valid_keys_nb The valid keys number of + * + * @return true if all enclave keys are intact, false otherwise + */ +bool furi_hal_crypto_enclave_verify(uint8_t* keys_nb, uint8_t* valid_keys_nb); + +/** Ensure that requested slot and slots before this slot contains keys. + * + * This function is used to provision FURI_HAL_CRYPTO_ENCLAVE_UNIQUE_KEY_SLOT. Also you + * may want to use it to generate some unique keys in user key slot range. + * + * @warning Because of the sequential nature of the secure enclave this + * method will generate key for all slots from + * FURI_HAL_CRYPTO_ENCLAVE_FACTORY_KEY_SLOT_END to the slot your requested. + * Keys are generated using on-chip RNG. + * + * @param[in] key_slot The key slot to enclave + * + * @return true if key exists or created, false if enclave corrupted + */ +bool furi_hal_crypto_enclave_ensure_key(uint8_t key_slot); + +/** Store key in crypto enclave + * + * @param key FuriHalCryptoKey to be stored + * @param slot pointer to int where enclave slot will be stored * * @return true on success */ -bool furi_hal_crypto_store_add_key(FuriHalCryptoKey* key, uint8_t* slot); +bool furi_hal_crypto_enclave_store_key(FuriHalCryptoKey* key, uint8_t* slot); -/** Init AES engine and load key from crypto store +/** Init AES engine and load key from crypto enclave * - * @param slot store slot number + * @warning Use only with furi_hal_crypto_enclave_unload_key() + * + * @param slot enclave slot * @param[in] iv pointer to 16 bytes Initialization Vector data * * @return true on success */ -bool furi_hal_crypto_store_load_key(uint8_t slot, const uint8_t* iv); +bool furi_hal_crypto_enclave_load_key(uint8_t slot, const uint8_t* iv); -/** Unload key engine and deinit AES engine +/** Unload key and deinit AES engine * - * @param slot store slot number + * @warning Use only with furi_hal_crypto_enclave_load_key() + * + * @param slot enclave slot * * @return true on success */ -bool furi_hal_crypto_store_unload_key(uint8_t slot); +bool furi_hal_crypto_enclave_unload_key(uint8_t slot); + +/** Init AES engine and load supplied key + * + * @warning Use only with furi_hal_crypto_unload_key() + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 16 bytes Initialization Vector data + * + * @return true on success + */ +bool furi_hal_crypto_load_key(const uint8_t* key, const uint8_t* iv); + +/** Unload key and de-init AES engine + * + * @warning Use this function only with furi_hal_crypto_load_key() + * + * @return true on success + */ +bool furi_hal_crypto_unload_key(void); /** Encrypt data * @@ -87,6 +187,109 @@ bool furi_hal_crypto_encrypt(const uint8_t* input, uint8_t* output, size_t size) */ bool furi_hal_crypto_decrypt(const uint8_t* input, uint8_t* output, size_t size); +/** Encrypt the input using AES-CTR + * + * Decryption can be performed by supplying the ciphertext as input. Inits and + * deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * + * @return true on success + */ +bool furi_hal_crypto_ctr( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* input, + uint8_t* output, + size_t length); + +/** Encrypt/decrypt the input using AES-GCM + * + * When decrypting the tag generated needs to be compared to the tag attached to + * the ciphertext in a constant-time fashion. If the tags are not equal, the + * decryption failed and the plaintext returned needs to be discarded. Inits and + * deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes space for the tag + * @param decrypt true for decryption, false otherwise + * + * @return true on success + */ +bool furi_hal_crypto_gcm( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag, + bool decrypt); + +/** Encrypt the input using AES-GCM and generate a tag + * + * Inits and deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes space for the tag + * + * @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on + * failure + */ +FuriHalCryptoGCMState furi_hal_crypto_gcm_encrypt_and_tag( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + uint8_t* tag); + +/** Decrypt the input using AES-GCM and verify the provided tag + * + * Inits and deinits the AES engine internally. + * + * @param[in] key pointer to 32 bytes key data + * @param[in] iv pointer to 12 bytes Initialization Vector data + * @param[in] aad pointer to additional authentication data + * @param aad_length length of the additional authentication data in bytes + * @param[in] input pointer to input data + * @param[out] output pointer to output data + * @param length length of the input and output in bytes + * @param[out] tag pointer to 16 bytes tag + * + * @return FuriHalCryptoGCMStateOk on success, FuriHalCryptoGCMStateError on + * failure, FuriHalCryptoGCMStateAuthFailure if the tag does not + * match + */ +FuriHalCryptoGCMState furi_hal_crypto_gcm_decrypt_and_verify( + const uint8_t* key, + const uint8_t* iv, + const uint8_t* aad, + size_t aad_length, + const uint8_t* input, + uint8_t* output, + size_t length, + const uint8_t* tag); + #ifdef __cplusplus } #endif diff --git a/lib/subghz/subghz_keystore.c b/lib/subghz/subghz_keystore.c index 54ed15a99..636288aa6 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -122,7 +122,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, do { if(iv) { - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load decryption key"); break; } @@ -181,7 +181,7 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, } } while(ret > 0 && result); - if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + if(iv) furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); } while(false); free(encrypted_line); @@ -280,7 +280,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 subghz_keystore_mess_with_iv(iv); - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -326,7 +326,7 @@ bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8 stream_write_char(stream, '\n'); encrypted_line_count++; } - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); size_t total_keys = SubGhzKeyArray_size(instance->data); result = encrypted_line_count == total_keys; if(result) { @@ -421,7 +421,7 @@ bool subghz_keystore_raw_encrypted_save( subghz_keystore_mess_with_iv(iv); - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -474,7 +474,7 @@ bool subghz_keystore_raw_encrypted_save( flipper_format_free(output_flipper_format); - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); if(!result) break; @@ -576,7 +576,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* } } - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + if(!furi_hal_crypto_enclave_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { FURI_LOG_E(TAG, "Unable to load encryption key"); break; } @@ -604,7 +604,7 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len); } while(0); - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); if(decrypted) result = true; } while(0); flipper_format_free(flipper_format); From dc2d12d468850c6a713294876ae96578478f681d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 10 Aug 2023 18:45:17 +0900 Subject: [PATCH 23/99] Scripts: OB recovery (#2964) * Scripts: OB recovery * Scripts: slightly different ob * Scripts: remove excessive return * Scripts: simplifying work with registers * Make PVS happy Co-authored-by: SG --- .../targets/f7/furi_hal/furi_hal_crypto.c | 4 +- scripts/flipper/utils/programmer.py | 4 + scripts/flipper/utils/programmer_openocd.py | 55 +++++--- scripts/flipper/utils/register.py | 17 ++- scripts/flipper/utils/stm32wb55.py | 133 ++++++++++-------- scripts/ob.py | 20 +++ 6 files changed, 148 insertions(+), 85 deletions(-) diff --git a/firmware/targets/f7/furi_hal/furi_hal_crypto.c b/firmware/targets/f7/furi_hal/furi_hal_crypto.c index b9a0feec9..a897648a3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_crypto.c +++ b/firmware/targets/f7/furi_hal/furi_hal_crypto.c @@ -500,7 +500,7 @@ bool furi_hal_crypto_ctr( size_t length) { /* prepare IV and counter */ uint8_t iv_and_counter[CRYPTO_CTR_IV_LEN + CRYPTO_CTR_CTR_LEN]; - memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); + memcpy(iv_and_counter, iv, CRYPTO_CTR_IV_LEN); //-V1086 furi_hal_crypto_ctr_prep_iv(iv_and_counter); /* load key and IV and set the mode to CTR */ @@ -648,7 +648,7 @@ bool furi_hal_crypto_gcm( /* prepare IV and counter */ uint8_t iv_and_counter[CRYPTO_GCM_IV_LEN + CRYPTO_GCM_CTR_LEN]; - memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); + memcpy(iv_and_counter, iv, CRYPTO_GCM_IV_LEN); //-V1086 furi_hal_crypto_gcm_prep_iv(iv_and_counter); /* load key and IV and set the mode to CTR */ diff --git a/scripts/flipper/utils/programmer.py b/scripts/flipper/utils/programmer.py index 84452d154..938065f7c 100644 --- a/scripts/flipper/utils/programmer.py +++ b/scripts/flipper/utils/programmer.py @@ -26,6 +26,10 @@ class Programmer(ABC): def option_bytes_set(self, file_path: str) -> bool: pass + @abstractmethod + def option_bytes_recover(self) -> bool: + pass + @abstractmethod def otp_write(self, address: int, file_path: str) -> bool: pass diff --git a/scripts/flipper/utils/programmer_openocd.py b/scripts/flipper/utils/programmer_openocd.py index 5a8029f37..77335ee5b 100644 --- a/scripts/flipper/utils/programmer_openocd.py +++ b/scripts/flipper/utils/programmer_openocd.py @@ -44,11 +44,11 @@ class OpenOCDProgrammer(Programmer): self.logger = logging.getLogger() def reset(self, mode: Programmer.RunMode = Programmer.RunMode.Run) -> bool: - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) if mode == Programmer.RunMode.Run: - stm32.reset(self.openocd, stm32.RunMode.Run) + stm32.reset(stm32.RunMode.Run) elif mode == Programmer.RunMode.Stop: - stm32.reset(self.openocd, stm32.RunMode.Init) + stm32.reset(stm32.RunMode.Init) else: raise Exception("Unknown mode") @@ -96,11 +96,11 @@ class OpenOCDProgrammer(Programmer): def option_bytes_validate(self, file_path: str) -> bool: # Registers - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) # OpenOCD self.openocd.start() - stm32.reset(self.openocd, stm32.RunMode.Init) + stm32.reset(stm32.RunMode.Init) # Generate Option Bytes data ob_data = OptionBytesData(file_path) @@ -133,7 +133,7 @@ class OpenOCDProgrammer(Programmer): self._ob_print_diff_table(ob_reference, ob_compare, self.logger.error) # Stop OpenOCD - stm32.reset(self.openocd, stm32.RunMode.Run) + stm32.reset(stm32.RunMode.Run) self.openocd.stop() return return_code @@ -143,11 +143,11 @@ class OpenOCDProgrammer(Programmer): def option_bytes_set(self, file_path: str) -> bool: # Registers - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) # OpenOCD self.openocd.start() - stm32.reset(self.openocd, stm32.RunMode.Init) + stm32.reset(stm32.RunMode.Init) # Generate Option Bytes data ob_data = OptionBytesData(file_path) @@ -159,11 +159,11 @@ class OpenOCDProgrammer(Programmer): ob_dwords = int(ob_length / 8) # Clear flash errors - stm32.clear_flash_errors(self.openocd) + stm32.clear_flash_errors() # Unlock Flash and Option Bytes - stm32.flash_unlock(self.openocd) - stm32.option_bytes_unlock(self.openocd) + stm32.flash_unlock() + stm32.option_bytes_unlock() ob_need_to_apply = False @@ -194,16 +194,16 @@ class OpenOCDProgrammer(Programmer): self.openocd.write_32(device_reg_addr, ob_value) if ob_need_to_apply: - stm32.option_bytes_apply(self.openocd) + stm32.option_bytes_apply() else: self.logger.info("Option Bytes are already correct") # Load Option Bytes # That will reset and also lock the Option Bytes and the Flash - stm32.option_bytes_load(self.openocd) + stm32.option_bytes_load() # Stop OpenOCD - stm32.reset(self.openocd, stm32.RunMode.Run) + stm32.reset(stm32.RunMode.Run) self.openocd.stop() return True @@ -233,11 +233,10 @@ class OpenOCDProgrammer(Programmer): self.logger.debug(f"Data: {data.hex().upper()}") # Start OpenOCD - oocd = self.openocd - oocd.start() + self.openocd.start() # Registers - stm32 = STM32WB55() + stm32 = STM32WB55(self.openocd) try: # Check that OTP is empty for the given address @@ -245,7 +244,7 @@ class OpenOCDProgrammer(Programmer): already_written = True for i in range(0, data_size, 4): file_word = int.from_bytes(data[i : i + 4], "little") - device_word = oocd.read_32(address + i) + device_word = self.openocd.read_32(address + i) if device_word != 0xFFFFFFFF and device_word != file_word: self.logger.error( f"OTP memory at {address + i:08X} is not empty: {device_word:08X}" @@ -260,7 +259,7 @@ class OpenOCDProgrammer(Programmer): return OpenOCDProgrammerResult.Success self.reset(self.RunMode.Stop) - stm32.clear_flash_errors(oocd) + stm32.clear_flash_errors() # Write OTP memory by 8 bytes for i in range(0, data_size, 8): @@ -269,14 +268,14 @@ class OpenOCDProgrammer(Programmer): self.logger.debug( f"Writing {word_1:08X} {word_2:08X} to {address + i:08X}" ) - stm32.write_flash_64(oocd, address + i, word_1, word_2) + stm32.write_flash_64(address + i, word_1, word_2) # Validate OTP memory validation_result = True for i in range(0, data_size, 4): file_word = int.from_bytes(data[i : i + 4], "little") - device_word = oocd.read_32(address + i) + device_word = self.openocd.read_32(address + i) if file_word != device_word: self.logger.error( f"Validation failed: {file_word:08X} != {device_word:08X} at {address + i:08X}" @@ -284,11 +283,21 @@ class OpenOCDProgrammer(Programmer): validation_result = False finally: # Stop OpenOCD - stm32.reset(oocd, stm32.RunMode.Run) - oocd.stop() + stm32.reset(stm32.RunMode.Run) + self.openocd.stop() return ( OpenOCDProgrammerResult.Success if validation_result else OpenOCDProgrammerResult.ErrorValidation ) + + def option_bytes_recover(self) -> bool: + try: + self.openocd.start() + stm32 = STM32WB55(self.openocd) + stm32.reset(stm32.RunMode.Halt) + stm32.option_bytes_recover() + return True + finally: + self.openocd.stop() diff --git a/scripts/flipper/utils/register.py b/scripts/flipper/utils/register.py index 26d66730c..aad75eaca 100644 --- a/scripts/flipper/utils/register.py +++ b/scripts/flipper/utils/register.py @@ -16,6 +16,7 @@ class Register32: self.names = [definition.name for definition in definition_list] # typecheck self.address = address self.definition_list = definition_list + self.openocd = None # Validate that the definitions are not overlapping for i in range(len(definition_list)): @@ -76,6 +77,14 @@ class Register32: def __dir__(self): return self.names + def set_openocd(self, openocd: OpenOCD): + self.openocd = openocd + + def get_openocd(self) -> OpenOCD: + if self.openocd is None: + raise RuntimeError("OpenOCD is not installed") + return self.openocd + def set(self, value: int): for definition in self.definition_list: definition.value = (value >> definition.offset) & ( @@ -88,8 +97,8 @@ class Register32: value |= definition.value << definition.offset return value - def load(self, openocd: OpenOCD): - self.set(openocd.read_32(self.address)) + def load(self): + self.set(self.get_openocd().read_32(self.address)) - def store(self, openocd: OpenOCD): - openocd.write_32(self.address, self.get()) + def store(self): + self.get_openocd().write_32(self.address, self.get()) diff --git a/scripts/flipper/utils/stm32wb55.py b/scripts/flipper/utils/stm32wb55.py index 52a5ec4e3..4a47b8bea 100644 --- a/scripts/flipper/utils/stm32wb55.py +++ b/scripts/flipper/utils/stm32wb55.py @@ -108,23 +108,27 @@ class STM32WB55: 15: None, # Core 2 Options } - def __init__(self): + def __init__(self, openocd: OpenOCD): + self.openocd = openocd self.logger = logging.getLogger("STM32WB55") + self.FLASH_CR.set_openocd(self.openocd) + self.FLASH_SR.set_openocd(self.openocd) + class RunMode(Enum): Init = "init" Run = "run" Halt = "halt" - def reset(self, oocd: OpenOCD, mode: RunMode): + def reset(self, mode: RunMode): self.logger.debug("Resetting device") - oocd.send_tcl(f"reset {mode.value}") + self.openocd.send_tcl(f"reset {mode.value}") - def clear_flash_errors(self, oocd: OpenOCD): + def clear_flash_errors(self): # Errata 2.2.9: Flash OPTVERR flag is always set after system reset # And also clear all other flash error flags self.logger.debug("Resetting flash errors") - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() self.FLASH_SR.OP_ERR = 1 self.FLASH_SR.PROG_ERR = 1 self.FLASH_SR.WRP_ERR = 1 @@ -135,51 +139,51 @@ class STM32WB55: self.FLASH_SR.FAST_ERR = 1 self.FLASH_SR.RD_ERR = 1 self.FLASH_SR.OPTV_ERR = 1 - self.FLASH_SR.store(oocd) + self.FLASH_SR.store() - def flash_unlock(self, oocd: OpenOCD): + def flash_unlock(self): # Check if flash is already unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 0: self.logger.debug("Flash is already unlocked") return # Unlock flash self.logger.debug("Unlocking Flash") - oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1) - oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2) + self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1) + self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2) # Check if flash is unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 0: self.logger.debug("Flash unlocked") else: self.logger.error("Flash unlock failed") raise Exception("Flash unlock failed") - def option_bytes_unlock(self, oocd: OpenOCD): + def option_bytes_unlock(self): # Check if options is already unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 0: self.logger.debug("Options is already unlocked") return # Unlock options self.logger.debug("Unlocking Options") - oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1) - oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2) + self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1) + self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2) # Check if options is unlocked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 0: self.logger.debug("Options unlocked") else: self.logger.error("Options unlock failed") raise Exception("Options unlock failed") - def option_bytes_lock(self, oocd: OpenOCD): + def option_bytes_lock(self): # Check if options is already locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 1: self.logger.debug("Options is already locked") return @@ -187,19 +191,19 @@ class STM32WB55: # Lock options self.logger.debug("Locking Options") self.FLASH_CR.OPT_LOCK = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Check if options is locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.OPT_LOCK == 1: self.logger.debug("Options locked") else: self.logger.error("Options lock failed") raise Exception("Options lock failed") - def flash_lock(self, oocd: OpenOCD): + def flash_lock(self): # Check if flash is already locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 1: self.logger.debug("Flash is already locked") return @@ -207,31 +211,31 @@ class STM32WB55: # Lock flash self.logger.debug("Locking Flash") self.FLASH_CR.LOCK = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Check if flash is locked - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() if self.FLASH_CR.LOCK == 1: self.logger.debug("Flash locked") else: self.logger.error("Flash lock failed") raise Exception("Flash lock failed") - def option_bytes_apply(self, oocd: OpenOCD): + def option_bytes_apply(self): self.logger.debug("Applying Option Bytes") - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.OPT_STRT = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Wait for Option Bytes to be applied - self.flash_wait_for_operation(oocd) + self.flash_wait_for_operation() - def option_bytes_load(self, oocd: OpenOCD): + def option_bytes_load(self): self.logger.debug("Loading Option Bytes") - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.OBL_LAUNCH = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() def option_bytes_id_to_address(self, id: int) -> int: # Check if this option byte (dword) is mapped to a register @@ -241,16 +245,16 @@ class STM32WB55: return device_reg_addr - def flash_wait_for_operation(self, oocd: OpenOCD): + def flash_wait_for_operation(self): # Wait for flash operation to complete # TODO: timeout while True: - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() if self.FLASH_SR.BSY == 0: break - def flash_dump_status_register(self, oocd: OpenOCD): - self.FLASH_SR.load(oocd) + def flash_dump_status_register(self): + self.FLASH_SR.load() self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}") if self.FLASH_SR.EOP: self.logger.info(" End of operation") @@ -283,70 +287,87 @@ class STM32WB55: if self.FLASH_SR.PESD: self.logger.info(" Programming / erase operation suspended.") - def write_flash_64(self, oocd: OpenOCD, address: int, word_1: int, word_2: int): + def write_flash_64(self, address: int, word_1: int, word_2: int): self.logger.debug(f"Writing flash at address {address:08x}") if address % 8 != 0: self.logger.error("Address must be aligned to 8 bytes") raise Exception("Address must be aligned to 8 bytes") - if word_1 == oocd.read_32(address) and word_2 == oocd.read_32(address + 4): + if word_1 == self.openocd.read_32(address) and word_2 == self.openocd.read_32( + address + 4 + ): self.logger.debug("Data is already programmed") return - self.flash_unlock(oocd) + self.flash_unlock() # Check that no flash main memory operation is ongoing by checking the BSY bit - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() if self.FLASH_SR.BSY: self.logger.error("Flash is busy") - self.flash_dump_status_register(oocd) + self.flash_dump_status_register() raise Exception("Flash is busy") # Enable end of operation interrupts and error interrupts - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.EOPIE = 1 self.FLASH_CR.ERRIE = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Check that flash memory program and erase operations are allowed if self.FLASH_SR.PESD: self.logger.error("Flash operations are not allowed") - self.flash_dump_status_register(oocd) + self.flash_dump_status_register() raise Exception("Flash operations are not allowed") # Check and clear all error programming flags due to a previous programming. - self.clear_flash_errors(oocd) + self.clear_flash_errors() # Set the PG bit in the Flash memory control register (FLASH_CR) - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.PG = 1 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() # Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed. # Write the first word - oocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}") + self.openocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}") # Write the second word - oocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}") + self.openocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}") # Wait for the BSY bit to be cleared - self.flash_wait_for_operation(oocd) + self.flash_wait_for_operation() # Check that EOP flag is set in the FLASH_SR register - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() if not self.FLASH_SR.EOP: self.logger.error("Flash operation failed") - self.flash_dump_status_register(oocd) + self.flash_dump_status_register() raise Exception("Flash operation failed") # Clear the EOP flag - self.FLASH_SR.load(oocd) + self.FLASH_SR.load() self.FLASH_SR.EOP = 1 - self.FLASH_SR.store(oocd) + self.FLASH_SR.store() # Clear the PG bit in the FLASH_CR register - self.FLASH_CR.load(oocd) + self.FLASH_CR.load() self.FLASH_CR.PG = 0 - self.FLASH_CR.store(oocd) + self.FLASH_CR.store() - self.flash_lock(oocd) + self.flash_lock() + + def option_bytes_recover(self): + self.openocd.send_tcl("mww 0x58004010 0x8000") # set OPTVERR to reset + # Replace flash_unlock and option_bytes_unlock with the following lines, if this does not work + # self.openocd.send_tcl("mww 0x58004008 0x45670123") # unlock FLASH + # self.openocd.send_tcl("mww 0x58004008 0xCDEF89AB") + # self.openocd.send_tcl("mww 0x5800400c 0x08192A3B") # unlock OB + # self.openocd.send_tcl("mww 0x5800400c 0x4C5D6E7F") + self.flash_unlock() + self.option_bytes_unlock() + self.openocd.send_tcl("mmw 0x58004020 0x3ffff1aa 0xffffffff") # Reset OB + self.openocd.send_tcl("mww 0x5800402c 0xff") # Reset WRP1AR + self.openocd.send_tcl("mww 0x58004030 0xff") # Reset WRP1BR + self.openocd.send_tcl("mmw 0x58004014 0x00020000 0") # OPTSTRT + self.openocd.send_tcl("mmw 0x58004014 0x08000000 0") # OBL_LAUNCH diff --git a/scripts/ob.py b/scripts/ob.py index 7010bdec5..b7a601612 100755 --- a/scripts/ob.py +++ b/scripts/ob.py @@ -22,6 +22,12 @@ class Main(App): self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes") self._add_args(self.parser_set) self.parser_set.set_defaults(func=self.set) + # Set command + self.parser_recover = self.subparsers.add_parser( + "recover", help="Recover Option Bytes" + ) + self._add_args(self.parser_recover) + self.parser_recover.set_defaults(func=self.recover) def _add_args(self, parser): parser.add_argument( @@ -75,6 +81,20 @@ class Main(App): return return_code + def recover(self): + self.logger.info("Setting Option Bytes") + + # OpenOCD + openocd = OpenOCDProgrammer( + self.args.interface, + self.args.port_base, + self.args.serial, + ) + + openocd.option_bytes_recover() + + return 0 + if __name__ == "__main__": Main()() From 3aad84aa709e3c86a955b386be3e127b1eb67af5 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Thu, 10 Aug 2023 12:53:12 +0300 Subject: [PATCH 24/99] FBT: devboard_flash to update WiFi devboard (#2968) --- SConstruct | 3 +++ documentation/fbt.md | 1 + 2 files changed, 4 insertions(+) diff --git a/SConstruct b/SConstruct index 3ea360979..f55b9f50e 100644 --- a/SConstruct +++ b/SConstruct @@ -327,6 +327,9 @@ distenv.PhonyTarget( "cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}" ) +# Update WiFi devboard firmware +distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") + # Find blackmagic probe distenv.PhonyTarget( diff --git a/documentation/fbt.md b/documentation/fbt.md index c19780ef5..1ab67b4e6 100644 --- a/documentation/fbt.md +++ b/documentation/fbt.md @@ -69,6 +69,7 @@ To run cleanup (think of `make clean`) for specified targets, add the `-c` optio - `debug` - build and flash firmware, then attach with gdb with firmware's .elf loaded. - `debug_other`, `debug_other_blackmagic` - attach GDB without loading any `.elf`. It will allow you to manually add external `.elf` files with `add-symbol-file` in GDB. - `updater_debug` - attach GDB with the updater's `.elf` loaded. +- `devboard_flash` - update WiFi dev board with the latest firmware. - `blackmagic` - debug firmware with Blackmagic probe (WiFi dev board). - `openocd` - just start OpenOCD. - `get_blackmagic` - output the blackmagic address in the GDB remote format. Useful for IDE integration. From 43ba76a37f09b662de33684f2dd48fd628080949 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Thu, 10 Aug 2023 15:29:44 +0300 Subject: [PATCH 25/99] uFBT: devboard_flash to update WiFi devboard (#2969) * uFBT: devboard_flash to update WiFi devboard * uFBT: help --- scripts/ufbt/SConstruct | 3 +++ scripts/ufbt/site_tools/ufbt_help.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/scripts/ufbt/SConstruct b/scripts/ufbt/SConstruct index 9a9e0938c..342cd2532 100644 --- a/scripts/ufbt/SConstruct +++ b/scripts/ufbt/SConstruct @@ -348,6 +348,9 @@ appenv.PhonyTarget( '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}', ) +# Update WiFi devboard firmware +dist_env.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") + # Linter dist_env.PhonyTarget( diff --git a/scripts/ufbt/site_tools/ufbt_help.py b/scripts/ufbt/site_tools/ufbt_help.py index 3f13edcdb..3d7f6f002 100644 --- a/scripts/ufbt/site_tools/ufbt_help.py +++ b/scripts/ufbt/site_tools/ufbt_help.py @@ -26,6 +26,8 @@ Flashing & debugging: Install firmware using self-update package debug, debug_other, blackmagic: Start GDB + devboard_flash: + Update WiFi dev board with the latest firmware Other: cli: From 8d8102b4f9f63d8f6e063d809087d465f4a752bd Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 11 Aug 2023 18:24:51 +0300 Subject: [PATCH 26/99] merge fixes --- applications/services/gui/gui.h | 2 -- .../desktop_settings/scenes/desktop_settings_scene_start.c | 5 ----- 2 files changed, 7 deletions(-) diff --git a/applications/services/gui/gui.h b/applications/services/gui/gui.h index a59a4ff6a..1b5987eda 100644 --- a/applications/services/gui/gui.h +++ b/applications/services/gui/gui.h @@ -132,8 +132,6 @@ Canvas* gui_direct_draw_acquire(Gui* gui); */ void gui_direct_draw_release(Gui* gui); -uint8_t gui_get_count_of_enabled_view_port_in_layer(Gui* gui, GuiLayer layer); - #ifdef __cplusplus } #endif diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c index 9c721b9c9..6629930c3 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_start.c @@ -167,14 +167,9 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even break; case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: case SCENE_EVENT_SELECT_CLOCK_DISPLAY: - consumed = true; - break; case SCENE_EVENT_SELECT_BATTERY_DISPLAY: consumed = true; break; - case SCENE_EVENT_SELECT_CLOCK_DISPLAY: - consumed = true; - break; case SCENE_EVENT_SELECT_CHANGE_NAME: scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneChangeName); consumed = true; From 751cb9f5d68c8ea7149189d811cffe1dda9f34de Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 11 Aug 2023 23:20:38 +0300 Subject: [PATCH 27/99] totp update --- .gitmodules | 3 + applications/external/totp/application.fam | 16 +- .../external/totp/cli/commands/add/add.c | 2 + .../external/totp/cli/commands/add/add.h | 5 +- .../totp/cli/commands/automation/automation.c | 2 + .../totp/cli/commands/automation/automation.h | 5 +- .../totp/cli/commands/delete/delete.c | 2 + .../totp/cli/commands/delete/delete.h | 5 +- .../totp/cli/commands/details/details.c | 2 + .../totp/cli/commands/details/details.h | 3 + .../external/totp/cli/commands/help/help.c | 7 + .../external/totp/cli/commands/help/help.h | 5 +- .../external/totp/cli/commands/list/list.c | 2 + .../external/totp/cli/commands/list/list.h | 3 + .../external/totp/cli/commands/move/move.c | 2 + .../external/totp/cli/commands/move/move.h | 5 +- .../cli/commands/notification/notification.c | 2 + .../cli/commands/notification/notification.h | 5 +- .../external/totp/cli/commands/pin/pin.c | 2 + .../external/totp/cli/commands/pin/pin.h | 5 +- .../external/totp/cli/commands/reset/reset.c | 2 + .../external/totp/cli/commands/reset/reset.h | 5 +- .../totp/cli/commands/timezone/timezone.c | 2 + .../totp/cli/commands/timezone/timezone.h | 5 +- .../totp/cli/commands/update/update.c | 2 + .../totp/cli/commands/update/update.h | 5 +- .../external/totp/config/app/config.h | 42 +++ .../external/totp/config/wolfssl/config.h | 34 ++ applications/external/totp/features_config.h | 20 -- applications/external/totp/lib/wolfssl | 1 + .../external/totp/services/config/config.c | 23 +- .../external/totp/services/config/constants.h | 6 +- .../config/migrations/common_migration.c | 11 +- .../services/config/token_info_iterator.c | 4 +- .../external/totp/services/crypto/constants.h | 9 +- .../totp/services/crypto/crypto_facade.c | 46 ++- .../external/totp/services/crypto/crypto_v1.c | 21 +- .../external/totp/services/crypto/crypto_v1.h | 5 +- .../external/totp/services/crypto/crypto_v2.c | 46 +-- .../external/totp/services/crypto/crypto_v2.h | 5 +- .../external/totp/services/crypto/crypto_v3.c | 195 +++++++++++ .../external/totp/services/crypto/crypto_v3.h | 52 +++ .../external/totp/services/crypto/polyfills.h | 14 + .../external/totp/services/hmac/byteswap.c | 12 - .../external/totp/services/hmac/byteswap.h | 17 - .../external/totp/services/hmac/hmac_common.h | 59 ---- .../external/totp/services/hmac/hmac_sha1.c | 24 -- .../external/totp/services/hmac/hmac_sha1.h | 11 - .../external/totp/services/hmac/hmac_sha256.c | 24 -- .../external/totp/services/hmac/hmac_sha256.h | 11 - .../external/totp/services/hmac/hmac_sha512.c | 24 -- .../external/totp/services/hmac/hmac_sha512.h | 11 - .../external/totp/services/hmac/memxor.c | 30 -- .../external/totp/services/hmac/memxor.h | 28 -- .../external/totp/services/hmac/sha1.c | 251 -------------- .../external/totp/services/hmac/sha1.h | 84 ----- .../external/totp/services/hmac/sha256.c | 283 ---------------- .../external/totp/services/hmac/sha256.h | 79 ----- .../external/totp/services/hmac/sha512.c | 309 ------------------ .../external/totp/services/hmac/sha512.h | 82 ----- .../totp/services/hmac/sha_pad_buffer.c | 11 - .../totp/services/hmac/sha_pad_buffer.h | 4 - .../external/totp/services/hmac/u64.h | 44 --- .../external/totp/services/totp/totp.c | 44 ++- applications/external/totp/totp_app.c | 5 +- .../external/totp/types/automation_method.h | 2 +- .../external/totp/types/crypto_settings.h | 4 +- .../external/totp/types/plugin_state.h | 2 +- .../external/totp/ui/scene_director.c | 11 + .../ui/scenes/add_new_token/totp_input_text.c | 2 + .../ui/scenes/add_new_token/totp_input_text.h | 3 + .../add_new_token/totp_scene_add_new_token.c | 3 +- .../add_new_token/totp_scene_add_new_token.h | 3 + .../scenes/app_settings/totp_app_settings.c | 3 +- .../totp_scene_generate_token.c | 7 +- .../scenes/token_menu/totp_scene_token_menu.c | 19 +- .../external/totp/ui/totp_scenes_enum.h | 4 + applications/external/totp/version.h | 6 +- .../totp/workers/bt_type_code/bt_type_code.c | 2 +- 79 files changed, 643 insertions(+), 1513 deletions(-) create mode 100644 applications/external/totp/config/app/config.h create mode 100644 applications/external/totp/config/wolfssl/config.h delete mode 100644 applications/external/totp/features_config.h create mode 160000 applications/external/totp/lib/wolfssl create mode 100644 applications/external/totp/services/crypto/crypto_v3.c create mode 100644 applications/external/totp/services/crypto/crypto_v3.h create mode 100644 applications/external/totp/services/crypto/polyfills.h delete mode 100644 applications/external/totp/services/hmac/byteswap.c delete mode 100644 applications/external/totp/services/hmac/byteswap.h delete mode 100644 applications/external/totp/services/hmac/hmac_common.h delete mode 100644 applications/external/totp/services/hmac/hmac_sha1.c delete mode 100644 applications/external/totp/services/hmac/hmac_sha1.h delete mode 100644 applications/external/totp/services/hmac/hmac_sha256.c delete mode 100644 applications/external/totp/services/hmac/hmac_sha256.h delete mode 100644 applications/external/totp/services/hmac/hmac_sha512.c delete mode 100644 applications/external/totp/services/hmac/hmac_sha512.h delete mode 100644 applications/external/totp/services/hmac/memxor.c delete mode 100644 applications/external/totp/services/hmac/memxor.h delete mode 100644 applications/external/totp/services/hmac/sha1.c delete mode 100644 applications/external/totp/services/hmac/sha1.h delete mode 100644 applications/external/totp/services/hmac/sha256.c delete mode 100644 applications/external/totp/services/hmac/sha256.h delete mode 100644 applications/external/totp/services/hmac/sha512.c delete mode 100644 applications/external/totp/services/hmac/sha512.h delete mode 100644 applications/external/totp/services/hmac/sha_pad_buffer.c delete mode 100644 applications/external/totp/services/hmac/sha_pad_buffer.h delete mode 100644 applications/external/totp/services/hmac/u64.h diff --git a/.gitmodules b/.gitmodules index e5721aef1..323ade223 100644 --- a/.gitmodules +++ b/.gitmodules @@ -47,3 +47,6 @@ [submodule "applications/external/multi_fuzzer"] path = applications/external/multi_fuzzer url = https://github.com/DarkFlippers/Multi_Fuzzer.git +[submodule "applications/external/totp/lib/wolfssl"] + path = applications/external/totp/lib/wolfssl + url = https://github.com/wolfSSL/wolfssl.git diff --git a/applications/external/totp/application.fam b/applications/external/totp/application.fam index aba01fe73..439c9a8a1 100644 --- a/applications/external/totp/application.fam +++ b/applications/external/totp/application.fam @@ -15,7 +15,7 @@ App( ], stack_size=2 * 1024, order=20, - fap_version="3.20", + fap_version="4.01", fap_author="Alexander Kopachov (@akopachov)", fap_description="Software-based TOTP authenticator for Flipper Zero device", fap_weburl="https://github.com/akopachov/flipper-zero_authenticator", @@ -41,5 +41,19 @@ App( Lib( name="fonts", ), + Lib( + name="wolfssl", + sources=[ + "wolfcrypt/src/pwdbased.c", + "wolfcrypt/src/hmac.c", + "wolfcrypt/src/hash.c", + "wolfcrypt/src/sha.c", + "wolfcrypt/src/sha256.c", + "wolfcrypt/src/sha512.c" + ], + cflags=["-Wno-error"], + cdefines=["HAVE_CONFIG_H"], + cincludes=["config/wolfssl"] + ), ], ) diff --git a/applications/external/totp/cli/commands/add/add.c b/applications/external/totp/cli/commands/add/add.c index d2bf8b907..5e5435eec 100644 --- a/applications/external/totp/cli/commands/add/add.c +++ b/applications/external/totp/cli/commands/add/add.c @@ -79,6 +79,7 @@ static TotpIteratorUpdateTokenResult return TotpIteratorUpdateTokenResultSuccess; } +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_add_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_ADD ", " TOTP_CLI_COMMAND_ADD_ALT ", " TOTP_CLI_COMMAND_ADD_ALT2 " Add new token\r\n"); @@ -155,6 +156,7 @@ void totp_cli_command_add_docopt_options() { TOTP_CLI_PRINTF(" # " TOKEN_AUTOMATION_FEATURE_TYPE_SLOWER_NAME " - Type slower\r\n"); } +#endif void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/add/add.h b/applications/external/totp/cli/commands/add/add.h index 001dc9e80..ac2006c12 100644 --- a/applications/external/totp/cli/commands/add/add.h +++ b/applications/external/totp/cli/commands/add/add.h @@ -1,6 +1,7 @@ #pragma once #include +#include "../../../config/app/config.h" #include "../../../types/plugin_state.h" #define TOTP_CLI_COMMAND_ADD "add" @@ -8,7 +9,9 @@ #define TOTP_CLI_COMMAND_ADD_ALT2 "new" void totp_cli_command_add_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_add_docopt_commands(); void totp_cli_command_add_docopt_usage(); void totp_cli_command_add_docopt_arguments(); -void totp_cli_command_add_docopt_options(); \ No newline at end of file +void totp_cli_command_add_docopt_options(); +#endif \ No newline at end of file diff --git a/applications/external/totp/cli/commands/automation/automation.c b/applications/external/totp/cli/commands/automation/automation.c index 87c121dbe..113393130 100644 --- a/applications/external/totp/cli/commands/automation/automation.c +++ b/applications/external/totp/cli/commands/automation/automation.c @@ -15,6 +15,7 @@ #define TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT_PREFIX "-k" #define TOTP_CLI_COMMAND_AUTOMATION_ARG_KB_LAYOUT "layout" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_automation_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_AUTOMATION " Get or set automation settings\r\n"); } @@ -45,6 +46,7 @@ void totp_cli_command_automation_docopt_options() { ", " TOTP_CLI_COMMAND_AUTOMATION_LAYOUT_AZERTY "\r\n"); } +#endif static void print_method(AutomationMethod method, const char* color) { #ifdef TOTP_BADBT_AUTOMATION_ENABLED diff --git a/applications/external/totp/cli/commands/automation/automation.h b/applications/external/totp/cli/commands/automation/automation.h index 4a713d49b..522bfc560 100644 --- a/applications/external/totp/cli/commands/automation/automation.h +++ b/applications/external/totp/cli/commands/automation/automation.h @@ -2,11 +2,14 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_AUTOMATION "automation" void totp_cli_command_automation_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_automation_docopt_commands(); void totp_cli_command_automation_docopt_usage(); void totp_cli_command_automation_docopt_arguments(); -void totp_cli_command_automation_docopt_options(); \ No newline at end of file +void totp_cli_command_automation_docopt_options(); +#endif \ No newline at end of file diff --git a/applications/external/totp/cli/commands/delete/delete.c b/applications/external/totp/cli/commands/delete/delete.c index 9a084b23f..d9cea4803 100644 --- a/applications/external/totp/cli/commands/delete/delete.c +++ b/applications/external/totp/cli/commands/delete/delete.c @@ -10,6 +10,7 @@ #define TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX "-f" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_delete_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DELETE ", " TOTP_CLI_COMMAND_DELETE_ALT " Delete existing token\r\n"); @@ -30,6 +31,7 @@ void totp_cli_command_delete_docopt_options() { TOTP_CLI_PRINTF(" " DOCOPT_SWITCH( TOTP_CLI_COMMAND_DELETE_ARG_FORCE_PREFIX) " Force command to do not ask user for interactive confirmation\r\n"); } +#endif void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/delete/delete.h b/applications/external/totp/cli/commands/delete/delete.h index deb7f74fe..98b15e595 100644 --- a/applications/external/totp/cli/commands/delete/delete.h +++ b/applications/external/totp/cli/commands/delete/delete.h @@ -2,12 +2,15 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_DELETE "delete" #define TOTP_CLI_COMMAND_DELETE_ALT "rm" void totp_cli_command_delete_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_delete_docopt_commands(); void totp_cli_command_delete_docopt_usage(); void totp_cli_command_delete_docopt_arguments(); -void totp_cli_command_delete_docopt_options(); \ No newline at end of file +void totp_cli_command_delete_docopt_options(); +#endif diff --git a/applications/external/totp/cli/commands/details/details.c b/applications/external/totp/cli/commands/details/details.c index 1677121b1..7e714bf69 100644 --- a/applications/external/totp/cli/commands/details/details.c +++ b/applications/external/totp/cli/commands/details/details.c @@ -37,6 +37,7 @@ static void print_automation_features(const TokenInfo* token_info) { } } +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_details_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_DETAILS ", " TOTP_CLI_COMMAND_DETAILS_ALT " Displays token details\r\n"); @@ -47,6 +48,7 @@ void totp_cli_command_details_docopt_usage() { TOTP_CLI_COMMAND_DETAILS " | " TOTP_CLI_COMMAND_DETAILS_ALT) " " DOCOPT_ARGUMENT(TOTP_CLI_COMMAND_ARG_INDEX) "\r\n"); } +#endif void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/details/details.h b/applications/external/totp/cli/commands/details/details.h index d141705a5..aec1a0184 100644 --- a/applications/external/totp/cli/commands/details/details.h +++ b/applications/external/totp/cli/commands/details/details.h @@ -2,10 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_DETAILS "lsattr" #define TOTP_CLI_COMMAND_DETAILS_ALT "cat" void totp_cli_command_details_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_details_docopt_commands(); void totp_cli_command_details_docopt_usage(); +#endif diff --git a/applications/external/totp/cli/commands/help/help.c b/applications/external/totp/cli/commands/help/help.c index 7093877ea..e5519d043 100644 --- a/applications/external/totp/cli/commands/help/help.c +++ b/applications/external/totp/cli/commands/help/help.c @@ -12,6 +12,7 @@ #include "../automation/automation.h" #include "../details/details.h" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_help_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_HELP ", " TOTP_CLI_COMMAND_HELP_ALT ", " TOTP_CLI_COMMAND_HELP_ALT2 " Show command usage help\r\n"); @@ -22,8 +23,10 @@ void totp_cli_command_help_docopt_usage() { TOTP_CLI_COMMAND_HELP " | " TOTP_CLI_COMMAND_HELP_ALT " | " TOTP_CLI_COMMAND_HELP_ALT2) "\r\n"); } +#endif void totp_cli_command_help_handle() { +#ifdef TOTP_CLI_RICH_HELP_ENABLED TOTP_CLI_PRINTF("Usage:\r\n"); totp_cli_command_help_docopt_usage(); totp_cli_command_list_docopt_usage(); @@ -66,4 +69,8 @@ void totp_cli_command_help_handle() { totp_cli_command_delete_docopt_options(); totp_cli_command_pin_docopt_options(); totp_cli_command_automation_docopt_options(); +#else + TOTP_CLI_PRINTF( + "All the TOTP CLI commands, their arguments, options and usage can be found here https://t.ly/_6pJG"); +#endif } \ No newline at end of file diff --git a/applications/external/totp/cli/commands/help/help.h b/applications/external/totp/cli/commands/help/help.h index 4268b8bc5..da7c2fd62 100644 --- a/applications/external/totp/cli/commands/help/help.h +++ b/applications/external/totp/cli/commands/help/help.h @@ -1,5 +1,6 @@ #pragma once +#include "../../../config/app/config.h" #include #define TOTP_CLI_COMMAND_HELP "help" @@ -7,5 +8,7 @@ #define TOTP_CLI_COMMAND_HELP_ALT2 "?" void totp_cli_command_help_handle(); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_help_docopt_commands(); -void totp_cli_command_help_docopt_usage(); \ No newline at end of file +void totp_cli_command_help_docopt_usage(); +#endif \ No newline at end of file diff --git a/applications/external/totp/cli/commands/list/list.c b/applications/external/totp/cli/commands/list/list.c index bf5a8da52..d5c97c6b0 100644 --- a/applications/external/totp/cli/commands/list/list.c +++ b/applications/external/totp/cli/commands/list/list.c @@ -6,6 +6,7 @@ #include "../../../ui/scene_director.h" #include "../../cli_helpers.h" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_list_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_LIST ", " TOTP_CLI_COMMAND_LIST_ALT " List all available tokens\r\n"); @@ -15,6 +16,7 @@ void totp_cli_command_list_docopt_usage() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " DOCOPT_REQUIRED( TOTP_CLI_COMMAND_LIST " | " TOTP_CLI_COMMAND_LIST_ALT) "\r\n"); } +#endif void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/list/list.h b/applications/external/totp/cli/commands/list/list.h index 7d6de5874..8237bfd2b 100644 --- a/applications/external/totp/cli/commands/list/list.h +++ b/applications/external/totp/cli/commands/list/list.h @@ -2,10 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_LIST "list" #define TOTP_CLI_COMMAND_LIST_ALT "ls" void totp_cli_command_list_handle(PluginState* plugin_state, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_list_docopt_commands(); void totp_cli_command_list_docopt_usage(); +#endif diff --git a/applications/external/totp/cli/commands/move/move.c b/applications/external/totp/cli/commands/move/move.c index f26cda46e..fbf82a3bb 100644 --- a/applications/external/totp/cli/commands/move/move.c +++ b/applications/external/totp/cli/commands/move/move.c @@ -10,6 +10,7 @@ #define TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX "new_index" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_move_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_MOVE ", " TOTP_CLI_COMMAND_MOVE_ALT " Move token\r\n"); @@ -26,6 +27,7 @@ void totp_cli_command_move_docopt_arguments() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_MOVE_ARG_NEW_INDEX " New token index in the list\r\n"); } +#endif void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/move/move.h b/applications/external/totp/cli/commands/move/move.h index da84f2a62..0dfd90f6b 100644 --- a/applications/external/totp/cli/commands/move/move.h +++ b/applications/external/totp/cli/commands/move/move.h @@ -2,11 +2,14 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_MOVE "move" #define TOTP_CLI_COMMAND_MOVE_ALT "mv" void totp_cli_command_move_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_move_docopt_commands(); void totp_cli_command_move_docopt_usage(); -void totp_cli_command_move_docopt_arguments(); \ No newline at end of file +void totp_cli_command_move_docopt_arguments(); +#endif diff --git a/applications/external/totp/cli/commands/notification/notification.c b/applications/external/totp/cli/commands/notification/notification.c index f91b1b982..d1814fb26 100644 --- a/applications/external/totp/cli/commands/notification/notification.c +++ b/applications/external/totp/cli/commands/notification/notification.c @@ -9,6 +9,7 @@ #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND "sound" #define TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "vibro" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_notification_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NOTIFICATION " Get or set notification method\r\n"); @@ -27,6 +28,7 @@ void totp_cli_command_notification_docopt_arguments() { ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_SOUND ", " TOTP_CLI_COMMAND_NOTIFICATION_METHOD_VIBRO "\r\n"); } +#endif static void totp_cli_command_notification_print_method(NotificationMethod method, const char* color) { diff --git a/applications/external/totp/cli/commands/notification/notification.h b/applications/external/totp/cli/commands/notification/notification.h index c349f9620..e4cdb651d 100644 --- a/applications/external/totp/cli/commands/notification/notification.h +++ b/applications/external/totp/cli/commands/notification/notification.h @@ -2,10 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_NOTIFICATION "notify" void totp_cli_command_notification_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_notification_docopt_commands(); void totp_cli_command_notification_docopt_usage(); -void totp_cli_command_notification_docopt_arguments(); \ No newline at end of file +void totp_cli_command_notification_docopt_arguments(); +#endif \ No newline at end of file diff --git a/applications/external/totp/cli/commands/pin/pin.c b/applications/external/totp/cli/commands/pin/pin.c index 2b989a713..d31790950 100644 --- a/applications/external/totp/cli/commands/pin/pin.c +++ b/applications/external/totp/cli/commands/pin/pin.c @@ -15,6 +15,7 @@ #define TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT_PREFIX "-c" #define TOTP_CLI_COMMAND_PIN_ARG_NEW_CRYPTO_KEY_SLOT "slot" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_pin_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_PIN " Set\\change\\remove PIN\r\n"); } @@ -37,6 +38,7 @@ void totp_cli_command_pin_docopt_options() { ACCEPTABLE_CRYPTO_KEY_SLOT_START, ACCEPTABLE_CRYPTO_KEY_SLOT_END); } +#endif static inline uint8_t totp_cli_key_to_pin_code(uint8_t key) { uint8_t code = 0; diff --git a/applications/external/totp/cli/commands/pin/pin.h b/applications/external/totp/cli/commands/pin/pin.h index 2d320a02a..708353565 100644 --- a/applications/external/totp/cli/commands/pin/pin.h +++ b/applications/external/totp/cli/commands/pin/pin.h @@ -2,10 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_PIN "pin" void totp_cli_command_pin_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_pin_docopt_commands(); void totp_cli_command_pin_docopt_usage(); -void totp_cli_command_pin_docopt_options(); \ No newline at end of file +void totp_cli_command_pin_docopt_options(); +#endif diff --git a/applications/external/totp/cli/commands/reset/reset.c b/applications/external/totp/cli/commands/reset/reset.c index 250d594bf..a02bc0eb0 100644 --- a/applications/external/totp/cli/commands/reset/reset.c +++ b/applications/external/totp/cli/commands/reset/reset.c @@ -8,6 +8,7 @@ #define TOTP_CLI_RESET_CONFIRMATION_KEYWORD "YES" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_reset_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_RESET " Reset application to default settings\r\n"); @@ -16,6 +17,7 @@ void totp_cli_command_reset_docopt_commands() { void totp_cli_command_reset_docopt_usage() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_NAME " " TOTP_CLI_COMMAND_RESET "\r\n"); } +#endif void totp_cli_command_reset_handle(PluginState* plugin_state, Cli* cli) { TOTP_CLI_LOCK_UI(plugin_state); diff --git a/applications/external/totp/cli/commands/reset/reset.h b/applications/external/totp/cli/commands/reset/reset.h index 221c84304..7f48aa10b 100644 --- a/applications/external/totp/cli/commands/reset/reset.h +++ b/applications/external/totp/cli/commands/reset/reset.h @@ -2,9 +2,12 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_RESET "reset" void totp_cli_command_reset_handle(PluginState* plugin_state, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_reset_docopt_commands(); -void totp_cli_command_reset_docopt_usage(); \ No newline at end of file +void totp_cli_command_reset_docopt_usage(); +#endif diff --git a/applications/external/totp/cli/commands/timezone/timezone.c b/applications/external/totp/cli/commands/timezone/timezone.c index 0f6bc5a76..af5db6e6e 100644 --- a/applications/external/totp/cli/commands/timezone/timezone.c +++ b/applications/external/totp/cli/commands/timezone/timezone.c @@ -6,6 +6,7 @@ #define TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE "timezone" +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_timezone_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE ", " TOTP_CLI_COMMAND_TIMEZONE_ALT " Get or set current timezone\r\n"); @@ -22,6 +23,7 @@ void totp_cli_command_timezone_docopt_arguments() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_TIMEZONE_ARG_TIMEZONE " Timezone offset in hours to be set\r\n"); } +#endif void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/timezone/timezone.h b/applications/external/totp/cli/commands/timezone/timezone.h index 9823cf0ee..1a697f067 100644 --- a/applications/external/totp/cli/commands/timezone/timezone.h +++ b/applications/external/totp/cli/commands/timezone/timezone.h @@ -2,11 +2,14 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_TIMEZONE "timezone" #define TOTP_CLI_COMMAND_TIMEZONE_ALT "tz" void totp_cli_command_timezone_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_timezone_docopt_commands(); void totp_cli_command_timezone_docopt_usage(); -void totp_cli_command_timezone_docopt_arguments(); \ No newline at end of file +void totp_cli_command_timezone_docopt_arguments(); +#endif diff --git a/applications/external/totp/cli/commands/update/update.c b/applications/external/totp/cli/commands/update/update.c index 79ed75670..5c1553d87 100644 --- a/applications/external/totp/cli/commands/update/update.c +++ b/applications/external/totp/cli/commands/update/update.c @@ -107,6 +107,7 @@ static TotpIteratorUpdateTokenResult return TotpIteratorUpdateTokenResultSuccess; } +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_update_docopt_commands() { TOTP_CLI_PRINTF(" " TOTP_CLI_COMMAND_UPDATE " Update existing token\r\n"); } @@ -129,6 +130,7 @@ void totp_cli_command_update_docopt_options() { TOTP_CLI_PRINTF(" " DOCOPT_SWITCH( TOTP_CLI_COMMAND_UPDATE_ARG_SECRET_PREFIX) " Update token secret\r\n"); } +#endif void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli) { if(!totp_cli_ensure_authenticated(plugin_state, cli)) { diff --git a/applications/external/totp/cli/commands/update/update.h b/applications/external/totp/cli/commands/update/update.h index 3b5b442e8..6100f4b38 100644 --- a/applications/external/totp/cli/commands/update/update.h +++ b/applications/external/totp/cli/commands/update/update.h @@ -2,10 +2,13 @@ #include #include "../../../types/plugin_state.h" +#include "../../../config/app/config.h" #define TOTP_CLI_COMMAND_UPDATE "update" void totp_cli_command_update_handle(PluginState* plugin_state, FuriString* args, Cli* cli); +#ifdef TOTP_CLI_RICH_HELP_ENABLED void totp_cli_command_update_docopt_commands(); void totp_cli_command_update_docopt_usage(); -void totp_cli_command_update_docopt_options(); \ No newline at end of file +void totp_cli_command_update_docopt_options(); +#endif \ No newline at end of file diff --git a/applications/external/totp/config/app/config.h b/applications/external/totp/config/app/config.h new file mode 100644 index 000000000..8c5fc1314 --- /dev/null +++ b/applications/external/totp/config/app/config.h @@ -0,0 +1,42 @@ +// Application automatic lock timeout if user IDLE. (ticks) +#ifndef TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC +#define TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC (60) +#endif + +// Enables\disables Bluetooth token input automation +#ifndef TOTP_NO_BADBT_AUTOMATION +#define TOTP_BADBT_AUTOMATION_ENABLED +#endif + +// Enables\disables backward compatibility with crypto algorithms v1 +#ifndef TOTP_NO_OBSOLETE_CRYPTO_V1_COMPATIBILITY +#define TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED +#endif + +// Enables\disables backward compatibility with crypto algorithms v2 +#ifndef TOTP_NO_OBSOLETE_CRYPTO_V2_COMPATIBILITY +#define TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED +#endif + +// Enables\disables userfriendly TOTP CLI help text +// If disabled, it will print a link to a wiki page +#ifndef TOTP_CLI_NO_RICH_HELP +#define TOTP_CLI_RICH_HELP_ENABLED +#endif + +// Enables\disables "Add new token" UI +// If disabled it will print a link to wiki page +#ifndef TOTP_UI_NO_ADD_NEW_TOKEN +#define TOTP_UI_ADD_NEW_TOKEN_ENABLED +#endif + +// List of compatible firmwares +#define TOTP_FIRMWARE_OFFICIAL_STABLE (1) +#define TOTP_FIRMWARE_OFFICIAL_DEV (2) +#define TOTP_FIRMWARE_XTREME_UL (3) +// End of list + +// Target firmware +#ifndef TOTP_TARGET_FIRMWARE +#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME_UL +#endif diff --git a/applications/external/totp/config/wolfssl/config.h b/applications/external/totp/config/wolfssl/config.h new file mode 100644 index 000000000..6500d6422 --- /dev/null +++ b/applications/external/totp/config/wolfssl/config.h @@ -0,0 +1,34 @@ +#pragma once + +#define NO_OLD_SHA_NAMES +#define WOLFCRYPT_ONLY +#define NO_SIG_WRAPPER +#define NO_AES +#define NO_AES_CBC +#define NO_DES3 +#define NO_DSA +#define NO_RSA +#define NO_DH +#define NO_RC4 +#define NO_MD4 +#define NO_MD5 +#define NO_PKCS12 +#define NO_PKCS8 +#define WC_NO_RNG +#define NO_FILESYSTEM +#define NO_WRITEV +#define NO_MAIN_DRIVER +#define NO_DEV_RANDOM +#define WOLFSSL_SHA512 +#define WOLFSSL_NOSHA512_224 +#define WOLFSSL_NOSHA512_256 +#define USE_SLOW_SHA512 +#define USE_SLOW_SHA256 +#define USE_SLOW_SHA +#define NO_CERTS +#define NO_WOLFSSL_MEMORY +#define WOLFSSL_NO_PEM +#define NO_PSK +#define NO_ERROR_STRINGS +#define NO_OLD_TLS +#define SINGLE_THREADED diff --git a/applications/external/totp/features_config.h b/applications/external/totp/features_config.h deleted file mode 100644 index 764f27bea..000000000 --- a/applications/external/totp/features_config.h +++ /dev/null @@ -1,20 +0,0 @@ -// Application automatic lock timeout if user IDLE. (ticks) -#ifndef TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC -#define TOTP_AUTO_LOCK_IDLE_TIMEOUT_SEC (60) -#endif - -// Include Bluetooth token input automation -#ifndef TOTP_NO_BADBT_AUTOMATION -#define TOTP_BADBT_AUTOMATION_ENABLED -#endif - -// List of compatible firmwares -#define TOTP_FIRMWARE_OFFICIAL_STABLE (1) -#define TOTP_FIRMWARE_OFFICIAL_DEV (2) -#define TOTP_FIRMWARE_XTREME_UL (3) -// End of list - -// Target firmware -#ifndef TOTP_TARGET_FIRMWARE -#define TOTP_TARGET_FIRMWARE TOTP_FIRMWARE_XTREME_UL -#endif diff --git a/applications/external/totp/lib/wolfssl b/applications/external/totp/lib/wolfssl new file mode 160000 index 000000000..3b3c175af --- /dev/null +++ b/applications/external/totp/lib/wolfssl @@ -0,0 +1 @@ +Subproject commit 3b3c175af0e993ffaae251871421e206cc41963f diff --git a/applications/external/totp/services/config/config.c b/applications/external/totp/services/config/config.c index 19b1cf368..9320f4507 100644 --- a/applications/external/totp/services/config/config.c +++ b/applications/external/totp/services/config/config.c @@ -8,7 +8,7 @@ #include #include "../../types/common.h" #include "../../types/token_info.h" -#include "../../features_config.h" +#include "../../config/app/config.h" #include "../crypto/crypto_facade.h" #include "../crypto/constants.h" #include "migrations/common_migration.h" @@ -112,7 +112,8 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); bool conf_file_exists = storage_common_stat(storage, CONFIG_FILE_PATH, NULL) == FSE_OK; - if(!conf_file_exists) { + if(!conf_file_exists && + storage_common_stat(storage, EXT_PATH("authenticator"), NULL) == FSE_OK) { FURI_LOG_I(LOGGING_TAG, "Application catalog needs to be migrated"); FS_Error migration_result = storage_common_migrate(storage, EXT_PATH("authenticator"), CONFIG_FILE_DIRECTORY_PATH); @@ -148,7 +149,7 @@ static bool totp_open_config_file(Storage* storage, FlipperFormat** file) { flipper_format_write_comment_cstr( fff_data_file, - "Config file format specification can be found here: https://github.com/akopachov/flipper-zero_authenticator/blob/master/docs/conf-file_description.md"); + "Config file format specification can be found here: https://t.ly/zwQjE"); float tmp_tz = 0; flipper_format_write_float(fff_data_file, TOTP_CONFIG_KEY_TIMEZONE, &tmp_tz, 1); @@ -396,10 +397,10 @@ bool totp_config_file_load(PluginState* const plugin_state) { if(!flipper_format_read_hex( fff_data_file, - TOTP_CONFIG_KEY_BASE_IV, - &plugin_state->crypto_settings.base_iv[0], - CRYPTO_IV_LENGTH)) { - FURI_LOG_D(LOGGING_TAG, "Missing base IV"); + TOTP_CONFIG_KEY_SALT, + &plugin_state->crypto_settings.salt[0], + CRYPTO_SALT_LENGTH)) { + FURI_LOG_D(LOGGING_TAG, "Missing salt"); } if(!flipper_format_rewind(fff_data_file)) { @@ -529,9 +530,9 @@ bool totp_config_file_update_crypto_signatures(const PluginState* plugin_state) if(!flipper_format_insert_or_update_hex( config_file, - TOTP_CONFIG_KEY_BASE_IV, - plugin_state->crypto_settings.base_iv, - CRYPTO_IV_LENGTH)) { + TOTP_CONFIG_KEY_SALT, + &plugin_state->crypto_settings.salt[0], + CRYPTO_SALT_LENGTH)) { break; } @@ -592,7 +593,7 @@ bool totp_config_file_update_encryption( CryptoSettings old_crypto_settings = plugin_state->crypto_settings; memset(&plugin_state->crypto_settings.iv[0], 0, CRYPTO_IV_LENGTH); - memset(&plugin_state->crypto_settings.base_iv[0], 0, CRYPTO_IV_LENGTH); + memset(&plugin_state->crypto_settings.salt[0], 0, CRYPTO_SALT_LENGTH); if(plugin_state->crypto_settings.crypto_verify_data != NULL) { free(plugin_state->crypto_settings.crypto_verify_data); plugin_state->crypto_settings.crypto_verify_data = NULL; diff --git a/applications/external/totp/services/config/constants.h b/applications/external/totp/services/config/constants.h index 1205bae07..97c8f0d0a 100644 --- a/applications/external/totp/services/config/constants.h +++ b/applications/external/totp/services/config/constants.h @@ -2,9 +2,9 @@ #include -#define CONFIG_FILE_DIRECTORY_PATH STORAGE_APP_DATA_PATH_PREFIX +#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/totp") #define CONFIG_FILE_HEADER "Flipper TOTP plugin config file" -#define CONFIG_FILE_ACTUAL_VERSION (8) +#define CONFIG_FILE_ACTUAL_VERSION (9) #define TOTP_CONFIG_KEY_TIMEZONE "Timezone" #define TOTP_CONFIG_KEY_TOKEN_NAME "TokenName" @@ -14,7 +14,7 @@ #define TOTP_CONFIG_KEY_TOKEN_DURATION "TokenDuration" #define TOTP_CONFIG_KEY_TOKEN_AUTOMATION_FEATURES "TokenAutomationFeatures" #define TOTP_CONFIG_KEY_CRYPTO_VERIFY "Crypto" -#define TOTP_CONFIG_KEY_BASE_IV "BaseIV" +#define TOTP_CONFIG_KEY_SALT "Salt" #define TOTP_CONFIG_KEY_PINSET "PinIsSet" #define TOTP_CONFIG_KEY_NOTIFICATION_METHOD "NotificationMethod" #define TOTP_CONFIG_KEY_AUTOMATION_METHOD "AutomationMethod" diff --git a/applications/external/totp/services/config/migrations/common_migration.c b/applications/external/totp/services/config/migrations/common_migration.c index 4a12cbd3a..33a441e72 100644 --- a/applications/external/totp/services/config/migrations/common_migration.c +++ b/applications/external/totp/services/config/migrations/common_migration.c @@ -4,6 +4,8 @@ #include "../../../types/automation_kb_layout.h" #include +#define TOTP_OLD_CONFIG_KEY_BASE_IV "BaseIV" + bool totp_config_migrate_to_latest( FlipperFormat* fff_data_file, FlipperFormat* fff_backup_data_file) { @@ -40,8 +42,13 @@ bool totp_config_migrate_to_latest( flipper_format_rewind(fff_backup_data_file); - if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str)) { - flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_BASE_IV, temp_str); + if(flipper_format_read_string(fff_backup_data_file, TOTP_CONFIG_KEY_SALT, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_SALT, temp_str); + } else if( + flipper_format_rewind(fff_backup_data_file) && + flipper_format_read_string( + fff_backup_data_file, TOTP_OLD_CONFIG_KEY_BASE_IV, temp_str)) { + flipper_format_write_string(fff_data_file, TOTP_CONFIG_KEY_SALT, temp_str); } flipper_format_rewind(fff_backup_data_file); diff --git a/applications/external/totp/services/config/token_info_iterator.c b/applications/external/totp/services/config/token_info_iterator.c index 75424ef43..3d47b9616 100644 --- a/applications/external/totp/services/config/token_info_iterator.c +++ b/applications/external/totp/services/config/token_info_iterator.c @@ -7,7 +7,7 @@ #include "../../types/crypto_settings.h" #define CONFIG_FILE_PART_FILE_PATH CONFIG_FILE_DIRECTORY_PATH "/totp.conf.part" -#define STREAM_COPY_BUFFER_SIZE 128 +#define STREAM_COPY_BUFFER_SIZE (128) struct TokenInfoIteratorContext { size_t total_count; @@ -547,4 +547,6 @@ void totp_token_info_iterator_attach_to_config_file( TokenInfoIteratorContext* context, FlipperFormat* config_file) { context->config_file = config_file; + Stream* stream = flipper_format_get_raw_stream(context->config_file); + stream_seek(stream, context->last_seek_offset, StreamOffsetFromStart); } \ No newline at end of file diff --git a/applications/external/totp/services/crypto/constants.h b/applications/external/totp/services/crypto/constants.h index 294ae4361..bb3affbd0 100644 --- a/applications/external/totp/services/crypto/constants.h +++ b/applications/external/totp/services/crypto/constants.h @@ -1,11 +1,14 @@ #pragma once +#include "polyfills.h" + #define CRYPTO_IV_LENGTH (16) +#define CRYPTO_SALT_LENGTH (16) // According to this explanation: https://github.com/flipperdevices/flipperzero-firmware/issues/2885#issuecomment-1646664666 // disabling usage of any key which is "the same across all devices" -#define ACCEPTABLE_CRYPTO_KEY_SLOT_START (12) -#define ACCEPTABLE_CRYPTO_KEY_SLOT_END (100) +#define ACCEPTABLE_CRYPTO_KEY_SLOT_START FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START +#define ACCEPTABLE_CRYPTO_KEY_SLOT_END FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END #define DEFAULT_CRYPTO_KEY_SLOT ACCEPTABLE_CRYPTO_KEY_SLOT_START -#define CRYPTO_LATEST_VERSION (2) \ No newline at end of file +#define CRYPTO_LATEST_VERSION (3) \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_facade.c b/applications/external/totp/services/crypto/crypto_facade.c index acbc09a4e..29e588073 100644 --- a/applications/external/totp/services/crypto/crypto_facade.c +++ b/applications/external/totp/services/crypto/crypto_facade.c @@ -1,8 +1,14 @@ #include "crypto_facade.h" +#include "../../config/app/config.h" #include #include +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED #include "crypto_v1.h" +#endif +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED #include "crypto_v2.h" +#endif +#include "crypto_v3.h" #include "constants.h" bool totp_crypto_check_key_slot(uint8_t key_slot) { @@ -11,9 +17,9 @@ bool totp_crypto_check_key_slot(uint8_t key_slot) { return false; } - return furi_hal_crypto_verify_key(key_slot) && - furi_hal_crypto_store_load_key(key_slot, empty_iv) && - furi_hal_crypto_store_unload_key(key_slot); + return furi_hal_crypto_enclave_ensure_key(key_slot) && + furi_hal_crypto_enclave_load_key(key_slot, empty_iv) && + furi_hal_crypto_enclave_unload_key(key_slot); } uint8_t* totp_crypto_encrypt( @@ -21,15 +27,24 @@ uint8_t* totp_crypto_encrypt( const size_t plain_data_length, const CryptoSettings* crypto_settings, size_t* encrypted_data_length) { +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED if(crypto_settings->crypto_version == 1) { return totp_crypto_encrypt_v1( plain_data, plain_data_length, crypto_settings, encrypted_data_length); } +#endif +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED if(crypto_settings->crypto_version == 2) { return totp_crypto_encrypt_v2( plain_data, plain_data_length, crypto_settings, encrypted_data_length); } +#endif + + if(crypto_settings->crypto_version == 3) { + return totp_crypto_encrypt_v3( + plain_data, plain_data_length, crypto_settings, encrypted_data_length); + } furi_crash("Unsupported crypto version"); } @@ -39,40 +54,65 @@ uint8_t* totp_crypto_decrypt( const size_t encrypted_data_length, const CryptoSettings* crypto_settings, size_t* decrypted_data_length) { +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED if(crypto_settings->crypto_version == 1) { return totp_crypto_decrypt_v1( encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length); } +#endif +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED if(crypto_settings->crypto_version == 2) { return totp_crypto_decrypt_v2( encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length); } +#endif + + if(crypto_settings->crypto_version == 3) { + return totp_crypto_decrypt_v3( + encrypted_data, encrypted_data_length, crypto_settings, decrypted_data_length); + } furi_crash("Unsupported crypto version"); } CryptoSeedIVResult totp_crypto_seed_iv(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length) { +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED if(crypto_settings->crypto_version == 1) { return totp_crypto_seed_iv_v1(crypto_settings, pin, pin_length); } +#endif +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED if(crypto_settings->crypto_version == 2) { return totp_crypto_seed_iv_v2(crypto_settings, pin, pin_length); } +#endif + + if(crypto_settings->crypto_version == 3) { + return totp_crypto_seed_iv_v3(crypto_settings, pin, pin_length); + } furi_crash("Unsupported crypto version"); } bool totp_crypto_verify_key(const CryptoSettings* crypto_settings) { +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED if(crypto_settings->crypto_version == 1) { return totp_crypto_verify_key_v1(crypto_settings); } +#endif +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED if(crypto_settings->crypto_version == 2) { return totp_crypto_verify_key_v2(crypto_settings); } +#endif + + if(crypto_settings->crypto_version == 3) { + return totp_crypto_verify_key_v3(crypto_settings); + } furi_crash("Unsupported crypto version"); } \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_v1.c b/applications/external/totp/services/crypto/crypto_v1.c index d637599f5..b6ead80b1 100644 --- a/applications/external/totp/services/crypto/crypto_v1.c +++ b/applications/external/totp/services/crypto/crypto_v1.c @@ -1,4 +1,5 @@ #include "crypto_v1.h" +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED #include #include #include @@ -6,6 +7,7 @@ #include #include "../../types/common.h" #include "memset_s.h" +#include "polyfills.h" #define CRYPTO_KEY_SLOT (2) #define CRYPTO_VERIFY_KEY_LENGTH (16) @@ -32,9 +34,9 @@ uint8_t* totp_crypto_encrypt_v1( furi_check(encrypted_data != NULL); *encrypted_data_length = plain_data_aligned_length; - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); + furi_hal_crypto_enclave_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(CRYPTO_KEY_SLOT); memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length); free(plain_data_aligned); @@ -43,9 +45,9 @@ uint8_t* totp_crypto_encrypt_v1( furi_check(encrypted_data != NULL); *encrypted_data_length = plain_data_length; - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); + furi_hal_crypto_enclave_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(CRYPTO_KEY_SLOT); } return encrypted_data; @@ -59,9 +61,9 @@ uint8_t* totp_crypto_decrypt_v1( *decrypted_data_length = encrypted_data_length; uint8_t* decrypted_data = malloc(*decrypted_data_length); furi_check(decrypted_data != NULL); - furi_hal_crypto_store_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); + furi_hal_crypto_enclave_load_key(CRYPTO_KEY_SLOT, crypto_settings->iv); furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length); - furi_hal_crypto_store_unload_key(CRYPTO_KEY_SLOT); + furi_hal_crypto_enclave_unload_key(CRYPTO_KEY_SLOT); return decrypted_data; } @@ -72,10 +74,10 @@ CryptoSeedIVResult totp_crypto_seed_iv_v1( CryptoSeedIVResult result; if(crypto_settings->crypto_verify_data == NULL) { FURI_LOG_I(LOGGING_TAG, "Generating new IV"); - furi_hal_random_fill_buf(&crypto_settings->base_iv[0], TOTP_IV_SIZE); + furi_hal_random_fill_buf(&crypto_settings->salt[0], CRYPTO_SALT_LENGTH); } - memcpy(&crypto_settings->iv[0], &crypto_settings->base_iv[0], TOTP_IV_SIZE); + memcpy(&crypto_settings->iv[0], &crypto_settings->salt[0], TOTP_IV_SIZE); if(pin != NULL && pin_length > 0) { uint8_t max_i; if(pin_length > TOTP_IV_SIZE) { @@ -139,4 +141,5 @@ bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings) { free(decrypted_key); return key_valid; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_v1.h b/applications/external/totp/services/crypto/crypto_v1.h index 80e850196..859615ae3 100644 --- a/applications/external/totp/services/crypto/crypto_v1.h +++ b/applications/external/totp/services/crypto/crypto_v1.h @@ -1,5 +1,7 @@ #pragma once +#include "../../config/app/config.h" +#ifdef TOTP_OBSOLETE_CRYPTO_V1_COMPATIBILITY_ENABLED #include #include #include @@ -49,4 +51,5 @@ CryptoSeedIVResult * @param crypto_settings crypto settings * @return \c true if cryptographic information is valid; \c false otherwise */ -bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings); \ No newline at end of file +bool totp_crypto_verify_key_v1(const CryptoSettings* crypto_settings); +#endif diff --git a/applications/external/totp/services/crypto/crypto_v2.c b/applications/external/totp/services/crypto/crypto_v2.c index 897a7fd32..498995619 100644 --- a/applications/external/totp/services/crypto/crypto_v2.c +++ b/applications/external/totp/services/crypto/crypto_v2.c @@ -1,13 +1,16 @@ #include "crypto_v2.h" +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED #include #include #include #include #include #include "../../types/common.h" -#include "../hmac/hmac_sha512.h" +#include "../../config/wolfssl/config.h" +#include #include "memset_s.h" #include "constants.h" +#include "polyfills.h" #define CRYPTO_ALIGNMENT_FACTOR (16) @@ -46,14 +49,14 @@ uint8_t* totp_crypto_encrypt_v2( *encrypted_data_length = plain_data_aligned_length; furi_check( - furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), - "Encryption failed: store_load_key"); + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Encryption failed: enclave_load_key"); furi_check( furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length), "Encryption failed: encrypt"); furi_check( - furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot), - "Encryption failed: store_unload_key"); + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Encryption failed: enclave_unload_key"); memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length); free(plain_data_aligned); @@ -63,13 +66,13 @@ uint8_t* totp_crypto_encrypt_v2( *encrypted_data_length = plain_data_length; furi_check( - furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), "Encryption failed: store_load_key"); furi_check( furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length), "Encryption failed: encrypt"); furi_check( - furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot), + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), "Encryption failed: store_unload_key"); } @@ -85,14 +88,14 @@ uint8_t* totp_crypto_decrypt_v2( uint8_t* decrypted_data = malloc(*decrypted_data_length); furi_check(decrypted_data != NULL); furi_check( - furi_hal_crypto_store_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), - "Decryption failed: store_load_key"); + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Decryption failed: enclave_load_key"); furi_check( furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length), "Decryption failed: decrypt"); furi_check( - furi_hal_crypto_store_unload_key(crypto_settings->crypto_key_slot), - "Decryption failed: store_unload_key"); + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Decryption failed: enclave_unload_key"); return decrypted_data; } @@ -102,12 +105,10 @@ CryptoSeedIVResult totp_crypto_seed_iv_v2( uint8_t pin_length) { CryptoSeedIVResult result; if(crypto_settings->crypto_verify_data == NULL) { - FURI_LOG_I(LOGGING_TAG, "Generating new IV"); - furi_hal_random_fill_buf(&crypto_settings->base_iv[0], CRYPTO_IV_LENGTH); + FURI_LOG_I(LOGGING_TAG, "Generating new salt"); + furi_hal_random_fill_buf(&crypto_settings->salt[0], CRYPTO_SALT_LENGTH); } - memcpy(&crypto_settings->iv[0], &crypto_settings->base_iv[0], CRYPTO_IV_LENGTH); - const uint8_t* device_uid = get_device_uid(); uint8_t device_uid_length = get_device_uid_length(); @@ -125,16 +126,20 @@ CryptoSeedIVResult totp_crypto_seed_iv_v2( memcpy(hmac_key + device_uid_length, pin, pin_length); } - uint8_t hmac[HMAC_SHA512_RESULT_SIZE] = {0}; - int hmac_result_code = hmac_sha512( - hmac_key, hmac_key_length, &crypto_settings->base_iv[0], CRYPTO_IV_LENGTH, &hmac[0]); + uint8_t hmac[WC_SHA512_DIGEST_SIZE] = {0}; + + Hmac hmac_context; + wc_HmacSetKey(&hmac_context, WC_SHA512, hmac_key, hmac_key_length); + wc_HmacUpdate(&hmac_context, &crypto_settings->salt[0], CRYPTO_SALT_LENGTH); + int hmac_result_code = wc_HmacFinal(&hmac_context, &hmac[0]); + wc_HmacFree(&hmac_context); memset_s(hmac_key, hmac_key_length, 0, hmac_key_length); free(hmac_key); if(hmac_result_code == 0) { uint8_t offset = - hmac[HMAC_SHA512_RESULT_SIZE - 1] % (HMAC_SHA512_RESULT_SIZE - CRYPTO_IV_LENGTH - 1); + hmac[WC_SHA512_DIGEST_SIZE - 1] % (WC_SHA512_DIGEST_SIZE - CRYPTO_IV_LENGTH - 1); memcpy(&crypto_settings->iv[0], &hmac[offset], CRYPTO_IV_LENGTH); result = CryptoSeedIVResultFlagSuccess; @@ -181,4 +186,5 @@ bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings) { free(decrypted_key); return key_valid; -} \ No newline at end of file +} +#endif diff --git a/applications/external/totp/services/crypto/crypto_v2.h b/applications/external/totp/services/crypto/crypto_v2.h index 1395e7046..c21edae20 100644 --- a/applications/external/totp/services/crypto/crypto_v2.h +++ b/applications/external/totp/services/crypto/crypto_v2.h @@ -1,5 +1,7 @@ #pragma once +#include "../../config/app/config.h" +#ifdef TOTP_OBSOLETE_CRYPTO_V2_COMPATIBILITY_ENABLED #include #include #include @@ -49,4 +51,5 @@ CryptoSeedIVResult * @param crypto_settings crypto settings * @return \c true if cryptographic information is valid; \c false otherwise */ -bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings); \ No newline at end of file +bool totp_crypto_verify_key_v2(const CryptoSettings* crypto_settings); +#endif diff --git a/applications/external/totp/services/crypto/crypto_v3.c b/applications/external/totp/services/crypto/crypto_v3.c new file mode 100644 index 000000000..79ecb3d72 --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v3.c @@ -0,0 +1,195 @@ +#include "crypto_v3.h" +#include +#include +#include +#include +#include +#include "../../types/common.h" +#include "../../config/wolfssl/config.h" +#include +#include +#include "memset_s.h" +#include "constants.h" +#include "polyfills.h" + +#define CRYPTO_ALIGNMENT_FACTOR (16) +#define PBKDF2_ITERATIONS_COUNT (200) + +static const uint8_t* get_device_uid() { + return (const uint8_t*)UID64_BASE; //-V566 +} + +static uint8_t get_device_uid_length() { + return furi_hal_version_uid_size(); +} + +static const uint8_t* get_crypto_verify_key() { + return get_device_uid(); +} + +static uint8_t get_crypto_verify_key_length() { + return get_device_uid_length(); +} + +uint8_t* totp_crypto_encrypt_v3( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length) { + uint8_t* encrypted_data; + size_t remain = plain_data_length % CRYPTO_ALIGNMENT_FACTOR; + if(remain) { + size_t plain_data_aligned_length = plain_data_length - remain + CRYPTO_ALIGNMENT_FACTOR; + uint8_t* plain_data_aligned = malloc(plain_data_aligned_length); + furi_check(plain_data_aligned != NULL); + memset(plain_data_aligned, 0, plain_data_aligned_length); + memcpy(plain_data_aligned, plain_data, plain_data_length); + + encrypted_data = malloc(plain_data_aligned_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_aligned_length; + + furi_check( + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Encryption failed: enclave_load_key"); + furi_check( + furi_hal_crypto_encrypt(plain_data_aligned, encrypted_data, plain_data_aligned_length), + "Encryption failed: encrypt"); + furi_check( + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Encryption failed: enclave_unload_key"); + + memset_s(plain_data_aligned, plain_data_aligned_length, 0, plain_data_aligned_length); + free(plain_data_aligned); + } else { + encrypted_data = malloc(plain_data_length); + furi_check(encrypted_data != NULL); + *encrypted_data_length = plain_data_length; + + furi_check( + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Encryption failed: enclave_load_key"); + furi_check( + furi_hal_crypto_encrypt(plain_data, encrypted_data, plain_data_length), + "Encryption failed: encrypt"); + furi_check( + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Encryption failed: enclave_unload_key"); + } + + return encrypted_data; +} + +uint8_t* totp_crypto_decrypt_v3( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length) { + *decrypted_data_length = encrypted_data_length; + uint8_t* decrypted_data = malloc(*decrypted_data_length); + furi_check(decrypted_data != NULL); + furi_check( + furi_hal_crypto_enclave_load_key(crypto_settings->crypto_key_slot, crypto_settings->iv), + "Decryption failed: enclave_load_key"); + furi_check( + furi_hal_crypto_decrypt(encrypted_data, decrypted_data, encrypted_data_length), + "Decryption failed: decrypt"); + furi_check( + furi_hal_crypto_enclave_unload_key(crypto_settings->crypto_key_slot), + "Decryption failed: enclave_unload_key"); + return decrypted_data; +} + +CryptoSeedIVResult totp_crypto_seed_iv_v3( + CryptoSettings* crypto_settings, + const uint8_t* pin, + uint8_t pin_length) { + CryptoSeedIVResult result; + if(crypto_settings->crypto_verify_data == NULL) { + FURI_LOG_I(LOGGING_TAG, "Generating new salt"); + furi_hal_random_fill_buf(&crypto_settings->salt[0], CRYPTO_SALT_LENGTH); + } + + const uint8_t* device_uid = get_device_uid(); + uint8_t device_uid_length = get_device_uid_length(); + + uint8_t pbkdf_key_length = device_uid_length; + if(pin != NULL && pin_length > 0) { + pbkdf_key_length += pin_length; + } + + uint8_t* pbkdf_key = malloc(pbkdf_key_length); + furi_check(pbkdf_key != NULL); + + memcpy(pbkdf_key, device_uid, device_uid_length); + + if(pin != NULL && pin_length > 0) { + memcpy(pbkdf_key + device_uid_length, pin, pin_length); + } + + uint8_t pbkdf_output[WC_SHA512_DIGEST_SIZE] = {0}; + + int pbkdf_result_code = wc_PBKDF2( + &pbkdf_output[0], + pbkdf_key, + pbkdf_key_length, + &crypto_settings->salt[0], + CRYPTO_SALT_LENGTH, + PBKDF2_ITERATIONS_COUNT, + WC_SHA512_DIGEST_SIZE, + WC_SHA512); + + memset_s(pbkdf_key, pbkdf_key_length, 0, pbkdf_key_length); + free(pbkdf_key); + + if(pbkdf_result_code == 0) { + uint8_t offset = pbkdf_output[WC_SHA512_DIGEST_SIZE - 1] % + (WC_SHA512_DIGEST_SIZE - CRYPTO_IV_LENGTH - 1); + memcpy(&crypto_settings->iv[0], &pbkdf_output[offset], CRYPTO_IV_LENGTH); + result = CryptoSeedIVResultFlagSuccess; + if(crypto_settings->crypto_verify_data == NULL) { + const uint8_t* crypto_vkey = get_crypto_verify_key(); + uint8_t crypto_vkey_length = get_crypto_verify_key_length(); + FURI_LOG_I(LOGGING_TAG, "Generating crypto verify data"); + crypto_settings->crypto_verify_data = malloc(crypto_vkey_length); + furi_check(crypto_settings->crypto_verify_data != NULL); + crypto_settings->crypto_verify_data_length = crypto_vkey_length; + + crypto_settings->crypto_verify_data = totp_crypto_encrypt_v3( + crypto_vkey, + crypto_vkey_length, + crypto_settings, + &crypto_settings->crypto_verify_data_length); + + crypto_settings->pin_required = pin != NULL && pin_length > 0; + + result |= CryptoSeedIVResultFlagNewCryptoVerifyData; + } + } else { + result = CryptoSeedIVResultFailed; + } + + memset_s(&pbkdf_output[0], WC_SHA512_DIGEST_SIZE, 0, WC_SHA512_DIGEST_SIZE); + + return result; +} + +bool totp_crypto_verify_key_v3(const CryptoSettings* crypto_settings) { + size_t decrypted_key_length; + uint8_t* decrypted_key = totp_crypto_decrypt_v3( + crypto_settings->crypto_verify_data, + crypto_settings->crypto_verify_data_length, + crypto_settings, + &decrypted_key_length); + + const uint8_t* crypto_vkey = get_crypto_verify_key(); + uint8_t crypto_vkey_length = get_crypto_verify_key_length(); + bool key_valid = true; + for(uint8_t i = 0; i < crypto_vkey_length && key_valid; i++) { + if(decrypted_key[i] != crypto_vkey[i]) key_valid = false; + } + + free(decrypted_key); + + return key_valid; +} \ No newline at end of file diff --git a/applications/external/totp/services/crypto/crypto_v3.h b/applications/external/totp/services/crypto/crypto_v3.h new file mode 100644 index 000000000..39c718aab --- /dev/null +++ b/applications/external/totp/services/crypto/crypto_v3.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include "../../types/crypto_settings.h" +#include "common_types.h" + +/** + * @brief Encrypts plain data using built-in certificate and given initialization vector (IV) + * @param plain_data plain data to be encrypted + * @param plain_data_length plain data length + * @param crypto_settings crypto settings + * @param[out] encrypted_data_length encrypted data length + * @return Encrypted data + */ +uint8_t* totp_crypto_encrypt_v3( + const uint8_t* plain_data, + const size_t plain_data_length, + const CryptoSettings* crypto_settings, + size_t* encrypted_data_length); + +/** + * @brief Decrypts encrypted data using built-in certificate and given initialization vector (IV) + * @param encrypted_data encrypted data to be decrypted + * @param encrypted_data_length encrypted data length + * @param crypto_settings crypto settings + * @param[out] decrypted_data_length decrypted data length + * @return Decrypted data + */ +uint8_t* totp_crypto_decrypt_v3( + const uint8_t* encrypted_data, + const size_t encrypted_data_length, + const CryptoSettings* crypto_settings, + size_t* decrypted_data_length); + +/** + * @brief Seed initialization vector (IV) using user's PIN + * @param crypto_settings crypto settings + * @param pin user's PIN + * @param pin_length user's PIN length + * @return Results of seeding IV + */ +CryptoSeedIVResult + totp_crypto_seed_iv_v3(CryptoSettings* crypto_settings, const uint8_t* pin, uint8_t pin_length); + +/** + * @brief Verifies whether cryptographic information (certificate + IV) is valid and can be used for encryption and decryption + * @param crypto_settings crypto settings + * @return \c true if cryptographic information is valid; \c false otherwise + */ +bool totp_crypto_verify_key_v3(const CryptoSettings* crypto_settings); \ No newline at end of file diff --git a/applications/external/totp/services/crypto/polyfills.h b/applications/external/totp/services/crypto/polyfills.h new file mode 100644 index 000000000..6e66d03d2 --- /dev/null +++ b/applications/external/totp/services/crypto/polyfills.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#ifndef FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START + +// FW Crypto API is outdated, let's polyfill it +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_START (12u) +#define FURI_HAL_CRYPTO_ENCLAVE_USER_KEY_SLOT_END (100u) +#define furi_hal_crypto_enclave_ensure_key furi_hal_crypto_verify_key +#define furi_hal_crypto_enclave_load_key furi_hal_crypto_store_load_key +#define furi_hal_crypto_enclave_unload_key furi_hal_crypto_store_unload_key + +#endif \ No newline at end of file diff --git a/applications/external/totp/services/hmac/byteswap.c b/applications/external/totp/services/hmac/byteswap.c deleted file mode 100644 index e922deec0..000000000 --- a/applications/external/totp/services/hmac/byteswap.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "byteswap.h" - -uint32_t swap_uint32(uint32_t val) { - val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF); - return (val << 16) | (val >> 16); -} - -uint64_t swap_uint64(uint64_t val) { - val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL); - val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL); - return (val << 32) | (val >> 32); -} diff --git a/applications/external/totp/services/hmac/byteswap.h b/applications/external/totp/services/hmac/byteswap.h deleted file mode 100644 index 2e3f1743f..000000000 --- a/applications/external/totp/services/hmac/byteswap.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -/** - * @brief Swap bytes in 32-bit value - * @param val value to swap bytes in - * @return Value with bytes swapped - */ -uint32_t swap_uint32(uint32_t val); - -/** - * @brief Swap bytes in 64-bit value - * @param val value to swap bytes in - * @return Value with bytes swapped - */ -uint64_t swap_uint64(uint64_t val); diff --git a/applications/external/totp/services/hmac/hmac_common.h b/applications/external/totp/services/hmac/hmac_common.h deleted file mode 100644 index 3499cb800..000000000 --- a/applications/external/totp/services/hmac/hmac_common.h +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include "memxor.h" - -#define IPAD 0x36 -#define OPAD 0x5c - -/* Concatenate two preprocessor tokens. */ -#define _GLHMAC_CONCAT_(prefix, suffix) prefix##suffix -#define _GLHMAC_CONCAT(prefix, suffix) _GLHMAC_CONCAT_(prefix, suffix) - -#define HMAC_ALG _GLHMAC_CONCAT(sha, GL_HMAC_NAME) - -#define GL_HMAC_CTX _GLHMAC_CONCAT(HMAC_ALG, _ctx) -#define GL_HMAC_FN _GLHMAC_CONCAT(hmac_, HMAC_ALG) -#define GL_HMAC_FN_INIT _GLHMAC_CONCAT(HMAC_ALG, _init_ctx) -#define GL_HMAC_FN_BLOC _GLHMAC_CONCAT(HMAC_ALG, _process_block) -#define GL_HMAC_FN_PROC _GLHMAC_CONCAT(HMAC_ALG, _process_bytes) -#define GL_HMAC_FN_FINI _GLHMAC_CONCAT(HMAC_ALG, _finish_ctx) - -static void - hmac_hash(const void* key, size_t keylen, const void* in, size_t inlen, int pad, void* resbuf) { - struct GL_HMAC_CTX hmac_ctx; - char block[GL_HMAC_BLOCKSIZE]; - - memset(block, pad, sizeof block); - memxor(block, key, keylen); - - GL_HMAC_FN_INIT(&hmac_ctx); - GL_HMAC_FN_BLOC(block, sizeof block, &hmac_ctx); - GL_HMAC_FN_PROC(in, inlen, &hmac_ctx); - GL_HMAC_FN_FINI(&hmac_ctx, resbuf); -} - -int GL_HMAC_FN(const void* key, size_t keylen, const void* in, size_t inlen, void* resbuf) { - char optkeybuf[GL_HMAC_HASHSIZE]; - char innerhash[GL_HMAC_HASHSIZE]; - - /* Ensure key size is <= block size. */ - if(keylen > GL_HMAC_BLOCKSIZE) { - struct GL_HMAC_CTX keyhash; - - GL_HMAC_FN_INIT(&keyhash); - GL_HMAC_FN_PROC(key, keylen, &keyhash); - GL_HMAC_FN_FINI(&keyhash, optkeybuf); - - key = optkeybuf; - /* zero padding of the key to the block size - is implicit in the memxor. */ - keylen = sizeof optkeybuf; - } - - /* Compute INNERHASH from KEY and IN. */ - hmac_hash(key, keylen, in, inlen, IPAD, innerhash); - - /* Compute result from KEY and INNERHASH. */ - hmac_hash(key, keylen, innerhash, sizeof innerhash, OPAD, resbuf); - - return 0; -} diff --git a/applications/external/totp/services/hmac/hmac_sha1.c b/applications/external/totp/services/hmac/hmac_sha1.c deleted file mode 100644 index 0a78d569a..000000000 --- a/applications/external/totp/services/hmac/hmac_sha1.c +++ /dev/null @@ -1,24 +0,0 @@ -/* hmac-sha1.c -- hashed message authentication codes - Copyright (C) 2018-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include "hmac_sha1.h" - -#include "sha1.h" - -#define GL_HMAC_NAME 1 -#define GL_HMAC_BLOCKSIZE 64 -#define GL_HMAC_HASHSIZE 20 -#include "hmac_common.h" diff --git a/applications/external/totp/services/hmac/hmac_sha1.h b/applications/external/totp/services/hmac/hmac_sha1.h deleted file mode 100644 index 25ff2f648..000000000 --- a/applications/external/totp/services/hmac/hmac_sha1.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#define HMAC_SHA1_RESULT_SIZE 20 - -/* Compute Hashed Message Authentication Code with SHA-1, over BUFFER - data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the - output to pre-allocated 20 byte minimum RESBUF buffer. Return 0 on - success. */ -int hmac_sha1(const void* key, size_t keylen, const void* in, size_t inlen, void* restrict resbuf); diff --git a/applications/external/totp/services/hmac/hmac_sha256.c b/applications/external/totp/services/hmac/hmac_sha256.c deleted file mode 100644 index 00ac2a177..000000000 --- a/applications/external/totp/services/hmac/hmac_sha256.c +++ /dev/null @@ -1,24 +0,0 @@ -/* hmac-sha256.c -- hashed message authentication codes - Copyright (C) 2018-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include "hmac_sha256.h" -#include "sha256.h" - -#define GL_HMAC_NAME 256 -#define GL_HMAC_BLOCKSIZE 64 -#define GL_HMAC_HASHSIZE 32 - -#include "hmac_common.h" diff --git a/applications/external/totp/services/hmac/hmac_sha256.h b/applications/external/totp/services/hmac/hmac_sha256.h deleted file mode 100644 index 9aeaf10d6..000000000 --- a/applications/external/totp/services/hmac/hmac_sha256.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#define HMAC_SHA256_RESULT_SIZE 32 - -/* Compute Hashed Message Authentication Code with SHA-256, over BUFFER - data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the - output to pre-allocated 32 byte minimum RESBUF buffer. Return 0 on - success. */ -int hmac_sha256(const void* key, size_t keylen, const void* in, size_t inlen, void* restrict resbuf); diff --git a/applications/external/totp/services/hmac/hmac_sha512.c b/applications/external/totp/services/hmac/hmac_sha512.c deleted file mode 100644 index dc9342a91..000000000 --- a/applications/external/totp/services/hmac/hmac_sha512.c +++ /dev/null @@ -1,24 +0,0 @@ -/* hmac-sha512.c -- hashed message authentication codes - Copyright (C) 2018-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include "hmac_sha512.h" - -#include "sha512.h" - -#define GL_HMAC_NAME 512 -#define GL_HMAC_BLOCKSIZE 128 -#define GL_HMAC_HASHSIZE 64 -#include "hmac_common.h" diff --git a/applications/external/totp/services/hmac/hmac_sha512.h b/applications/external/totp/services/hmac/hmac_sha512.h deleted file mode 100644 index 712b7f4a0..000000000 --- a/applications/external/totp/services/hmac/hmac_sha512.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#define HMAC_SHA512_RESULT_SIZE 64 - -/* Compute Hashed Message Authentication Code with SHA-512, over BUFFER - data of BUFLEN bytes using the KEY of KEYLEN bytes, writing the - output to pre-allocated 64 byte minimum RESBUF buffer. Return 0 on - success. */ -int hmac_sha512(const void* key, size_t keylen, const void* in, size_t inlen, void* restrict resbuf); diff --git a/applications/external/totp/services/hmac/memxor.c b/applications/external/totp/services/hmac/memxor.c deleted file mode 100644 index ab6026aa3..000000000 --- a/applications/external/totp/services/hmac/memxor.c +++ /dev/null @@ -1,30 +0,0 @@ -/* memxor.c -- perform binary exclusive OR operation of two memory blocks. - Copyright (C) 2005, 2006 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/* Written by Simon Josefsson. The interface was inspired by memxor - in Niels Möller's Nettle. */ - -#include "memxor.h" - -void* memxor(void* /*restrict*/ dest, const void* /*restrict*/ src, size_t n) { - char const* s = (char const*)src; - char* d = (char*)dest; - - for(; n > 0; n--) *d++ ^= *s++; - - return dest; -} diff --git a/applications/external/totp/services/hmac/memxor.h b/applications/external/totp/services/hmac/memxor.h deleted file mode 100644 index 71aa604c8..000000000 --- a/applications/external/totp/services/hmac/memxor.h +++ /dev/null @@ -1,28 +0,0 @@ -/* memxor.h -- perform binary exclusive OR operation on memory blocks. - Copyright (C) 2005 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/* Written by Simon Josefsson. The interface was inspired by memxor - in Niels Möller's Nettle. */ - -#pragma once - -#include - -/* Compute binary exclusive OR of memory areas DEST and SRC, putting - the result in DEST, of length N bytes. Returns a pointer to - DEST. */ -void* memxor(void* /*restrict*/ dest, const void* /*restrict*/ src, size_t n); diff --git a/applications/external/totp/services/hmac/sha1.c b/applications/external/totp/services/hmac/sha1.c deleted file mode 100644 index 29f22e3c3..000000000 --- a/applications/external/totp/services/hmac/sha1.c +++ /dev/null @@ -1,251 +0,0 @@ -/* sha1.c - Functions to compute SHA1 message digest of files or - memory blocks according to the NIST specification FIPS-180-1. - - Copyright (C) 2000-2001, 2003-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by Scott G. Miller - Credits: - Robert Klep -- Expansion function fix -*/ - -/* Specification. */ -#include "sha1.h" - -#include -#include - -#include "sha_pad_buffer.h" - -#ifdef WORDS_BIGENDIAN -#define SWAP(n) (n) -#else -#include "byteswap.h" -#define SWAP(n) swap_uint32(n) -#endif - -/* Take a pointer to a 160 bit block of data (five 32 bit ints) and - initialize it to the start constants of the SHA1 algorithm. This - must be called before using hash in the call to sha1_hash. */ -void sha1_init_ctx(struct sha1_ctx* ctx) { - ctx->A = 0x67452301; - ctx->B = 0xefcdab89; - ctx->C = 0x98badcfe; - ctx->D = 0x10325476; - ctx->E = 0xc3d2e1f0; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -/* Copy the 4 byte value from v into the memory location pointed to by *cp, - If your architecture allows unaligned access this is equivalent to - * (uint32_t *) cp = v */ -static void set_uint32(char* cp, uint32_t v) { - memcpy(cp, &v, sizeof v); -} - -/* Put result from CTX in first 20 bytes following RESBUF. The result - must be in little endian byte order. */ -void* sha1_read_ctx(const struct sha1_ctx* ctx, void* resbuf) { - char* r = resbuf; - set_uint32(r + 0 * sizeof ctx->A, SWAP(ctx->A)); - set_uint32(r + 1 * sizeof ctx->B, SWAP(ctx->B)); - set_uint32(r + 2 * sizeof ctx->C, SWAP(ctx->C)); - set_uint32(r + 3 * sizeof ctx->D, SWAP(ctx->D)); - set_uint32(r + 4 * sizeof ctx->E, SWAP(ctx->E)); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. */ -void* sha1_finish_ctx(struct sha1_ctx* ctx, void* resbuf) { - /* Take yet unprocessed bytes into account. */ - uint32_t bytes = ctx->buflen; - size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; - - /* Now count remaining bytes. */ - ctx->total[0] += bytes; - if(ctx->total[0] < bytes) ++ctx->total[1]; - - /* Put the 64-bit file length in *bits* at the end of the buffer. */ - ctx->buffer[size - 2] = SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29)); - ctx->buffer[size - 1] = SWAP(ctx->total[0] << 3); - - sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); - - /* Process last bytes. */ - sha1_process_block(ctx->buffer, size * 4, ctx); - - return sha1_read_ctx(ctx, resbuf); -} - -/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void* sha1_buffer(const char* buffer, size_t len, void* resblock) { - struct sha1_ctx ctx; - - /* Initialize the computation context. */ - sha1_init_ctx(&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - sha1_process_bytes(buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha1_finish_ctx(&ctx, resblock); -} - -void sha1_process_bytes(const void* buffer, size_t len, struct sha1_ctx* ctx) { - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if(ctx->buflen != 0) { - size_t left_over = ctx->buflen; - size_t add = 128 - left_over > len ? len : 128 - left_over; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, add); - ctx->buflen += add; - - if(ctx->buflen > 64) { - sha1_process_block(ctx->buffer, ctx->buflen & ~63, ctx); - - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap, - because ctx->buflen < 64 ≤ (left_over + add) & ~63. */ - memcpy(ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~63], ctx->buflen); - } - - buffer = (const char*)buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if(len >= 64) { -#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned) -#define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(uint32_t) != 0) - if(UNALIGNED_P(buffer)) - while(len > 64) { - sha1_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); //-V1086 - buffer = (const char*)buffer + 64; - len -= 64; - } - else -#endif - { - sha1_process_block(buffer, len & ~63, ctx); - buffer = (const char*)buffer + (len & ~63); - len &= 63; - } - } - - /* Move remaining bytes in internal buffer. */ - if(len > 0) { - size_t left_over = ctx->buflen; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, len); - left_over += len; - if(left_over >= 64) { - sha1_process_block(ctx->buffer, 64, ctx); - left_over -= 64; - /* The regions in the following copy operation cannot overlap, - because left_over ≤ 64. */ - memcpy(ctx->buffer, &ctx->buffer[16], left_over); - } - ctx->buflen = left_over; - } -} - -/* --- Code below is the primary difference between md5.c and sha1.c --- */ - -/* SHA1 round constants */ -static const int sha1_round_constants[4] = {0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6}; - -/* Round functions. Note that F2 is the same as F4. */ -#define F1(B, C, D) (D ^ (B & (C ^ D))) -#define F2_4(B, C, D) (B ^ C ^ D) -#define F3(B, C, D) ((B & C) | (D & (B | C))) -#define FN(I, B, C, D) (I == 0 ? F1(B, C, D) : (I == 2 ? F3(B, C, D) : F2_4(B, C, D))) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 64 == 0. - Most of this code comes from GnuPG's cipher/sha1.c. */ - -void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx) { - const uint32_t* words = buffer; - size_t nwords = len / sizeof(uint32_t); - const uint32_t* endp = words + nwords; - uint32_t x[16]; - uint32_t a = ctx->A; - uint32_t b = ctx->B; - uint32_t c = ctx->C; - uint32_t d = ctx->D; - uint32_t e = ctx->E; - uint32_t lolen = len; - - /* First increment the byte count. RFC 1321 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += lolen; - ctx->total[1] += (len >> 31 >> 1) + (ctx->total[0] < lolen); - -#define rol(x, n) (((x) << (n)) | ((uint32_t)(x) >> (32 - (n)))) - -#define M(I) \ - (tm = x[I & 0x0f] ^ x[(I - 14) & 0x0f] ^ x[(I - 8) & 0x0f] ^ x[(I - 3) & 0x0f], \ - (x[I & 0x0f] = rol(tm, 1))) - -#define R(A, B, C, D, E, F, K, M, KI) \ - do { \ - E += rol(A, 5) + F(KI, B, C, D) + K + M; \ - B = rol(B, 30); \ - } while(0) - - while(words < endp) { - uint32_t tm; - int t; - for(t = 0; t < 16; t++) { - x[t] = SWAP(*words); - words++; - } - - for(uint8_t i = 0; i < 80; i++) { - uint32_t m = i < 16 ? x[i] : M(i); - uint8_t ki = i / 20; - int k_const = sha1_round_constants[ki]; - R(a, b, c, d, e, FN, k_const, m, ki); - uint32_t tt = a; - a = e; - e = d; - d = c; - c = b; - b = tt; - } - - a = ctx->A += a; - b = ctx->B += b; - c = ctx->C += c; - d = ctx->D += d; - e = ctx->E += e; - } -} - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha1.h b/applications/external/totp/services/hmac/sha1.h deleted file mode 100644 index e9eb7712a..000000000 --- a/applications/external/totp/services/hmac/sha1.h +++ /dev/null @@ -1,84 +0,0 @@ -/* Declarations of functions and data types used for SHA1 sum - library functions. - Copyright (C) 2000-2001, 2003, 2005-2006, 2008-2022 Free Software - Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define SHA1_DIGEST_SIZE 20 - -/* Structure to save state of computation between the single steps. */ -struct sha1_ctx { - uint32_t A; - uint32_t B; - uint32_t C; - uint32_t D; - uint32_t E; - - uint32_t total[2]; - uint32_t buflen; /* ≥ 0, ≤ 128 */ - uint32_t buffer[32]; /* 128 bytes; the first buflen bytes are in use */ -}; - -/* Initialize structure containing state of computation. */ -extern void sha1_init_ctx(struct sha1_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 64!!! */ -extern void sha1_process_block(const void* buffer, size_t len, struct sha1_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 64. */ -extern void sha1_process_bytes(const void* buffer, size_t len, struct sha1_ctx* ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 20 bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ -extern void* sha1_finish_ctx(struct sha1_ctx* ctx, void* restrict resbuf); - -/* Put result from CTX in first 20 bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. */ -extern void* sha1_read_ctx(const struct sha1_ctx* ctx, void* restrict resbuf); - -/* Compute SHA1 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void* sha1_buffer(const char* buffer, size_t len, void* restrict resblock); - -#ifdef __cplusplus -} -#endif - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha256.c b/applications/external/totp/services/hmac/sha256.c deleted file mode 100644 index 09ba272e7..000000000 --- a/applications/external/totp/services/hmac/sha256.c +++ /dev/null @@ -1,283 +0,0 @@ -/* sha256.c - Functions to compute SHA256 message digest of files or - memory blocks according to the NIST specification FIPS-180-2. - - Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by David Madore, considerably copypasting from - Scott G. Miller's sha1.c -*/ - -/* Specification. */ -#include "sha256.h" - -#include -#include -#include "sha_pad_buffer.h" - -#ifdef WORDS_BIGENDIAN -#define SWAP(n) (n) -#else -#include "byteswap.h" -#define SWAP(n) swap_uint32(n) -#endif - -/* - Takes a pointer to a 256 bit block of data (eight 32 bit ints) and - initializes it to the start constants of the SHA256 algorithm. This - must be called before using hash in the call to sha256_hash -*/ -void sha256_init_ctx(struct sha256_ctx* ctx) { - ctx->state[0] = 0x6a09e667UL; - ctx->state[1] = 0xbb67ae85UL; - ctx->state[2] = 0x3c6ef372UL; - ctx->state[3] = 0xa54ff53aUL; - ctx->state[4] = 0x510e527fUL; - ctx->state[5] = 0x9b05688cUL; - ctx->state[6] = 0x1f83d9abUL; - ctx->state[7] = 0x5be0cd19UL; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -/* Copy the value from v into the memory location pointed to by *CP, - If your architecture allows unaligned access, this is equivalent to - * (__typeof__ (v) *) cp = v */ -static void set_uint32(char* cp, uint32_t v) { - memcpy(cp, &v, sizeof v); -} - -/* Put result from CTX in first 32 bytes following RESBUF. - The result must be in little endian byte order. */ -void* sha256_read_ctx(const struct sha256_ctx* ctx, void* resbuf) { - int i; - char* r = resbuf; - - for(i = 0; i < 8; i++) set_uint32(r + i * sizeof ctx->state[0], SWAP(ctx->state[i])); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. */ -static void sha256_conclude_ctx(struct sha256_ctx* ctx) { - /* Take yet unprocessed bytes into account. */ - size_t bytes = ctx->buflen; - size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; - - /* Now count remaining bytes. */ - ctx->total[0] += bytes; - if(ctx->total[0] < bytes) ++ctx->total[1]; - - /* Put the 64-bit file length in *bits* at the end of the buffer. - Use set_uint32 rather than a simple assignment, to avoid risk of - unaligned access. */ - set_uint32((char*)&ctx->buffer[size - 2], SWAP((ctx->total[1] << 3) | (ctx->total[0] >> 29))); - set_uint32((char*)&ctx->buffer[size - 1], SWAP(ctx->total[0] << 3)); - - sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 4 - bytes); - - /* Process last bytes. */ - sha256_process_block(ctx->buffer, size * 4, ctx); -} - -void* sha256_finish_ctx(struct sha256_ctx* ctx, void* resbuf) { - sha256_conclude_ctx(ctx); - return sha256_read_ctx(ctx, resbuf); -} - -/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void* sha256_buffer(const char* buffer, size_t len, void* resblock) { - struct sha256_ctx ctx; - - /* Initialize the computation context. */ - sha256_init_ctx(&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - sha256_process_bytes(buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha256_finish_ctx(&ctx, resblock); -} - -void sha256_process_bytes(const void* buffer, size_t len, struct sha256_ctx* ctx) { - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if(ctx->buflen != 0) { - size_t left_over = ctx->buflen; - size_t add = 128 - left_over > len ? len : 128 - left_over; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, add); - ctx->buflen += add; - - if(ctx->buflen > 64) { - sha256_process_block(ctx->buffer, ctx->buflen & ~63, ctx); - - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap, - because ctx->buflen < 64 ≤ (left_over + add) & ~63. */ - memcpy(ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~63], ctx->buflen); - } - - buffer = (const char*)buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if(len >= 64) { -#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned) -#define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(uint32_t) != 0) - if(UNALIGNED_P(buffer)) - while(len > 64) { - sha256_process_block(memcpy(ctx->buffer, buffer, 64), 64, ctx); //-V1086 - buffer = (const char*)buffer + 64; - len -= 64; - } - else -#endif - { - sha256_process_block(buffer, len & ~63, ctx); - buffer = (const char*)buffer + (len & ~63); - len &= 63; - } - } - - /* Move remaining bytes in internal buffer. */ - if(len > 0) { - size_t left_over = ctx->buflen; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, len); - left_over += len; - if(left_over >= 64) { - sha256_process_block(ctx->buffer, 64, ctx); - left_over -= 64; - /* The regions in the following copy operation cannot overlap, - because left_over ≤ 64. */ - memcpy(ctx->buffer, &ctx->buffer[16], left_over); - } - ctx->buflen = left_over; - } -} - -/* --- Code below is the primary difference between sha1.c and sha256.c --- */ - -/* SHA256 round constants */ -#define K(I) sha256_round_constants[I] -static const uint32_t sha256_round_constants[64] = { - 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL, - 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, - 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, - 0x0fc19dc6UL, 0x240ca1ccUL, 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, - 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, - 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, - 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, 0xa2bfe8a1UL, 0xa81a664bUL, - 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, - 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, - 0x5b9cca4fUL, 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, - 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL, -}; - -/* Round functions. */ -#define F2(A, B, C) ((A & B) | (C & (A | B))) -#define F1(E, F, G) (G ^ (E & (F ^ G))) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 64 == 0. - Most of this code comes from GnuPG's cipher/sha1.c. */ - -void sha256_process_block(const void* buffer, size_t len, struct sha256_ctx* ctx) { - const uint32_t* words = buffer; - size_t nwords = len / sizeof(uint32_t); - const uint32_t* endp = words + nwords; - uint32_t x[16]; - uint32_t a = ctx->state[0]; - uint32_t b = ctx->state[1]; - uint32_t c = ctx->state[2]; - uint32_t d = ctx->state[3]; - uint32_t e = ctx->state[4]; - uint32_t f = ctx->state[5]; - uint32_t g = ctx->state[6]; - uint32_t h = ctx->state[7]; - uint32_t lolen = len; - - /* First increment the byte count. FIPS PUB 180-2 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += lolen; - ctx->total[1] += (len >> 31 >> 1) + (ctx->total[0] < lolen); - -#define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) -#define S0(x) (rol(x, 25) ^ rol(x, 14) ^ (x >> 3)) -#define S1(x) (rol(x, 15) ^ rol(x, 13) ^ (x >> 10)) -#define SS0(x) (rol(x, 30) ^ rol(x, 19) ^ rol(x, 10)) -#define SS1(x) (rol(x, 26) ^ rol(x, 21) ^ rol(x, 7)) - -#define M(I) \ - (tm = S1(x[(I - 2) & 0x0f]) + x[(I - 7) & 0x0f] + S0(x[(I - 15) & 0x0f]) + x[I & 0x0f], \ - x[I & 0x0f] = tm) - -#define R(A, B, C, D, E, F, G, H, K, M) \ - do { \ - t0 = SS0(A) + F2(A, B, C); \ - t1 = H + SS1(E) + F1(E, F, G) + K + M; \ - D += t1; \ - H = t0 + t1; \ - } while(0) - - while(words < endp) { - uint32_t tm; - uint32_t t0, t1; - int t; - /* FIXME: see sha1.c for a better implementation. */ - for(t = 0; t < 16; t++) { - x[t] = SWAP(*words); - words++; - } - - for(int i = 0; i < 64; i++) { - uint32_t xx = i < 16 ? x[i] : M(i); - R(a, b, c, d, e, f, g, h, K(i), xx); - uint32_t tt = a; - a = h; - h = g; - g = f; - f = e; - e = d; - d = c; - c = b; - b = tt; - } - - a = ctx->state[0] += a; - b = ctx->state[1] += b; - c = ctx->state[2] += c; - d = ctx->state[3] += d; - e = ctx->state[4] += e; - f = ctx->state[5] += f; - g = ctx->state[6] += g; - h = ctx->state[7] += h; - } -} - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha256.h b/applications/external/totp/services/hmac/sha256.h deleted file mode 100644 index 964f2eb97..000000000 --- a/applications/external/totp/services/hmac/sha256.h +++ /dev/null @@ -1,79 +0,0 @@ -/* Declarations of functions and data types used for SHA256 sum - library functions. - Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -enum { SHA256_DIGEST_SIZE = 256 / 8 }; - -/* Structure to save state of computation between the single steps. */ -struct sha256_ctx { - uint32_t state[8]; - - uint32_t total[2]; - size_t buflen; /* ≥ 0, ≤ 128 */ - uint32_t buffer[32]; /* 128 bytes; the first buflen bytes are in use */ -}; - -/* Initialize structure containing state of computation. */ -extern void sha256_init_ctx(struct sha256_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 64!!! */ -extern void sha256_process_block(const void* buffer, size_t len, struct sha256_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 64. */ -extern void sha256_process_bytes(const void* buffer, size_t len, struct sha256_ctx* ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 32 (28) bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ -extern void* sha256_finish_ctx(struct sha256_ctx* ctx, void* restrict resbuf); - -/* Put result from CTX in first 32 (28) bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. */ -extern void* sha256_read_ctx(const struct sha256_ctx* ctx, void* restrict resbuf); - -/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. - The result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void* sha256_buffer(const char* buffer, size_t len, void* restrict resblock); - -#ifdef __cplusplus -} -#endif - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha512.c b/applications/external/totp/services/hmac/sha512.c deleted file mode 100644 index ffe2864fb..000000000 --- a/applications/external/totp/services/hmac/sha512.c +++ /dev/null @@ -1,309 +0,0 @@ -/* sha512.c - Functions to compute SHA512 message digest of files or - memory blocks according to the NIST specification FIPS-180-2. - - Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by David Madore, considerably copypasting from - Scott G. Miller's sha1.c -*/ - -/* Specification. */ -#include "sha512.h" - -#include -#include - -#include "byteswap.h" -#include "sha_pad_buffer.h" - -#define SWAP(n) swap_uint64(n) - -/* - Takes a pointer to a 512 bit block of data (eight 64 bit ints) and - initializes it to the start constants of the SHA512 algorithm. This - must be called before using hash in the call to sha512_hash -*/ -void sha512_init_ctx(struct sha512_ctx* ctx) { - ctx->state[0] = u64hilo(0x6a09e667, 0xf3bcc908); - ctx->state[1] = u64hilo(0xbb67ae85, 0x84caa73b); - ctx->state[2] = u64hilo(0x3c6ef372, 0xfe94f82b); - ctx->state[3] = u64hilo(0xa54ff53a, 0x5f1d36f1); - ctx->state[4] = u64hilo(0x510e527f, 0xade682d1); - ctx->state[5] = u64hilo(0x9b05688c, 0x2b3e6c1f); - ctx->state[6] = u64hilo(0x1f83d9ab, 0xfb41bd6b); - ctx->state[7] = u64hilo(0x5be0cd19, 0x137e2179); - - ctx->total[0] = ctx->total[1] = u64lo(0); - ctx->buflen = 0; -} - -/* Copy the value from V into the memory location pointed to by *CP, - If your architecture allows unaligned access, this is equivalent to - * (__typeof__ (v) *) cp = v */ -static void set_uint64(char* cp, u64 v) { - memcpy(cp, &v, sizeof v); -} - -/* Put result from CTX in first 64 bytes following RESBUF. - The result must be in little endian byte order. */ -void* sha512_read_ctx(const struct sha512_ctx* ctx, void* resbuf) { - int i; - char* r = resbuf; - - for(i = 0; i < 8; i++) set_uint64(r + i * sizeof ctx->state[0], SWAP(ctx->state[i])); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. */ -static void sha512_conclude_ctx(struct sha512_ctx* ctx) { - /* Take yet unprocessed bytes into account. */ - size_t bytes = ctx->buflen; - size_t size = (bytes < 112) ? 128 / 8 : 128 * 2 / 8; - - /* Now count remaining bytes. */ - ctx->total[0] = u64plus(ctx->total[0], u64lo(bytes)); - if(u64lt(ctx->total[0], u64lo(bytes))) ctx->total[1] = u64plus(ctx->total[1], u64lo(1)); - - /* Put the 128-bit file length in *bits* at the end of the buffer. - Use set_uint64 rather than a simple assignment, to avoid risk of - unaligned access. */ - set_uint64( - (char*)&ctx->buffer[size - 2], - SWAP(u64or(u64shl(ctx->total[1], 3), u64shr(ctx->total[0], 61)))); - set_uint64((char*)&ctx->buffer[size - 1], SWAP(u64shl(ctx->total[0], 3))); - - sha_pad_buffer(&((uint8_t*)ctx->buffer)[bytes], (size - 2) * 8 - bytes); - - /* Process last bytes. */ - sha512_process_block(ctx->buffer, size * 8, ctx); -} - -void* sha512_finish_ctx(struct sha512_ctx* ctx, void* resbuf) { - sha512_conclude_ctx(ctx); - return sha512_read_ctx(ctx, resbuf); -} - -/* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void* sha512_buffer(const char* buffer, size_t len, void* resblock) { - struct sha512_ctx ctx; - - /* Initialize the computation context. */ - sha512_init_ctx(&ctx); - - /* Process whole buffer but last len % 128 bytes. */ - sha512_process_bytes(buffer, len, &ctx); - - /* Put result in desired memory area. */ - return sha512_finish_ctx(&ctx, resblock); -} - -void sha512_process_bytes(const void* buffer, size_t len, struct sha512_ctx* ctx) { - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if(ctx->buflen != 0) { - size_t left_over = ctx->buflen; - size_t add = 256 - left_over > len ? len : 256 - left_over; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, add); - ctx->buflen += add; - - if(ctx->buflen > 128) { - sha512_process_block(ctx->buffer, ctx->buflen & ~127, ctx); - - ctx->buflen &= 127; - /* The regions in the following copy operation cannot overlap, - because ctx->buflen < 128 ≤ (left_over + add) & ~127. */ - memcpy(ctx->buffer, &((char*)ctx->buffer)[(left_over + add) & ~127], ctx->buflen); - } - - buffer = (const char*)buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if(len >= 128) { -#if !(_STRING_ARCH_unaligned || _STRING_INLINE_unaligned) -#define UNALIGNED_P(p) ((uintptr_t)(p) % sizeof(u64) != 0) - if(UNALIGNED_P(buffer)) - while(len > 128) { - sha512_process_block(memcpy(ctx->buffer, buffer, 128), 128, ctx); //-V1086 - buffer = (const char*)buffer + 128; - len -= 128; - } - else -#endif - { - sha512_process_block(buffer, len & ~127, ctx); - buffer = (const char*)buffer + (len & ~127); - len &= 127; - } - } - - /* Move remaining bytes in internal buffer. */ - if(len > 0) { - size_t left_over = ctx->buflen; - - memcpy(&((char*)ctx->buffer)[left_over], buffer, len); - left_over += len; - if(left_over >= 128) { - sha512_process_block(ctx->buffer, 128, ctx); - left_over -= 128; - /* The regions in the following copy operation cannot overlap, - because left_over ≤ 128. */ - memcpy(ctx->buffer, &ctx->buffer[16], left_over); - } - ctx->buflen = left_over; - } -} - -/* --- Code below is the primary difference between sha1.c and sha512.c --- */ - -/* SHA512 round constants */ -#define K(I) sha512_round_constants[I] -static u64 const sha512_round_constants[80] = { - u64init(0x428a2f98, 0xd728ae22), u64init(0x71374491, 0x23ef65cd), - u64init(0xb5c0fbcf, 0xec4d3b2f), u64init(0xe9b5dba5, 0x8189dbbc), - u64init(0x3956c25b, 0xf348b538), u64init(0x59f111f1, 0xb605d019), - u64init(0x923f82a4, 0xaf194f9b), u64init(0xab1c5ed5, 0xda6d8118), - u64init(0xd807aa98, 0xa3030242), u64init(0x12835b01, 0x45706fbe), - u64init(0x243185be, 0x4ee4b28c), u64init(0x550c7dc3, 0xd5ffb4e2), - u64init(0x72be5d74, 0xf27b896f), u64init(0x80deb1fe, 0x3b1696b1), - u64init(0x9bdc06a7, 0x25c71235), u64init(0xc19bf174, 0xcf692694), - u64init(0xe49b69c1, 0x9ef14ad2), u64init(0xefbe4786, 0x384f25e3), - u64init(0x0fc19dc6, 0x8b8cd5b5), u64init(0x240ca1cc, 0x77ac9c65), - u64init(0x2de92c6f, 0x592b0275), u64init(0x4a7484aa, 0x6ea6e483), - u64init(0x5cb0a9dc, 0xbd41fbd4), u64init(0x76f988da, 0x831153b5), - u64init(0x983e5152, 0xee66dfab), u64init(0xa831c66d, 0x2db43210), - u64init(0xb00327c8, 0x98fb213f), u64init(0xbf597fc7, 0xbeef0ee4), - u64init(0xc6e00bf3, 0x3da88fc2), u64init(0xd5a79147, 0x930aa725), - u64init(0x06ca6351, 0xe003826f), u64init(0x14292967, 0x0a0e6e70), - u64init(0x27b70a85, 0x46d22ffc), u64init(0x2e1b2138, 0x5c26c926), - u64init(0x4d2c6dfc, 0x5ac42aed), u64init(0x53380d13, 0x9d95b3df), - u64init(0x650a7354, 0x8baf63de), u64init(0x766a0abb, 0x3c77b2a8), - u64init(0x81c2c92e, 0x47edaee6), u64init(0x92722c85, 0x1482353b), - u64init(0xa2bfe8a1, 0x4cf10364), u64init(0xa81a664b, 0xbc423001), - u64init(0xc24b8b70, 0xd0f89791), u64init(0xc76c51a3, 0x0654be30), - u64init(0xd192e819, 0xd6ef5218), u64init(0xd6990624, 0x5565a910), - u64init(0xf40e3585, 0x5771202a), u64init(0x106aa070, 0x32bbd1b8), - u64init(0x19a4c116, 0xb8d2d0c8), u64init(0x1e376c08, 0x5141ab53), - u64init(0x2748774c, 0xdf8eeb99), u64init(0x34b0bcb5, 0xe19b48a8), - u64init(0x391c0cb3, 0xc5c95a63), u64init(0x4ed8aa4a, 0xe3418acb), - u64init(0x5b9cca4f, 0x7763e373), u64init(0x682e6ff3, 0xd6b2b8a3), - u64init(0x748f82ee, 0x5defb2fc), u64init(0x78a5636f, 0x43172f60), - u64init(0x84c87814, 0xa1f0ab72), u64init(0x8cc70208, 0x1a6439ec), - u64init(0x90befffa, 0x23631e28), u64init(0xa4506ceb, 0xde82bde9), - u64init(0xbef9a3f7, 0xb2c67915), u64init(0xc67178f2, 0xe372532b), - u64init(0xca273ece, 0xea26619c), u64init(0xd186b8c7, 0x21c0c207), - u64init(0xeada7dd6, 0xcde0eb1e), u64init(0xf57d4f7f, 0xee6ed178), - u64init(0x06f067aa, 0x72176fba), u64init(0x0a637dc5, 0xa2c898a6), - u64init(0x113f9804, 0xbef90dae), u64init(0x1b710b35, 0x131c471b), - u64init(0x28db77f5, 0x23047d84), u64init(0x32caab7b, 0x40c72493), - u64init(0x3c9ebe0a, 0x15c9bebc), u64init(0x431d67c4, 0x9c100d4c), - u64init(0x4cc5d4be, 0xcb3e42b6), u64init(0x597f299c, 0xfc657e2a), - u64init(0x5fcb6fab, 0x3ad6faec), u64init(0x6c44198c, 0x4a475817), -}; - -/* Round functions. */ -#define F2(A, B, C) u64or(u64and(A, B), u64and(C, u64or(A, B))) -#define F1(E, F, G) u64xor(G, u64and(E, u64xor(F, G))) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 128 == 0. - Most of this code comes from GnuPG's cipher/sha1.c. */ - -void sha512_process_block(const void* buffer, size_t len, struct sha512_ctx* ctx) { - u64 const* words = buffer; - u64 const* endp = words + len / sizeof(u64); - u64 x[16]; - u64 a = ctx->state[0]; - u64 b = ctx->state[1]; - u64 c = ctx->state[2]; - u64 d = ctx->state[3]; - u64 e = ctx->state[4]; - u64 f = ctx->state[5]; - u64 g = ctx->state[6]; - u64 h = ctx->state[7]; - u64 lolen = u64size(len); - - /* First increment the byte count. FIPS PUB 180-2 specifies the possible - length of the file up to 2^128 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] = u64plus(ctx->total[0], lolen); - ctx->total[1] = u64plus( - ctx->total[1], u64plus(u64size(len >> 31 >> 31 >> 2), u64lo(u64lt(ctx->total[0], lolen)))); - -#define S0(x) u64xor(u64rol(x, 63), u64xor(u64rol(x, 56), u64shr(x, 7))) -#define S1(x) u64xor(u64rol(x, 45), u64xor(u64rol(x, 3), u64shr(x, 6))) -#define SS0(x) u64xor(u64rol(x, 36), u64xor(u64rol(x, 30), u64rol(x, 25))) -#define SS1(x) u64xor(u64rol(x, 50), u64xor(u64rol(x, 46), u64rol(x, 23))) - -#define M(I) \ - (x[(I)&15] = u64plus( \ - x[(I)&15], \ - u64plus(S1(x[((I)-2) & 15]), u64plus(x[((I)-7) & 15], S0(x[((I)-15) & 15]))))) - -#define R(A, B, C, D, E, F, G, H, K, M) \ - do { \ - u64 t0 = u64plus(SS0(A), F2(A, B, C)); \ - u64 t1 = u64plus(H, u64plus(SS1(E), u64plus(F1(E, F, G), u64plus(K, M)))); \ - D = u64plus(D, t1); \ - H = u64plus(t0, t1); \ - } while(0) - - while(words < endp) { - int t; - /* FIXME: see sha1.c for a better implementation. */ - for(t = 0; t < 16; t++) { - x[t] = SWAP(*words); - words++; - } - - for(int i = 0; i < 80; i++) { - u64 xx = i < 16 ? x[i] : M(i); - R(a, b, c, d, e, f, g, h, K(i), xx); - u64 tt = a; - a = h; - h = g; - g = f; - f = e; - e = d; - d = c; - c = b; - b = tt; - } - - a = ctx->state[0] = u64plus(ctx->state[0], a); - b = ctx->state[1] = u64plus(ctx->state[1], b); - c = ctx->state[2] = u64plus(ctx->state[2], c); - d = ctx->state[3] = u64plus(ctx->state[3], d); - e = ctx->state[4] = u64plus(ctx->state[4], e); - f = ctx->state[5] = u64plus(ctx->state[5], f); - g = ctx->state[6] = u64plus(ctx->state[6], g); - h = ctx->state[7] = u64plus(ctx->state[7], h); - } -} - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha512.h b/applications/external/totp/services/hmac/sha512.h deleted file mode 100644 index 38590829e..000000000 --- a/applications/external/totp/services/hmac/sha512.h +++ /dev/null @@ -1,82 +0,0 @@ -/* Declarations of functions and data types used for SHA512 and SHA384 sum - library functions. - Copyright (C) 2005-2006, 2008-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#pragma once - -#include -#include "u64.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum { SHA512_DIGEST_SIZE = 512 / 8 }; - -/* Structure to save state of computation between the single steps. */ -struct sha512_ctx { - u64 state[8]; - - u64 total[2]; - size_t buflen; /* ≥ 0, ≤ 256 */ - u64 buffer[32]; /* 256 bytes; the first buflen bytes are in use */ -}; - -/* Initialize structure containing state of computation. */ -extern void sha512_init_ctx(struct sha512_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 128!!! */ -extern void sha512_process_block(const void* buffer, size_t len, struct sha512_ctx* ctx); - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 128. */ -extern void sha512_process_bytes(const void* buffer, size_t len, struct sha512_ctx* ctx); - -/* Process the remaining bytes in the buffer and put result from CTX - in first 64 (48) bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ -extern void* sha512_finish_ctx(struct sha512_ctx* ctx, void* restrict resbuf); - -/* Put result from CTX in first 64 (48) bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -extern void* sha512_read_ctx(const struct sha512_ctx* ctx, void* restrict resbuf); - -/* Compute SHA512 message digest for LEN bytes beginning at BUFFER. - The result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void* sha512_buffer(const char* buffer, size_t len, void* restrict resblock); - -#ifdef __cplusplus -} -#endif - -/* - * Hey Emacs! - * Local Variables: - * coding: utf-8 - * End: - */ diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.c b/applications/external/totp/services/hmac/sha_pad_buffer.c deleted file mode 100644 index badedbcc7..000000000 --- a/applications/external/totp/services/hmac/sha_pad_buffer.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "sha_pad_buffer.h" -#include - -void sha_pad_buffer(uint8_t* buffer, size_t size) { - if(size > 0) { - buffer[0] = 0x80; - if(size > 1) { - memset(&buffer[1], 0, size - 1); - } - } -} \ No newline at end of file diff --git a/applications/external/totp/services/hmac/sha_pad_buffer.h b/applications/external/totp/services/hmac/sha_pad_buffer.h deleted file mode 100644 index 7dba40fa9..000000000 --- a/applications/external/totp/services/hmac/sha_pad_buffer.h +++ /dev/null @@ -1,4 +0,0 @@ -#include -#include - -void sha_pad_buffer(uint8_t* buffer, size_t size); \ No newline at end of file diff --git a/applications/external/totp/services/hmac/u64.h b/applications/external/totp/services/hmac/u64.h deleted file mode 100644 index 91e42e6c9..000000000 --- a/applications/external/totp/services/hmac/u64.h +++ /dev/null @@ -1,44 +0,0 @@ -/* uint64_t-like operations that work even on hosts lacking uint64_t - - Copyright (C) 2006, 2009-2022 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by Paul Eggert. */ - -#pragma once - -#include - -#ifndef _GL_U64_INLINE -#define _GL_U64_INLINE _GL_INLINE -#endif - -/* Return X rotated left by N bits, where 0 < N < 64. */ -#define u64rol(x, n) u64or(u64shl(x, n), u64shr(x, 64 - (n))) - -/* Native implementations are trivial. See below for comments on what - these operations do. */ -typedef uint64_t u64; -#define u64hilo(hi, lo) ((u64)(((u64)(hi) << 32) + (lo))) -#define u64init(hi, lo) u64hilo(hi, lo) -#define u64lo(x) ((u64)(x)) -#define u64size(x) u64lo(x) -#define u64lt(x, y) ((x) < (y)) -#define u64and(x, y) ((x) & (y)) -#define u64or(x, y) ((x) | (y)) -#define u64xor(x, y) ((x) ^ (y)) -#define u64plus(x, y) ((x) + (y)) -#define u64shl(x, n) ((x) << (n)) -#define u64shr(x, n) ((x) >> (n)) diff --git a/applications/external/totp/services/totp/totp.c b/applications/external/totp/services/totp/totp.c index 45a283b06..32232f21a 100644 --- a/applications/external/totp/services/totp/totp.c +++ b/applications/external/totp/services/totp/totp.c @@ -4,12 +4,16 @@ #include #include #include -#include "../hmac/hmac_sha1.h" -#include "../hmac/hmac_sha256.h" -#include "../hmac/hmac_sha512.h" -#include "../hmac/byteswap.h" +#include "../../config/wolfssl/config.h" +#include -#define HMAC_MAX_RESULT_SIZE HMAC_SHA512_RESULT_SIZE +#define HMAC_MAX_RESULT_SIZE WC_SHA512_DIGEST_SIZE + +static uint64_t swap_uint64(uint64_t val) { + val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL); + val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL); + return (val << 32) | (val >> 32); +} /** * @brief Generates the timeblock for a time in seconds. @@ -68,14 +72,34 @@ uint64_t totp_at( algo, plain_secret, plain_secret_length, totp_timecode(interval, for_time_adjusted)); } +static int totp_algo_common( + int type, + const uint8_t* key, + size_t key_length, + const uint8_t* input, + size_t input_length, + uint8_t* output) { + Hmac hmac; + int ret = wc_HmacSetKey(&hmac, type, key, key_length); + if(ret == 0) { + ret = wc_HmacUpdate(&hmac, input, input_length); + } + + if(ret == 0) { + ret = wc_HmacFinal(&hmac, output); + } + + wc_HmacFree(&hmac); + return ret == 0 ? wc_HmacSizeByType(type) : 0; +} + static int totp_algo_sha1( const uint8_t* key, size_t key_length, const uint8_t* input, size_t input_length, uint8_t* output) { - hmac_sha1(key, key_length, input, input_length, output); - return HMAC_SHA1_RESULT_SIZE; + return totp_algo_common(WC_SHA, key, key_length, input, input_length, output); } static int totp_algo_sha256( @@ -84,8 +108,7 @@ static int totp_algo_sha256( const uint8_t* input, size_t input_length, uint8_t* output) { - hmac_sha256(key, key_length, input, input_length, output); - return HMAC_SHA256_RESULT_SIZE; + return totp_algo_common(WC_SHA256, key, key_length, input, input_length, output); } static int totp_algo_sha512( @@ -94,8 +117,7 @@ static int totp_algo_sha512( const uint8_t* input, size_t input_length, uint8_t* output) { - hmac_sha512(key, key_length, input, input_length, output); - return HMAC_SHA512_RESULT_SIZE; + return totp_algo_common(WC_SHA512, key, key_length, input, input_length, output); } const TOTP_ALGO TOTP_ALGO_SHA1 = (TOTP_ALGO)(&totp_algo_sha1); diff --git a/applications/external/totp/totp_app.c b/applications/external/totp/totp_app.c index 251d7bb5b..adfd4a9a5 100644 --- a/applications/external/totp/totp_app.c +++ b/applications/external/totp/totp_app.c @@ -5,7 +5,7 @@ #include #include #include -#include "features_config.h" +#include "config/app/config.h" #include "services/config/config.h" #include "types/plugin_state.h" #include "types/token_info.h" @@ -18,6 +18,7 @@ #include "services/crypto/crypto_facade.h" #include "cli/cli.h" #include "version.h" +#include struct TotpRenderCallbackContext { FuriMutex* mutex; @@ -218,6 +219,8 @@ int32_t totp_app() { TOTP_APP_VERSION_MAJOR, TOTP_APP_VERSION_MINOR, TOTP_APP_VERSION_PATCH); + FURI_LOG_I(LOGGING_TAG, "WolfSSL version: " LIBWOLFSSL_VERSION_STRING); + PluginState* plugin_state = malloc(sizeof(PluginState)); furi_check(plugin_state != NULL); diff --git a/applications/external/totp/types/automation_method.h b/applications/external/totp/types/automation_method.h index 89e39e18e..24c85ee76 100644 --- a/applications/external/totp/types/automation_method.h +++ b/applications/external/totp/types/automation_method.h @@ -1,6 +1,6 @@ #pragma once -#include "../features_config.h" +#include "../config/app/config.h" typedef uint8_t AutomationMethod; diff --git a/applications/external/totp/types/crypto_settings.h b/applications/external/totp/types/crypto_settings.h index 8b970fd6d..22daf147e 100644 --- a/applications/external/totp/types/crypto_settings.h +++ b/applications/external/totp/types/crypto_settings.h @@ -20,9 +20,9 @@ typedef struct { uint8_t iv[CRYPTO_IV_LENGTH]; /** - * @brief Basic randomly-generated initialization vector (IV) + * @brief Randomly-generated salt */ - uint8_t base_iv[CRYPTO_IV_LENGTH]; + uint8_t salt[CRYPTO_SALT_LENGTH]; /** * @brief Encrypted well-known data diff --git a/applications/external/totp/types/plugin_state.h b/applications/external/totp/types/plugin_state.h index a3b2f5c8f..388da5edf 100644 --- a/applications/external/totp/types/plugin_state.h +++ b/applications/external/totp/types/plugin_state.h @@ -3,7 +3,7 @@ #include #include #include -#include "../features_config.h" +#include "../config/app/config.h" #include "../ui/totp_scenes_enum.h" #include "../services/config/config_file_context.h" #include "../services/idle_timeout/idle_timeout.h" diff --git a/applications/external/totp/ui/scene_director.c b/applications/external/totp/ui/scene_director.c index fd7cf1dca..cddbd7136 100644 --- a/applications/external/totp/ui/scene_director.c +++ b/applications/external/totp/ui/scene_director.c @@ -1,8 +1,11 @@ #include "../types/common.h" +#include "../config/app/config.h" #include "scene_director.h" #include "scenes/authenticate/totp_scene_authenticate.h" #include "scenes/generate_token/totp_scene_generate_token.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include "scenes/add_new_token/totp_scene_add_new_token.h" +#endif #include "scenes/token_menu/totp_scene_token_menu.h" #include "scenes/app_settings/totp_app_settings.h" #include "scenes/standby/standby.h" @@ -16,9 +19,11 @@ void totp_scene_director_activate_scene(PluginState* const plugin_state, Scene s case TotpSceneAuthentication: totp_scene_authenticate_activate(plugin_state); break; +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED case TotpSceneAddNewToken: totp_scene_add_new_token_activate(plugin_state); break; +#endif case TotpSceneTokenMenu: totp_scene_token_menu_activate(plugin_state); break; @@ -45,9 +50,11 @@ void totp_scene_director_deactivate_active_scene(PluginState* const plugin_state case TotpSceneAuthentication: totp_scene_authenticate_deactivate(plugin_state); break; +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED case TotpSceneAddNewToken: totp_scene_add_new_token_deactivate(plugin_state); break; +#endif case TotpSceneTokenMenu: totp_scene_token_menu_deactivate(plugin_state); break; @@ -70,9 +77,11 @@ void totp_scene_director_render(Canvas* const canvas, PluginState* const plugin_ case TotpSceneAuthentication: totp_scene_authenticate_render(canvas, plugin_state); break; +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED case TotpSceneAddNewToken: totp_scene_add_new_token_render(canvas, plugin_state); break; +#endif case TotpSceneTokenMenu: totp_scene_token_menu_render(canvas, plugin_state); break; @@ -98,9 +107,11 @@ bool totp_scene_director_handle_event(PluginEvent* const event, PluginState* con case TotpSceneAuthentication: processing = totp_scene_authenticate_handle_event(event, plugin_state); break; +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED case TotpSceneAddNewToken: processing = totp_scene_add_new_token_handle_event(event, plugin_state); break; +#endif case TotpSceneTokenMenu: processing = totp_scene_token_menu_handle_event(event, plugin_state); break; diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c index 95aa4fbaa..389d651e7 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.c @@ -1,5 +1,6 @@ #include "totp_input_text.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include #include @@ -50,3 +51,4 @@ void totp_input_text(Gui* gui, const char* header_text, InputTextResult* result) view_dispatcher_free(view_dispatcher); text_input_free(text_input); } +#endif diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h index 7ec116a9b..89980ad35 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_input_text.h @@ -1,5 +1,7 @@ #pragma once +#include "../../../config/app/config.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include #define INPUT_BUFFER_SIZE (255) @@ -11,3 +13,4 @@ typedef struct { } InputTextResult; void totp_input_text(Gui* gui, const char* header_text, InputTextResult* result); +#endif diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c index 6ae288d4c..ae0a7bd48 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c @@ -1,4 +1,5 @@ #include "totp_scene_add_new_token.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include "../../../types/common.h" #include "../../constants.h" #include "../../scene_director.h" @@ -8,7 +9,6 @@ #include "../../ui_controls.h" #include "../../common_dialogs.h" #include -#include "../generate_token/totp_scene_generate_token.h" char* TOKEN_ALGO_LIST[] = {"SHA1", "SHA256", "SHA512", "Steam"}; char* TOKEN_DIGITS_TEXT_LIST[] = {"5 digits", "6 digits", "8 digits"}; @@ -318,3 +318,4 @@ void totp_scene_add_new_token_deactivate(PluginState* plugin_state) { free(plugin_state->current_scene_state); plugin_state->current_scene_state = NULL; } +#endif diff --git a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h index 7297869d0..686cbe37c 100644 --- a/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h +++ b/applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h @@ -1,5 +1,7 @@ #pragma once +#include "../../../config/app/config.h" +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED #include #include "../../../types/plugin_state.h" #include "../../../types/plugin_event.h" @@ -10,3 +12,4 @@ bool totp_scene_add_new_token_handle_event( const PluginEvent* const event, PluginState* plugin_state); void totp_scene_add_new_token_deactivate(PluginState* plugin_state); +#endif diff --git a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c index 33dcd5b43..252829432 100644 --- a/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c +++ b/applications/external/totp/ui/scenes/app_settings/totp_app_settings.c @@ -6,12 +6,11 @@ #include "../../ui_controls.h" #include "../../common_dialogs.h" #include "../../scene_director.h" -#include "../token_menu/totp_scene_token_menu.h" #include "../../constants.h" #include "../../../services/config/config.h" #include "../../../services/convert/convert.h" #include -#include "../../../features_config.h" +#include "../../../config/app/config.h" #ifdef TOTP_BADBT_AUTOMATION_ENABLED #include "../../../workers/bt_type_code/bt_type_code.h" #endif diff --git a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c index de1c0685f..b769d364d 100644 --- a/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -1,18 +1,17 @@ +#include "totp_scene_generate_token.h" #include #include #include #include #include #include -#include "totp_scene_generate_token.h" #include "../../canvas_extensions.h" #include "../../../types/token_info.h" #include "../../../types/common.h" #include "../../constants.h" #include "../../../services/config/config.h" #include "../../scene_director.h" -#include "../token_menu/totp_scene_token_menu.h" -#include "../../../features_config.h" +#include "../../../config/app/config.h" #include "../../../workers/generate_totp_code/generate_totp_code.h" #include "../../../workers/usb_type_code/usb_type_code.h" #ifdef TOTP_BADBT_AUTOMATION_ENABLED @@ -246,7 +245,7 @@ void totp_scene_generate_token_render(Canvas* const canvas, PluginState* plugin_ SCREEN_HEIGHT_CENTER + 10, AlignCenter, AlignCenter, - "Press OK button to add"); + "Press OK button to access menu"); return; } diff --git a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c index 1340b5a8e..7eb4ea87c 100644 --- a/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c +++ b/applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c @@ -7,9 +7,7 @@ #include "../../scene_director.h" #include "../../../services/config/config.h" #include "../../../types/token_info.h" -#include "../generate_token/totp_scene_generate_token.h" -#include "../add_new_token/totp_scene_add_new_token.h" -#include "../app_settings/totp_app_settings.h" +#include "../../../config/app/config.h" #include #define SCREEN_HEIGHT_THIRD (SCREEN_HEIGHT / 3) @@ -130,7 +128,22 @@ bool totp_scene_token_menu_handle_event(const PluginEvent* const event, PluginSt } else if(event->input.type == InputTypeRelease && event->input.key == InputKeyOk) { switch(scene_state->selected_control) { case AddNewToken: { +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED totp_scene_director_activate_scene(plugin_state, TotpSceneAddNewToken); +#else + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_buttons(message, "Back", NULL, NULL); + dialog_message_set_header(message, "Information", 0, 0, AlignLeft, AlignTop); + dialog_message_set_text( + message, + "Read here\nhttps://t.ly/8ZOtj\n how to add new token", + SCREEN_WIDTH_CENTER, + SCREEN_HEIGHT_CENTER, + AlignCenter, + AlignCenter); + dialog_message_show(plugin_state->dialogs_app, message); + dialog_message_free(message); +#endif break; } case DeleteToken: { diff --git a/applications/external/totp/ui/totp_scenes_enum.h b/applications/external/totp/ui/totp_scenes_enum.h index 4624eddd9..fa7c427dc 100644 --- a/applications/external/totp/ui/totp_scenes_enum.h +++ b/applications/external/totp/ui/totp_scenes_enum.h @@ -1,5 +1,7 @@ #pragma once +#include "../config/app/config.h" + typedef uint8_t Scene; /** @@ -21,10 +23,12 @@ enum Scenes { */ TotpSceneGenerateToken, +#ifdef TOTP_UI_ADD_NEW_TOKEN_ENABLED /** * @brief Scene where user can add new token */ TotpSceneAddNewToken, +#endif /** * @brief Scene with a menu for given token, allowing user to do multiple actions diff --git a/applications/external/totp/version.h b/applications/external/totp/version.h index e6925b600..8b77b9cd3 100644 --- a/applications/external/totp/version.h +++ b/applications/external/totp/version.h @@ -1,5 +1,5 @@ #pragma once -#define TOTP_APP_VERSION_MAJOR (3) -#define TOTP_APP_VERSION_MINOR (2) -#define TOTP_APP_VERSION_PATCH (0) \ No newline at end of file +#define TOTP_APP_VERSION_MAJOR (4) +#define TOTP_APP_VERSION_MINOR (0) +#define TOTP_APP_VERSION_PATCH (1) \ No newline at end of file diff --git a/applications/external/totp/workers/bt_type_code/bt_type_code.c b/applications/external/totp/workers/bt_type_code/bt_type_code.c index 16dfcc0e8..54e355321 100644 --- a/applications/external/totp/workers/bt_type_code/bt_type_code.c +++ b/applications/external/totp/workers/bt_type_code/bt_type_code.c @@ -11,7 +11,7 @@ #include "../../types/common.h" #include "../../types/token_info.h" #include "../type_code_common.h" -#include "../../features_config.h" +#include "../../config/app/config.h" #include "../../services/config/constants.h" #if TOTP_TARGET_FIRMWARE == TOTP_FIRMWARE_XTREME_UL From 4164932044799fdf61f18dda237762b7100a3d10 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 11 Aug 2023 23:23:06 +0300 Subject: [PATCH 28/99] remove --- firmware/targets/f18/api_symbols.csv | 1 - firmware/targets/f7/api_symbols.csv | 1 - 2 files changed, 2 deletions(-) diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index d3c240d13..792e44e59 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1470,7 +1470,6 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 83cc20452..8a9b3a430 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1678,7 +1678,6 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,-,gui_active_view_port_count,size_t,"Gui*, GuiLayer" Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" From 773a5e382fd4f89d5e2d878e4f00c219b18820fd Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 12 Aug 2023 04:43:23 +0300 Subject: [PATCH 29/99] remove base apps pack --- .gitmodules | 12 - applications/external/application.fam | 6 - .../external/arkanoid/application.fam | 14 - .../external/arkanoid/arkanoid_10px.png | Bin 1622 -> 0 bytes .../external/arkanoid/arkanoid_game.c | 479 --- .../avr_isp_programmer/application.fam | 17 - .../avr_isp_programmer/avr_app_icon_10x10.png | Bin 3614 -> 0 bytes .../external/avr_isp_programmer/avr_isp_app.c | 179 - .../avr_isp_programmer/avr_isp_app_i.c | 31 - .../avr_isp_programmer/avr_isp_app_i.h | 44 - .../avr_isp_programmer/helpers/avr_isp.c | 496 --- .../avr_isp_programmer/helpers/avr_isp.h | 70 - .../helpers/avr_isp_event.h | 23 - .../helpers/avr_isp_types.h | 32 - .../helpers/avr_isp_worker.c | 266 -- .../helpers/avr_isp_worker.h | 49 - .../helpers/avr_isp_worker_rw.c | 1157 ------ .../helpers/avr_isp_worker_rw.h | 99 - .../helpers/flipper_i32hex_file.c | 321 -- .../helpers/flipper_i32hex_file.h | 55 - .../images/avr_app_icon_10x10.png | Bin 3614 -> 0 bytes .../avr_isp_programmer/images/avr_wiring.png | Bin 4513 -> 0 bytes .../images/chif_not_found_83x37.png | Bin 3742 -> 0 bytes .../images/chip_error_70x22.png | Bin 3688 -> 0 bytes .../images/chip_long_70x22.png | Bin 3656 -> 0 bytes .../images/chip_not_found_83x37.png | Bin 3779 -> 0 bytes .../images/dolphin_nice_96x59.png | Bin 2459 -> 0 bytes .../images/isp_active_128x53.png | Bin 3961 -> 0 bytes .../images/link_waiting_77x56.png | Bin 3883 -> 0 bytes .../lib/driver/avr_isp_chip_arr.c | 386 -- .../lib/driver/avr_isp_chip_arr.h | 33 - .../lib/driver/avr_isp_prog.c | 639 ---- .../lib/driver/avr_isp_prog.h | 16 - .../lib/driver/avr_isp_prog_cmd.h | 97 - .../lib/driver/avr_isp_spi_sw.c | 71 - .../lib/driver/avr_isp_spi_sw.h | 24 - .../avr_isp_programmer/lib/driver/clock.png | Bin 3649 -> 0 bytes .../avr_isp_programmer/scenes/avr_isp_scene.c | 30 - .../avr_isp_programmer/scenes/avr_isp_scene.h | 29 - .../scenes/avr_isp_scene_about.c | 99 - .../scenes/avr_isp_scene_chip_detect.c | 72 - .../scenes/avr_isp_scene_config.h | 10 - .../scenes/avr_isp_scene_input_name.c | 89 - .../scenes/avr_isp_scene_load.c | 22 - .../scenes/avr_isp_scene_programmer.c | 28 - .../scenes/avr_isp_scene_reader.c | 64 - .../scenes/avr_isp_scene_start.c | 75 - .../scenes/avr_isp_scene_success.c | 44 - .../scenes/avr_isp_scene_wiring.c | 21 - .../scenes/avr_isp_scene_writer.c | 69 - .../views/avr_isp_view_chip_detect.c | 213 -- .../views/avr_isp_view_chip_detect.h | 32 - .../views/avr_isp_view_programmer.c | 134 - .../views/avr_isp_view_programmer.h | 27 - .../views/avr_isp_view_reader.c | 215 -- .../views/avr_isp_view_reader.h | 35 - .../views/avr_isp_view_writer.c | 268 -- .../views/avr_isp_view_writer.h | 37 - applications/external/bad_bt/application.fam | 16 - applications/external/bad_bt/bad_bt_app.c | 333 -- applications/external/bad_bt/bad_bt_app.h | 39 - .../external/bad_bt/helpers/ducky_script.c | 792 ---- .../external/bad_bt/helpers/ducky_script.h | 154 - .../bad_bt/helpers/ducky_script_commands.c | 201 -- .../external/bad_bt/helpers/ducky_script_i.h | 44 - .../bad_bt/helpers/ducky_script_keycodes.c | 78 - .../external/bad_bt/images/badbt_10px.png | Bin 576 -> 0 bytes .../external/bad_bt/scenes/bad_bt_scene.c | 30 - .../external/bad_bt/scenes/bad_bt_scene.h | 29 - .../bad_bt/scenes/bad_bt_scene_config.c | 104 - .../bad_bt/scenes/bad_bt_scene_config.h | 7 - .../scenes/bad_bt_scene_config_layout.c | 47 - .../bad_bt/scenes/bad_bt_scene_config_mac.c | 52 - .../bad_bt/scenes/bad_bt_scene_config_name.c | 45 - .../bad_bt/scenes/bad_bt_scene_error.c | 61 - .../bad_bt/scenes/bad_bt_scene_file_select.c | 49 - .../bad_bt/scenes/bad_bt_scene_work.c | 56 - .../external/bad_bt/views/bad_bt_view.c | 233 -- .../external/bad_bt/views/bad_bt_view.h | 29 - applications/external/barcode_gen/LICENSE | 22 - applications/external/barcode_gen/README.md | 88 - .../external/barcode_gen/application.fam | 16 - .../external/barcode_gen/barcode_app.c | 348 -- .../external/barcode_gen/barcode_app.h | 88 - .../codabar_encodings.txt | 22 - .../code128_encodings.txt | 202 -- .../code128c_encodings.txt | 106 - .../code39_encodings.txt | 44 - .../external/barcode_gen/barcode_utils.c | 147 - .../external/barcode_gen/barcode_utils.h | 55 - .../external/barcode_gen/barcode_validator.c | 532 --- .../external/barcode_gen/barcode_validator.h | 15 - applications/external/barcode_gen/encodings.c | 52 - applications/external/barcode_gen/encodings.h | 6 - .../barcode_gen/images/barcode_10.png | Bin 161 -> 0 bytes .../screenshots/Codabar Data Example.png | Bin 1672 -> 0 bytes .../screenshots/Creating Barcode.png | Bin 1681 -> 0 bytes .../screenshots/Flipper Barcode.png | Bin 1207 -> 0 bytes .../screenshots/Flipper Box Barcode.png | Bin 1372 -> 0 bytes .../external/barcode_gen/views/barcode_view.c | 510 --- .../external/barcode_gen/views/barcode_view.h | 23 - .../external/barcode_gen/views/create_view.c | 494 --- .../external/barcode_gen/views/create_view.h | 46 - .../external/barcode_gen/views/message_view.c | 66 - .../external/barcode_gen/views/message_view.h | 22 - .../external/blackjack/application.fam | 15 - .../external/blackjack/assets/blackjack.png | Bin 1324 -> 0 bytes .../blackjack/assets/card_graphics.png | Bin 409 -> 0 bytes .../external/blackjack/assets/endscreen.png | Bin 1241 -> 0 bytes applications/external/blackjack/blackjack.c | 633 ---- .../external/blackjack/blackjack_10px.png | Bin 119 -> 0 bytes applications/external/blackjack/common/card.c | 353 -- applications/external/blackjack/common/card.h | 192 - applications/external/blackjack/common/dml.c | 53 - applications/external/blackjack/common/dml.h | 116 - applications/external/blackjack/common/menu.c | 103 - applications/external/blackjack/common/menu.h | 77 - .../external/blackjack/common/queue.c | 69 - .../external/blackjack/common/queue.h | 70 - applications/external/blackjack/common/ui.c | 257 -- applications/external/blackjack/common/ui.h | 105 - applications/external/blackjack/defines.h | 77 - applications/external/blackjack/ui.c | 186 - applications/external/blackjack/ui.h | 18 - applications/external/blackjack/util.c | 123 - applications/external/blackjack/util.h | 7 - applications/external/bomberduck/LICENSE | 22 - applications/external/bomberduck/README.md | 2 - .../external/bomberduck/application.fam | 17 - .../external/bomberduck/assets/bomb0.png | Bin 4536 -> 0 bytes .../external/bomberduck/assets/bomb1.png | Bin 4529 -> 0 bytes .../external/bomberduck/assets/bomb2.png | Bin 4531 -> 0 bytes .../external/bomberduck/assets/box.png | Bin 4329 -> 0 bytes .../external/bomberduck/assets/end.png | Bin 4739 -> 0 bytes .../external/bomberduck/assets/enemy1.png | Bin 4757 -> 0 bytes .../external/bomberduck/assets/enemyleft.png | Bin 4761 -> 0 bytes .../external/bomberduck/assets/enemyright.png | Bin 4536 -> 0 bytes .../external/bomberduck/assets/explore.png | Bin 4540 -> 0 bytes .../external/bomberduck/assets/playerleft.png | Bin 4311 -> 0 bytes .../bomberduck/assets/playerright.png | Bin 4307 -> 0 bytes .../external/bomberduck/assets/unbreakbox.png | Bin 4763 -> 0 bytes applications/external/bomberduck/bomb.png | Bin 4534 -> 0 bytes applications/external/bomberduck/bomberduck.c | 647 ---- .../external/camera_suite/application.fam | 16 - .../external/camera_suite/camera_suite.c | 132 - .../external/camera_suite/camera_suite.h | 67 - .../external/camera_suite/docs/CHANGELOG.md | 20 - .../external/camera_suite/docs/README.md | 35 - .../helpers/camera_suite_custom_event.h | 67 - .../helpers/camera_suite_haptic.c | 35 - .../helpers/camera_suite_haptic.h | 7 - .../camera_suite/helpers/camera_suite_led.c | 38 - .../camera_suite/helpers/camera_suite_led.h | 3 - .../helpers/camera_suite_speaker.c | 26 - .../helpers/camera_suite_speaker.h | 5 - .../helpers/camera_suite_storage.c | 113 - .../helpers/camera_suite_storage.h | 20 - .../camera_suite/icons/camera_suite.png | Bin 2307 -> 0 bytes .../camera_suite/scenes/camera_suite_scene.c | 30 - .../camera_suite/scenes/camera_suite_scene.h | 29 - .../scenes/camera_suite_scene_camera.c | 52 - .../scenes/camera_suite_scene_config.h | 5 - .../scenes/camera_suite_scene_guide.c | 51 - .../scenes/camera_suite_scene_menu.c | 73 - .../scenes/camera_suite_scene_settings.c | 137 - .../scenes/camera_suite_scene_start.c | 55 - .../screenshots/camera_preview.png | Bin 3342 -> 0 bytes .../camera_suite/screenshots/guide.png | Bin 2233 -> 0 bytes .../camera_suite/screenshots/main_menu.png | Bin 1499 -> 0 bytes .../camera_suite/screenshots/settings.png | Bin 1900 -> 0 bytes .../camera_suite/screenshots/start_screen.png | Bin 1707 -> 0 bytes .../views/camera_suite_view_camera.c | 374 -- .../views/camera_suite_view_camera.h | 61 - .../views/camera_suite_view_guide.c | 120 - .../views/camera_suite_view_guide.h | 19 - .../views/camera_suite_view_start.c | 126 - .../views/camera_suite_view_start.h | 19 - applications/external/dap_link/README.md | 105 - .../external/dap_link/application.fam | 24 - applications/external/dap_link/dap_config.h | 234 -- applications/external/dap_link/dap_link.c | 527 --- applications/external/dap_link/dap_link.h | 55 - applications/external/dap_link/dap_link.png | Bin 143 -> 0 bytes applications/external/dap_link/gui/dap_gui.c | 92 - applications/external/dap_link/gui/dap_gui.h | 4 - .../dap_link/gui/dap_gui_custom_event.h | 7 - .../external/dap_link/gui/dap_gui_i.h | 34 - .../dap_link/gui/scenes/config/dap_scene.c | 30 - .../dap_link/gui/scenes/config/dap_scene.h | 29 - .../gui/scenes/config/dap_scene_config.h | 4 - .../dap_link/gui/scenes/dap_scene_about.c | 68 - .../dap_link/gui/scenes/dap_scene_config.c | 107 - .../dap_link/gui/scenes/dap_scene_help.c | 102 - .../dap_link/gui/scenes/dap_scene_main.c | 154 - .../dap_link/gui/views/dap_main_view.c | 189 - .../dap_link/gui/views/dap_main_view.h | 45 - .../dap_link/icons/ActiveConnection_50x64.png | Bin 3842 -> 0 bytes .../dap_link/icons/ArrowUpEmpty_12x18.png | Bin 159 -> 0 bytes .../dap_link/icons/ArrowUpFilled_12x18.png | Bin 173 -> 0 bytes applications/external/dap_link/lib/free-dap | 1 - .../external/dap_link/usb/dap_v2_usb.c | 977 ----- .../external/dap_link/usb/dap_v2_usb.h | 53 - .../external/dap_link/usb/usb_winusb.h | 143 - applications/external/doom/.gitignore | 4 - applications/external/doom/README.md | 10 - applications/external/doom/application.fam | 18 - applications/external/doom/assets.c | 331 -- applications/external/doom/assets.h | 108 - applications/external/doom/assets/door2.png | Bin 31942 -> 0 bytes .../external/doom/assets/door_inv.png | Bin 1068 -> 0 bytes .../external/doom/assets/fire_inv.png | Bin 583 -> 0 bytes .../external/doom/assets/fireball_inv.png | Bin 418 -> 0 bytes .../doom/assets/fireball_mask_inv.png | Bin 388 -> 0 bytes .../external/doom/assets/gradient_inv.png | Bin 1037 -> 0 bytes applications/external/doom/assets/gun_inv.png | Bin 1050 -> 0 bytes .../external/doom/assets/gun_mask_inv.png | Bin 847 -> 0 bytes applications/external/doom/assets/imp_inv.png | Bin 903 -> 0 bytes .../external/doom/assets/imp_mask_inv.png | Bin 958 -> 0 bytes .../external/doom/assets/item_inv.png | Bin 417 -> 0 bytes .../external/doom/assets/item_mask_inv.png | Bin 383 -> 0 bytes .../external/doom/assets/logo_inv.png | Bin 5085 -> 0 bytes applications/external/doom/constants.h | 91 - applications/external/doom/display.h | 280 -- applications/external/doom/doom.c | 1104 ------ applications/external/doom/doom_10px.png | Bin 1772 -> 0 bytes .../external/doom/doom_music_player_worker.c | 504 --- .../external/doom/doom_music_player_worker.h | 44 - applications/external/doom/entities.c | 42 - applications/external/doom/entities.h | 56 - applications/external/doom/img/1.png | Bin 1487 -> 0 bytes applications/external/doom/img/2.png | Bin 1705 -> 0 bytes applications/external/doom/img/3.png | Bin 1629 -> 0 bytes applications/external/doom/level.h | 188 - applications/external/doom/sound.h | 34 - applications/external/doom/types.c | 33 - applications/external/doom/types.h | 36 - applications/external/dtmf_dolphin/LICENSE | 674 ---- applications/external/dtmf_dolphin/README.md | 18 - .../external/dtmf_dolphin/application.fam | 18 - .../external/dtmf_dolphin/dtmf_dolphin.c | 89 - .../dtmf_dolphin/dtmf_dolphin_audio.c | 270 -- .../dtmf_dolphin/dtmf_dolphin_audio.h | 54 - .../external/dtmf_dolphin/dtmf_dolphin_data.c | 220 -- .../external/dtmf_dolphin/dtmf_dolphin_data.h | 36 - .../dtmf_dolphin/dtmf_dolphin_event.h | 21 - .../external/dtmf_dolphin/dtmf_dolphin_hal.c | 52 - .../external/dtmf_dolphin/dtmf_dolphin_hal.h | 33 - .../external/dtmf_dolphin/dtmf_dolphin_i.h | 42 - applications/external/dtmf_dolphin/phone.png | Bin 306 -> 0 bytes .../external/dtmf_dolphin/pics/dialer.jpg | Bin 2003305 -> 0 bytes .../dtmf_dolphin/scenes/dtmf_dolphin_scene.c | 30 - .../dtmf_dolphin/scenes/dtmf_dolphin_scene.h | 29 - .../scenes/dtmf_dolphin_scene_config.h | 2 - .../scenes/dtmf_dolphin_scene_dialer.c | 49 - .../scenes/dtmf_dolphin_scene_start.c | 94 - .../dtmf_dolphin/views/dtmf_dolphin_common.h | 10 - .../dtmf_dolphin/views/dtmf_dolphin_dialer.c | 350 -- .../dtmf_dolphin/views/dtmf_dolphin_dialer.h | 18 - .../FlipperZeroWiFiDeauthModuleDefines.h | 7 - .../external/esp8266_deauth/application.fam | 14 - .../external/esp8266_deauth/esp8266_deauth.c | 543 --- .../external/esp8266_deauth/wifi_10px.png | Bin 1781 -> 0 bytes .../external/flappy_bird/application.fam | 15 - .../external/flappy_bird/assets/bird_01.png | Bin 113 -> 0 bytes .../external/flappy_bird/assets/bird_02.png | Bin 116 -> 0 bytes .../external/flappy_bird/assets/bird_03.png | Bin 116 -> 0 bytes .../external/flappy_bird/flappy_10px.png | Bin 1787 -> 0 bytes .../external/flappy_bird/flappy_bird.c | 378 -- .../external/flipper_i2ctools/LICENSE | 674 ---- .../external/flipper_i2ctools/README.md | 44 - .../external/flipper_i2ctools/application.fam | 15 - .../external/flipper_i2ctools/i2cscanner.c | 35 - .../external/flipper_i2ctools/i2cscanner.h | 23 - .../external/flipper_i2ctools/i2csender.c | 29 - .../external/flipper_i2ctools/i2csender.h | 21 - .../external/flipper_i2ctools/i2csniffer.c | 101 - .../external/flipper_i2ctools/i2csniffer.h | 46 - .../external/flipper_i2ctools/i2ctools.c | 231 -- .../external/flipper_i2ctools/i2ctools.gif | Bin 2661177 -> 0 bytes .../external/flipper_i2ctools/i2ctools.png | Bin 8425 -> 0 bytes .../external/flipper_i2ctools/i2ctools_i.h | 24 - .../images/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../images/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../images/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../flipper_i2ctools/images/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../flipper_i2ctools/images/Ok_btn_9x9.png | Bin 3605 -> 0 bytes .../flipper_i2ctools/images/Voltage_16x16.png | Bin 294 -> 0 bytes .../images/i2ctools_main_76x59.png | Bin 1058 -> 0 bytes .../flipper_i2ctools/views/infos_view.c | 15 - .../flipper_i2ctools/views/infos_view.h | 8 - .../flipper_i2ctools/views/main_view.c | 82 - .../flipper_i2ctools/views/main_view.h | 43 - .../flipper_i2ctools/views/scanner_view.c | 47 - .../flipper_i2ctools/views/scanner_view.h | 9 - .../flipper_i2ctools/views/sender_view.c | 70 - .../flipper_i2ctools/views/sender_view.h | 9 - .../flipper_i2ctools/views/sniffer_view.c | 92 - .../flipper_i2ctools/views/sniffer_view.h | 9 - applications/external/game15/README.md | 13 - applications/external/game15/application.fam | 14 - applications/external/game15/game15.c | 473 --- applications/external/game15/game15_10px.png | Bin 152 -> 0 bytes .../external/game15/images/Game15.png | Bin 2078 -> 0 bytes .../external/game15/images/Game15Popup.png | Bin 2267 -> 0 bytes .../external/game15/images/Game15Restore.png | Bin 2156 -> 0 bytes applications/external/game15/sandbox.c | 93 - applications/external/game15/sandbox.h | 24 - applications/external/game_2048/LICENSE | 21 - applications/external/game_2048/README.md | 17 - .../external/game_2048/application.fam | 16 - applications/external/game_2048/array_utils.c | 40 - applications/external/game_2048/array_utils.h | 22 - applications/external/game_2048/digits.h | 263 -- applications/external/game_2048/game_2048.c | 515 --- applications/external/game_2048/game_2048.png | Bin 89 -> 0 bytes .../external/game_2048/images/screenshot1.png | Bin 2038 -> 0 bytes .../external/game_2048/images/screenshot2.png | Bin 2183 -> 0 bytes applications/external/gps_nmea_uart/LICENSE | 674 ---- applications/external/gps_nmea_uart/README.md | 40 - .../external/gps_nmea_uart/application.fam | 14 - applications/external/gps_nmea_uart/gps.c | 190 - .../external/gps_nmea_uart/gps_10px.png | Bin 6750 -> 0 bytes .../external/gps_nmea_uart/gps_uart.c | 217 -- .../external/gps_nmea_uart/gps_uart.h | 46 - applications/external/gps_nmea_uart/minmea.c | 640 ---- applications/external/gps_nmea_uart/minmea.h | 295 -- applications/external/gps_nmea_uart/ui.png | Bin 84291 -> 0 bytes .../external/gps_nmea_uart/wiring.png | Bin 82441 -> 0 bytes applications/external/hc_sr04/application.fam | 16 - .../external/hc_sr04/dist_sensor10px.png | Bin 141 -> 0 bytes applications/external/hc_sr04/hc_sr04.c | 275 -- .../heap_defence_game/application.fam | 14 - .../assets_images/Background_128x64.png | Bin 872 -> 0 bytes .../assets_images/Box1_10x10.png | Bin 489 -> 0 bytes .../assets_images/Box2_10x10.png | Bin 448 -> 0 bytes .../assets_images/Box3_10x10.png | Bin 459 -> 0 bytes .../assets_images/Box4_10x10.png | Bin 463 -> 0 bytes .../assets_images/Box5_10x10.png | Bin 475 -> 0 bytes .../assets_images/Box6p_10x10.png | Bin 471 -> 0 bytes .../assets_images/Box7p_10x10.png | Bin 470 -> 0 bytes .../assets_images/Box8p_10x10.png | Bin 465 -> 0 bytes .../assets_images/Game_over_128x64.png | Bin 683 -> 0 bytes .../HD_game_over_128x64/frame_01.png | Bin 683 -> 0 bytes .../HD_game_over_128x64/frame_02.png | Bin 680 -> 0 bytes .../HD_game_over_128x64/frame_03.png | Bin 671 -> 0 bytes .../HD_game_over_128x64/frame_04.png | Bin 667 -> 0 bytes .../HD_game_over_128x64/frame_05.png | Bin 670 -> 0 bytes .../HD_game_over_128x64/frame_06.png | Bin 669 -> 0 bytes .../HD_game_over_128x64/frame_07.png | Bin 677 -> 0 bytes .../HD_game_over_128x64/frame_rate | 1 - .../HD_person_block_left_10x20/frame_01.png | Bin 8918 -> 0 bytes .../HD_person_block_left_10x20/frame_02.png | Bin 9144 -> 0 bytes .../HD_person_block_left_10x20/frame_rate | 1 - .../HD_person_block_right_10x20/frame_01.png | Bin 475 -> 0 bytes .../HD_person_block_right_10x20/frame_02.png | Bin 503 -> 0 bytes .../HD_person_block_right_10x20/frame_rate | 1 - .../HD_person_left_10x20/frame_01.png | Bin 214 -> 0 bytes .../HD_person_left_10x20/frame_02.png | Bin 216 -> 0 bytes .../HD_person_left_10x20/frame_03.png | Bin 221 -> 0 bytes .../HD_person_left_10x20/frame_04.png | Bin 216 -> 0 bytes .../HD_person_left_10x20/frame_rate | 1 - .../HD_person_right_10x20/frame_01.png | Bin 217 -> 0 bytes .../HD_person_right_10x20/frame_02.png | Bin 218 -> 0 bytes .../HD_person_right_10x20/frame_03.png | Bin 216 -> 0 bytes .../HD_person_right_10x20/frame_04.png | Bin 218 -> 0 bytes .../HD_person_right_10x20/frame_rate | 1 - .../HD_start_128x64/frame_01.png | Bin 3820 -> 0 bytes .../HD_start_128x64/frame_02.png | Bin 3881 -> 0 bytes .../HD_start_128x64/frame_03.png | Bin 3810 -> 0 bytes .../HD_start_128x64/frame_04.png | Bin 3881 -> 0 bytes .../assets_images/HD_start_128x64/frame_rate | 1 - .../assets_images/Person4_1_10x20.png | Bin 8918 -> 0 bytes .../assets_images/Person4_2_10x20.png | Bin 9144 -> 0 bytes .../assets_images/Person5_1_10x20.png | Bin 475 -> 0 bytes .../assets_images/Person5_2_10x20.png | Bin 503 -> 0 bytes .../assets_images/Start_128x64.png | Bin 3881 -> 0 bytes .../external/heap_defence_game/box.png | Bin 131 -> 0 bytes .../external/heap_defence_game/heap_defence.c | 598 ---- .../external/heap_defence_game/hede_assets.c | 28 - .../external/heap_defence_game/hede_assets.h | 11 - applications/external/hex_viewer/LICENSE | 21 - .../external/hex_viewer/application.fam | 18 - applications/external/hex_viewer/hex_viewer.c | 294 -- .../external/hex_viewer/icons/hex_10px.png | Bin 171 -> 0 bytes applications/external/hid_app/application.fam | 24 - .../external/hid_app/assets/Arr_dwn_7x9.png | Bin 3602 -> 0 bytes .../external/hid_app/assets/Arr_up_7x9.png | Bin 3605 -> 0 bytes .../hid_app/assets/Ble_connected_15x15.png | Bin 3634 -> 0 bytes .../hid_app/assets/Ble_disconnected_15x15.png | Bin 657 -> 0 bytes .../hid_app/assets/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../external/hid_app/assets/ButtonF10_5x8.png | Bin 172 -> 0 bytes .../external/hid_app/assets/ButtonF11_5x8.png | Bin 173 -> 0 bytes .../external/hid_app/assets/ButtonF12_5x8.png | Bin 180 -> 0 bytes .../external/hid_app/assets/ButtonF1_5x8.png | Bin 177 -> 0 bytes .../external/hid_app/assets/ButtonF2_5x8.png | Bin 179 -> 0 bytes .../external/hid_app/assets/ButtonF3_5x8.png | Bin 178 -> 0 bytes .../external/hid_app/assets/ButtonF4_5x8.png | Bin 177 -> 0 bytes .../external/hid_app/assets/ButtonF5_5x8.png | Bin 178 -> 0 bytes .../external/hid_app/assets/ButtonF6_5x8.png | Bin 177 -> 0 bytes .../external/hid_app/assets/ButtonF7_5x8.png | Bin 176 -> 0 bytes .../external/hid_app/assets/ButtonF8_5x8.png | Bin 176 -> 0 bytes .../external/hid_app/assets/ButtonF9_5x8.png | Bin 179 -> 0 bytes .../hid_app/assets/ButtonLeft_4x7.png | Bin 1415 -> 0 bytes .../hid_app/assets/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../external/hid_app/assets/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../external/hid_app/assets/Button_18x18.png | Bin 3609 -> 0 bytes .../external/hid_app/assets/Circles_47x47.png | Bin 3712 -> 0 bytes .../hid_app/assets/KB_key_Alt_17x10.png | Bin 162 -> 0 bytes .../hid_app/assets/KB_key_Cmd_17x10.png | Bin 163 -> 0 bytes .../hid_app/assets/KB_key_Ctl_17x10.png | Bin 161 -> 0 bytes .../hid_app/assets/KB_key_Del_17x10.png | Bin 165 -> 0 bytes .../hid_app/assets/KB_key_Esc_17x10.png | Bin 164 -> 0 bytes .../hid_app/assets/KB_key_Tab_17x10.png | Bin 163 -> 0 bytes .../hid_app/assets/Left_mouse_icon_9x9.png | Bin 3622 -> 0 bytes .../external/hid_app/assets/Like_def_11x9.png | Bin 3616 -> 0 bytes .../hid_app/assets/Like_pressed_17x17.png | Bin 3643 -> 0 bytes .../external/hid_app/assets/Ok_btn_9x9.png | Bin 3605 -> 0 bytes .../hid_app/assets/Ok_btn_pressed_13x13.png | Bin 3625 -> 0 bytes .../hid_app/assets/OutCircles_70x51.png | Bin 2469 -> 0 bytes .../hid_app/assets/Pause_icon_9x9.png | Bin 1743 -> 0 bytes .../hid_app/assets/Pin_arrow_down_7x9.png | Bin 3607 -> 0 bytes .../hid_app/assets/Pin_arrow_left_9x7.png | Bin 3603 -> 0 bytes .../hid_app/assets/Pin_arrow_right_9x7.png | Bin 3602 -> 0 bytes .../hid_app/assets/Pin_arrow_up_7x9.png | Bin 3603 -> 0 bytes .../hid_app/assets/Pin_back_arrow_10x10.png | Bin 4575 -> 0 bytes .../hid_app/assets/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes .../hid_app/assets/Pressed_Button_13x13.png | Bin 3606 -> 0 bytes .../hid_app/assets/Pressed_Button_19x19.png | Bin 1790 -> 0 bytes .../hid_app/assets/Right_mouse_icon_9x9.png | Bin 3622 -> 0 bytes .../external/hid_app/assets/S_DOWN_31x15.png | Bin 1893 -> 0 bytes .../external/hid_app/assets/S_LEFT_15x31.png | Bin 1906 -> 0 bytes .../external/hid_app/assets/S_RIGHT_15x31.png | Bin 1902 -> 0 bytes .../external/hid_app/assets/S_UP_31x15.png | Bin 1886 -> 0 bytes .../external/hid_app/assets/Space_60x18.png | Bin 2871 -> 0 bytes .../external/hid_app/assets/Space_65x18.png | Bin 3619 -> 0 bytes .../external/hid_app/assets/Voldwn_6x6.png | Bin 4556 -> 0 bytes .../external/hid_app/assets/Volup_8x6.png | Bin 4564 -> 0 bytes applications/external/hid_app/hid.c | 462 --- applications/external/hid_app/hid.h | 69 - .../external/hid_app/hid_ble_10px.png | Bin 151 -> 0 bytes .../external/hid_app/hid_usb_10px.png | Bin 174 -> 0 bytes applications/external/hid_app/views.h | 12 - .../external/hid_app/views/hid_keyboard.c | 411 --- .../external/hid_app/views/hid_keyboard.h | 14 - .../external/hid_app/views/hid_keynote.c | 312 -- .../external/hid_app/views/hid_keynote.h | 16 - .../external/hid_app/views/hid_media.c | 229 -- .../external/hid_app/views/hid_media.h | 13 - .../external/hid_app/views/hid_mouse.c | 232 -- .../external/hid_app/views/hid_mouse.h | 17 - .../hid_app/views/hid_mouse_clicker.c | 214 -- .../hid_app/views/hid_mouse_clicker.h | 14 - .../hid_app/views/hid_mouse_jiggler.c | 183 - .../hid_app/views/hid_mouse_jiggler.h | 17 - .../external/hid_app/views/hid_numpad.c | 318 -- .../external/hid_app/views/hid_numpad.h | 14 - .../external/hid_app/views/hid_tikshorts.c | 271 -- .../external/hid_app/views/hid_tikshorts.h | 14 - .../external/ir_scope/application.fam | 14 - applications/external/ir_scope/ir_scope.c | 184 - applications/external/ir_scope/ir_scope.png | Bin 169 -> 0 bytes .../external/jetpack_joyride/application.fam | 15 - .../jetpack_joyride/assets/air_vent.png | Bin 1838 -> 0 bytes .../jetpack_joyride/assets/alert/frame_01.png | Bin 1820 -> 0 bytes .../jetpack_joyride/assets/alert/frame_02.png | Bin 1807 -> 0 bytes .../jetpack_joyride/assets/alert/frame_rate | 1 - .../jetpack_joyride/assets/barry/frame_01.png | Bin 1932 -> 0 bytes .../jetpack_joyride/assets/barry/frame_02.png | Bin 1930 -> 0 bytes .../jetpack_joyride/assets/barry/frame_03.png | Bin 1929 -> 0 bytes .../jetpack_joyride/assets/barry/frame_rate | 1 - .../jetpack_joyride/assets/barry_infill.png | Bin 1614 -> 0 bytes .../external/jetpack_joyride/assets/bg1.png | Bin 835 -> 0 bytes .../external/jetpack_joyride/assets/bg2.png | Bin 968 -> 0 bytes .../external/jetpack_joyride/assets/bg3.png | Bin 886 -> 0 bytes .../external/jetpack_joyride/assets/coin.png | Bin 1842 -> 0 bytes .../jetpack_joyride/assets/coin_infill.png | Bin 1974 -> 0 bytes .../jetpack_joyride/assets/dead_scientist.png | Bin 1768 -> 0 bytes .../assets/dead_scientist_infill.png | Bin 1900 -> 0 bytes .../external/jetpack_joyride/assets/door.png | Bin 1961 -> 0 bytes .../assets/missile/frame_01.png | Bin 1898 -> 0 bytes .../jetpack_joyride/assets/missile/frame_rate | 1 - .../jetpack_joyride/assets/missile_infill.png | Bin 1591 -> 0 bytes .../jetpack_joyride/assets/pillar.png | Bin 1880 -> 0 bytes .../jetpack_joyride/assets/scientist_left.png | Bin 2056 -> 0 bytes .../assets/scientist_left_infill.png | Bin 2211 -> 0 bytes .../assets/scientist_right.png | Bin 1917 -> 0 bytes .../assets/scientist_right_infill.png | Bin 2210 -> 0 bytes .../external/jetpack_joyride/icon.png | Bin 1836 -> 0 bytes .../includes/background_asset.c | 81 - .../includes/background_assets.h | 34 - .../external/jetpack_joyride/includes/barry.c | 33 - .../external/jetpack_joyride/includes/barry.h | 23 - .../external/jetpack_joyride/includes/coin.c | 98 - .../external/jetpack_joyride/includes/coin.h | 26 - .../jetpack_joyride/includes/game_sprites.h | 35 - .../jetpack_joyride/includes/game_state.c | 5 - .../jetpack_joyride/includes/game_state.h | 34 - .../jetpack_joyride/includes/missile.c | 86 - .../jetpack_joyride/includes/missile.h | 24 - .../jetpack_joyride/includes/particle.c | 57 - .../jetpack_joyride/includes/particle.h | 21 - .../external/jetpack_joyride/includes/point.h | 14 - .../jetpack_joyride/includes/scientist.c | 77 - .../jetpack_joyride/includes/scientist.h | 29 - .../jetpack_joyride/includes/states.h | 9 - .../external/jetpack_joyride/jetpack.c | 379 -- applications/external/lightmeter/LICENSE | 21 - applications/external/lightmeter/README.md | 17 - .../external/lightmeter/application.fam | 34 - .../external/lightmeter/docs/README.md | 17 - .../external/lightmeter/docs/changelog.md | 15 - .../gui/scenes/config/lightmeter_scene.c | 30 - .../gui/scenes/config/lightmeter_scene.h | 29 - .../scenes/config/lightmeter_scene_config.h | 4 - .../gui/scenes/lightmeter_scene_about.c | 71 - .../gui/scenes/lightmeter_scene_config.c | 344 -- .../gui/scenes/lightmeter_scene_help.c | 41 - .../gui/scenes/lightmeter_scene_main.c | 60 - .../external/lightmeter/gui/views/main_view.c | 548 --- .../external/lightmeter/gui/views/main_view.h | 110 - .../external/lightmeter/icons/T_10x14.png | Bin 194 -> 0 bytes .../external/lightmeter/icons/f_10x14.png | Bin 224 -> 0 bytes .../external/lightmeter/lib/BH1750/BH1750.c | 150 - .../external/lightmeter/lib/BH1750/BH1750.h | 110 - .../external/lightmeter/lib/BH1750/LICENSE | 21 - .../external/lightmeter/lib/BH1750/README.md | 2 - .../lightmeter/lib/BH1750/docs/BH1750.pdf | Bin 570536 -> 0 bytes .../lightmeter/lib/MAX44009/MAX44009.c | 35 - .../lightmeter/lib/MAX44009/MAX44009.h | 27 - applications/external/lightmeter/lightmeter.c | 259 -- applications/external/lightmeter/lightmeter.h | 79 - .../external/lightmeter/lightmeter.png | Bin 243 -> 0 bytes .../external/lightmeter/lightmeter_config.h | 124 - .../external/lightmeter/lightmeter_helper.c | 43 - .../external/lightmeter/lightmeter_helper.h | 11 - applications/external/metronome/README.md | 23 - .../external/metronome/application.fam | 17 - .../external/metronome/gui_extensions.c | 56 - .../external/metronome/gui_extensions.h | 3 - .../metronome/images/ButtonUp_7x4.png | Bin 102 -> 0 bytes .../external/metronome/img/screenshot.png | Bin 1978 -> 0 bytes .../external/metronome/img/wave_left_4x14.png | Bin 144 -> 0 bytes .../metronome/img/wave_right_4x14.png | Bin 132 -> 0 bytes applications/external/metronome/metronome.c | 398 -- .../external/metronome/metronome_icon.png | Bin 170 -> 0 bytes applications/external/mfkey32/application.fam | 17 - .../external/mfkey32/images/mfkey.png | Bin 9060 -> 0 bytes applications/external/mfkey32/mfkey.png | Bin 9060 -> 0 bytes applications/external/mfkey32/mfkey32.c | 1349 ------- .../external/mifare_nested/LICENSE.md | 674 ---- applications/external/mifare_nested/README.md | 38 - applications/external/mifare_nested/TODO.md | 3 - .../external/mifare_nested/application.fam | 25 - .../mifare_nested/assets/ApplyTag.png | Bin 3804 -> 0 bytes .../mifare_nested/assets/DolphinCry.png | Bin 3898 -> 0 bytes .../mifare_nested/assets/DolphinSuccess.png | Bin 2681 -> 0 bytes .../external/mifare_nested/assets/Loading.png | Bin 3649 -> 0 bytes .../external/mifare_nested/assets/icon.png | Bin 145 -> 0 bytes .../mifare_nested/lib/crypto1/crypto1.c | 118 - .../mifare_nested/lib/crypto1/crypto1.h | 39 - .../mifare_nested/lib/nested/nested.c | 718 ---- .../mifare_nested/lib/nested/nested.h | 118 - .../mifare_nested/lib/parity/parity.c | 71 - .../mifare_nested/lib/parity/parity.h | 10 - .../external/mifare_nested/mifare_nested.c | 409 --- .../external/mifare_nested/mifare_nested.h | 3 - .../external/mifare_nested/mifare_nested_i.h | 181 - .../mifare_nested/mifare_nested_worker.c | 1713 --------- .../mifare_nested/mifare_nested_worker.h | 98 - .../mifare_nested/mifare_nested_worker_i.h | 28 - .../scenes/mifare_nested_scene.c | 30 - .../scenes/mifare_nested_scene.h | 29 - .../scenes/mifare_nested_scene_about.c | 77 - .../scenes/mifare_nested_scene_added_keys.c | 76 - .../scenes/mifare_nested_scene_check.c | 95 - .../scenes/mifare_nested_scene_check_keys.c | 117 - .../scenes/mifare_nested_scene_collecting.c | 161 - .../scenes/mifare_nested_scene_config.h | 14 - .../scenes/mifare_nested_scene_failed.c | 59 - .../mifare_nested_scene_need_collection.c | 56 - .../mifare_nested_scene_need_key_recovery.c | 59 - .../scenes/mifare_nested_scene_no_keys.c | 61 - .../mifare_nested_scene_no_nonces_collected.c | 94 - .../mifare_nested_scene_nonces_collected.c | 58 - .../scenes/mifare_nested_scene_settings.c | 65 - .../scenes/mifare_nested_scene_start.c | 84 - ...fare_nested_scene_static_encrypted_nonce.c | 58 - applications/external/minesweeper/LICENSE | 674 ---- applications/external/minesweeper/README.md | 20 - .../external/minesweeper/application.fam | 14 - applications/external/minesweeper/assets.h | 144 - .../external/minesweeper/assets/asset | 48 - .../external/minesweeper/assets/mockup.png | Bin 400 -> 0 bytes .../external/minesweeper/assets/tile_0.png | Bin 130 -> 0 bytes .../external/minesweeper/assets/tile_0.xbm | 4 - .../external/minesweeper/assets/tile_1.png | Bin 152 -> 0 bytes .../external/minesweeper/assets/tile_1.xbm | 4 - .../external/minesweeper/assets/tile_2.png | Bin 164 -> 0 bytes .../external/minesweeper/assets/tile_2.xbm | 4 - .../external/minesweeper/assets/tile_3.png | Bin 166 -> 0 bytes .../external/minesweeper/assets/tile_3.xbm | 4 - .../external/minesweeper/assets/tile_4.png | Bin 164 -> 0 bytes .../external/minesweeper/assets/tile_4.xbm | 4 - .../external/minesweeper/assets/tile_5.png | Bin 167 -> 0 bytes .../external/minesweeper/assets/tile_5.xbm | 4 - .../external/minesweeper/assets/tile_6.png | Bin 164 -> 0 bytes .../external/minesweeper/assets/tile_6.xbm | 4 - .../external/minesweeper/assets/tile_7.png | Bin 165 -> 0 bytes .../external/minesweeper/assets/tile_7.xbm | 4 - .../external/minesweeper/assets/tile_8.png | Bin 161 -> 0 bytes .../external/minesweeper/assets/tile_8.xbm | 4 - .../minesweeper/assets/tile_empty.png | Bin 131 -> 0 bytes .../external/minesweeper/assets/tile_flag.png | Bin 170 -> 0 bytes .../external/minesweeper/assets/tile_flag.xbm | 4 - .../external/minesweeper/assets/tile_mine.png | Bin 152 -> 0 bytes .../external/minesweeper/assets/tile_mine.xbm | 4 - .../minesweeper/assets/tile_uncleared.png | Bin 127 -> 0 bytes .../minesweeper/assets/tile_uncleared.xbm | 4 - .../external/minesweeper/img/screenshot.png | Bin 1842 -> 0 bytes .../external/minesweeper/minesweeper.c | 515 --- .../external/minesweeper/minesweeper_icon.png | Bin 161 -> 0 bytes .../external/morse_code/application.fam | 16 - applications/external/morse_code/morse_code.c | 166 - .../external/morse_code/morse_code_10px.png | Bin 168 -> 0 bytes .../external/morse_code/morse_code_worker.c | 183 - .../external/morse_code/morse_code_worker.h | 36 - .../external/mousejacker/application.fam | 26 - .../mousejacker/images/badusb_10px.png | Bin 576 -> 0 bytes .../external/mousejacker/images/sub1_10px.png | Bin 299 -> 0 bytes .../external/mousejacker/lib/nrf24/nrf24.c | 530 --- .../external/mousejacker/lib/nrf24/nrf24.h | 373 -- .../external/mousejacker/mouse_10px.png | Bin 1634 -> 0 bytes .../external/mousejacker/mousejacker.c | 396 -- .../external/mousejacker/mousejacker_ducky.c | 460 --- .../external/mousejacker/mousejacker_ducky.h | 46 - .../external/multi_converter/application.fam | 14 - .../multi_converter/converter_10px.png | Bin 1632 -> 0 bytes .../multi_converter/multi_converter.c | 164 - .../multi_converter_definitions.h | 83 - .../multi_converter_mode_display.c | 326 -- .../multi_converter_mode_display.h | 61 - .../multi_converter_mode_select.c | 210 -- .../multi_converter_mode_select.h | 73 - .../multi_converter/multi_converter_units.c | 261 -- .../multi_converter/multi_converter_units.h | 171 - applications/external/multi_fuzzer | 1 - .../external/music_player/application.fam | 16 - .../music_player/icons/music_10px.png | Bin 142 -> 0 bytes .../external/music_player/music_player.c | 367 -- .../external/nfc_magic/application.fam | 21 - .../nfc_magic/assets/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes .../nfc_magic/assets/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../external/nfc_magic/assets/Loading_24.png | Bin 3649 -> 0 bytes .../nfc_magic/assets/NFC_manual_60x50.png | Bin 3804 -> 0 bytes .../nfc_magic/lib/magic/classic_gen1.c | 145 - .../nfc_magic/lib/magic/classic_gen1.h | 11 - .../external/nfc_magic/lib/magic/common.c | 33 - .../external/nfc_magic/lib/magic/common.h | 19 - .../external/nfc_magic/lib/magic/gen4.c | 199 - .../external/nfc_magic/lib/magic/gen4.h | 48 - .../external/nfc_magic/lib/magic/types.c | 23 - .../external/nfc_magic/lib/magic/types.h | 5 - applications/external/nfc_magic/nfc_magic.c | 184 - applications/external/nfc_magic/nfc_magic.h | 5 - applications/external/nfc_magic/nfc_magic_i.h | 94 - .../external/nfc_magic/nfc_magic_worker.c | 483 --- .../external/nfc_magic/nfc_magic_worker.h | 42 - .../external/nfc_magic/nfc_magic_worker_i.h | 29 - .../nfc_magic/scenes/nfc_magic_scene.c | 30 - .../nfc_magic/scenes/nfc_magic_scene.h | 29 - .../scenes/nfc_magic_scene_actions.c | 50 - .../nfc_magic/scenes/nfc_magic_scene_check.c | 89 - .../nfc_magic/scenes/nfc_magic_scene_config.h | 18 - .../scenes/nfc_magic_scene_file_select.c | 76 - .../scenes/nfc_magic_scene_gen4_actions.c | 70 - .../scenes/nfc_magic_scene_key_input.c | 45 - .../scenes/nfc_magic_scene_magic_info.c | 59 - .../scenes/nfc_magic_scene_new_key_input.c | 45 - .../scenes/nfc_magic_scene_not_magic.c | 43 - .../nfc_magic/scenes/nfc_magic_scene_rekey.c | 95 - .../scenes/nfc_magic_scene_rekey_fail.c | 50 - .../nfc_magic/scenes/nfc_magic_scene_start.c | 56 - .../scenes/nfc_magic_scene_success.c | 42 - .../nfc_magic/scenes/nfc_magic_scene_wipe.c | 97 - .../scenes/nfc_magic_scene_wipe_fail.c | 41 - .../nfc_magic/scenes/nfc_magic_scene_write.c | 97 - .../scenes/nfc_magic_scene_write_confirm.c | 64 - .../scenes/nfc_magic_scene_write_fail.c | 58 - .../scenes/nfc_magic_scene_wrong_card.c | 53 - .../external/nfc_maker/application.fam | 15 - .../nfc_maker/assets/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../assets/KeyBackspaceSelected_16x9.png | Bin 1812 -> 0 bytes .../nfc_maker/assets/KeyBackspace_16x9.png | Bin 1829 -> 0 bytes .../assets/KeyKeyboardSelected_10x11.png | Bin 7210 -> 0 bytes .../nfc_maker/assets/KeyKeyboard_10x11.png | Bin 7763 -> 0 bytes .../assets/KeySaveSelected_24x11.png | Bin 1853 -> 0 bytes .../nfc_maker/assets/KeySave_24x11.png | Bin 1863 -> 0 bytes .../nfc_maker/assets/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes applications/external/nfc_maker/nfc_maker.c | 84 - applications/external/nfc_maker/nfc_maker.h | 68 - .../external/nfc_maker/nfc_maker_10px.png | Bin 4142 -> 0 bytes .../external/nfc_maker/nfc_maker_text_input.c | 770 ---- .../external/nfc_maker/nfc_maker_text_input.h | 85 - .../external/nfc_maker/nfc_maker_validators.c | 57 - .../external/nfc_maker/nfc_maker_validators.h | 21 - .../nfc_maker/scenes/nfc_maker_scene.c | 30 - .../nfc_maker/scenes/nfc_maker_scene.h | 29 - .../scenes/nfc_maker_scene_bluetooth.c | 57 - .../nfc_maker/scenes/nfc_maker_scene_config.h | 18 - .../scenes/nfc_maker_scene_contact.c | 53 - .../scenes/nfc_maker_scene_contact_last.c | 55 - .../scenes/nfc_maker_scene_contact_mail.c | 55 - .../scenes/nfc_maker_scene_contact_phone.c | 55 - .../scenes/nfc_maker_scene_contact_url.c | 55 - .../nfc_maker/scenes/nfc_maker_scene_https.c | 53 - .../nfc_maker/scenes/nfc_maker_scene_mail.c | 53 - .../nfc_maker/scenes/nfc_maker_scene_phone.c | 53 - .../nfc_maker/scenes/nfc_maker_scene_result.c | 400 --- .../nfc_maker/scenes/nfc_maker_scene_save.c | 57 - .../nfc_maker/scenes/nfc_maker_scene_start.c | 68 - .../nfc_maker/scenes/nfc_maker_scene_text.c | 53 - .../nfc_maker/scenes/nfc_maker_scene_url.c | 53 - .../nfc_maker/scenes/nfc_maker_scene_wifi.c | 55 - .../scenes/nfc_maker_scene_wifi_auth.c | 83 - .../scenes/nfc_maker_scene_wifi_encr.c | 48 - .../scenes/nfc_maker_scene_wifi_pass.c | 53 - applications/external/nfc_maker/strnlen.c | 11 - applications/external/nfc_maker/strnlen.h | 6 - .../nfc_rfid_detector/application.fam | 13 - .../helpers/nfc_rfid_detector_event.h | 7 - .../helpers/nfc_rfid_detector_types.h | 15 - .../images/Modern_reader_18x34.png | Bin 3670 -> 0 bytes .../images/Move_flipper_26x39.png | Bin 3698 -> 0 bytes .../images/NFC_detect_45x30.png | Bin 168 -> 0 bytes .../images/Rfid_detect_45x30.png | Bin 158 -> 0 bytes .../nfc_rfid_detector_10px.png | Bin 124 -> 0 bytes .../nfc_rfid_detector/nfc_rfid_detector_app.c | 108 - .../nfc_rfid_detector_app_i.c | 40 - .../nfc_rfid_detector_app_i.h | 30 - .../scenes/nfc_rfid_detector_scene.c | 31 - .../scenes/nfc_rfid_detector_scene.h | 29 - .../scenes/nfc_rfid_detector_scene_about.c | 69 - .../scenes/nfc_rfid_detector_scene_config.h | 3 - .../nfc_rfid_detector_scene_field_presence.c | 60 - .../scenes/nfc_rfid_detector_scene_start.c | 58 - .../nfc_rfid_detector_view_field_presence.c | 164 - .../nfc_rfid_detector_view_field_presence.h | 19 - .../external/nrfsniff/application.fam | 22 - .../external/nrfsniff/lib/nrf24/nrf24.c | 530 --- .../external/nrfsniff/lib/nrf24/nrf24.h | 373 -- applications/external/nrfsniff/nrfsniff.c | 463 --- .../external/nrfsniff/nrfsniff_10px.png | Bin 1771 -> 0 bytes applications/external/picopass/125_10px.png | Bin 308 -> 0 bytes .../external/picopass/application.fam | 22 - .../picopass/helpers/iclass_elite_dict.c | 164 - .../picopass/helpers/iclass_elite_dict.h | 29 - .../picopass/icons/DolphinMafia_115x62.png | Bin 2504 -> 0 bytes .../picopass/icons/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../external/picopass/icons/Nfc_10px.png | Bin 304 -> 0 bytes .../icons/RFIDDolphinReceive_97x61.png | Bin 1421 -> 0 bytes .../picopass/icons/RFIDDolphinSend_97x61.png | Bin 1418 -> 0 bytes .../picopass/lib/loclass/optimized_cipher.c | 299 -- .../picopass/lib/loclass/optimized_cipher.h | 98 - .../lib/loclass/optimized_cipherutils.c | 136 - .../lib/loclass/optimized_cipherutils.h | 64 - .../picopass/lib/loclass/optimized_elite.c | 232 -- .../picopass/lib/loclass/optimized_elite.h | 58 - .../picopass/lib/loclass/optimized_ikeys.c | 320 -- .../picopass/lib/loclass/optimized_ikeys.h | 66 - applications/external/picopass/picopass.c | 212 -- applications/external/picopass/picopass.h | 3 - .../external/picopass/picopass_device.c | 380 -- .../external/picopass/picopass_device.h | 117 - applications/external/picopass/picopass_i.h | 99 - .../external/picopass/picopass_keys.c | 8 - .../external/picopass/picopass_keys.h | 10 - .../external/picopass/picopass_worker.c | 752 ---- .../external/picopass/picopass_worker.h | 52 - .../external/picopass/picopass_worker_i.h | 34 - .../external/picopass/rfal_picopass.c | 214 -- .../external/picopass/rfal_picopass.h | 48 - .../external/picopass/scenes/picopass_scene.c | 30 - .../external/picopass/scenes/picopass_scene.h | 29 - .../scenes/picopass_scene_card_menu.c | 78 - .../picopass/scenes/picopass_scene_config.h | 17 - .../picopass/scenes/picopass_scene_delete.c | 58 - .../scenes/picopass_scene_delete_success.c | 40 - .../scenes/picopass_scene_device_info.c | 114 - .../scenes/picopass_scene_elite_dict_attack.c | 172 - .../scenes/picopass_scene_file_select.c | 25 - .../picopass/scenes/picopass_scene_key_menu.c | 100 - .../scenes/picopass_scene_read_card.c | 61 - .../scenes/picopass_scene_read_card_success.c | 172 - .../picopass_scene_read_factory_success.c | 80 - .../scenes/picopass_scene_save_name.c | 81 - .../scenes/picopass_scene_save_success.c | 47 - .../scenes/picopass_scene_saved_menu.c | 64 - .../picopass/scenes/picopass_scene_start.c | 64 - .../scenes/picopass_scene_write_card.c | 53 - .../picopass_scene_write_card_success.c | 70 - .../scenes/picopass_scene_write_key.c | 53 - .../external/picopass/views/dict_attack.c | 281 -- .../external/picopass/views/dict_attack.h | 44 - .../external/playlist/application.fam | 15 - .../external/playlist/canvas_helper.c | 81 - .../external/playlist/canvas_helper.h | 5 - .../playlist/helpers/radio_device_loader.c | 64 - .../playlist/helpers/radio_device_loader.h | 15 - .../playlist/images/ButtonRight_4x7.png | Bin 1839 -> 0 bytes .../external/playlist/images/sub1_10px.png | Bin 299 -> 0 bytes applications/external/playlist/playlist.c | 842 ----- .../external/playlist/playlist_10px.png | Bin 1756 -> 0 bytes .../external/playlist/playlist_file.c | 21 - .../external/playlist/playlist_file.h | 7 - .../external/pocsag_pager/application.fam | 15 - .../pocsag_pager/helpers/pocsag_pager_event.h | 14 - .../pocsag_pager/helpers/pocsag_pager_types.h | 49 - .../helpers/radio_device_loader.c | 66 - .../helpers/radio_device_loader.h | 17 - .../pocsag_pager/images/Fishing_123x52.png | Bin 966 -> 0 bytes .../external/pocsag_pager/images/Lock_7x8.png | Bin 3597 -> 0 bytes .../pocsag_pager/images/Message_8x7.png | Bin 130 -> 0 bytes .../images/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes .../pocsag_pager/images/Quest_7x8.png | Bin 3675 -> 0 bytes .../pocsag_pager/images/Scanning_123x52.png | Bin 1690 -> 0 bytes .../pocsag_pager/images/Unlock_7x8.png | Bin 3598 -> 0 bytes .../images/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../pocsag_pager/pocsag_pager_10px.png | Bin 134 -> 0 bytes .../external/pocsag_pager/pocsag_pager_app.c | 206 -- .../pocsag_pager/pocsag_pager_app_i.c | 146 - .../pocsag_pager/pocsag_pager_app_i.h | 76 - .../pocsag_pager/pocsag_pager_history.c | 223 -- .../pocsag_pager/pocsag_pager_history.h | 112 - .../pocsag_pager/protocols/pcsg_generic.c | 124 - .../pocsag_pager/protocols/pcsg_generic.h | 56 - .../external/pocsag_pager/protocols/pocsag.c | 374 -- .../external/pocsag_pager/protocols/pocsag.h | 13 - .../pocsag_pager/protocols/protocol_items.c | 9 - .../pocsag_pager/protocols/protocol_items.h | 6 - .../scenes/pocsag_pager_receiver.c | 214 -- .../pocsag_pager/scenes/pocsag_pager_scene.c | 30 - .../pocsag_pager/scenes/pocsag_pager_scene.h | 29 - .../scenes/pocsag_pager_scene_about.c | 74 - .../scenes/pocsag_pager_scene_config.h | 5 - .../pocsag_pager_scene_receiver_config.c | 221 -- .../scenes/pocsag_pager_scene_receiver_info.c | 50 - .../scenes/pocsag_pager_scene_start.c | 58 - .../views/pocsag_pager_receiver.c | 478 --- .../views/pocsag_pager_receiver.h | 43 - .../views/pocsag_pager_receiver_info.c | 137 - .../views/pocsag_pager_receiver_info.h | 16 - applications/external/protoview/LICENSE | 24 - applications/external/protoview/README.md | 289 -- applications/external/protoview/app.c | 400 --- applications/external/protoview/app.h | 377 -- applications/external/protoview/app_subghz.c | 206 -- applications/external/protoview/appicon.png | Bin 132 -> 0 bytes .../external/protoview/application.fam | 14 - applications/external/protoview/crc.c | 36 - .../external/protoview/custom_presets.h | 317 -- applications/external/protoview/fields.c | 369 -- .../protoview/helpers/radio_device_loader.c | 64 - .../protoview/helpers/radio_device_loader.h | 15 - .../protoview/images/ProtoViewSignal.jpg | Bin 85828 -> 0 bytes .../external/protoview/images/protoview_1.jpg | Bin 66885 -> 0 bytes .../external/protoview/images/protoview_2.jpg | Bin 64574 -> 0 bytes .../external/protoview/protocols/b4b1.c | 93 - .../external/protoview/protocols/keeloq.c | 121 - .../external/protoview/protocols/oregon2.c | 84 - .../external/protoview/protocols/pvchat.c | 205 -- .../protoview/protocols/tpms/citroen.c | 58 - .../external/protoview/protocols/tpms/ford.c | 61 - .../protoview/protocols/tpms/renault.c | 119 - .../protoview/protocols/tpms/schrader.c | 70 - .../protocols/tpms/schrader_eg53ma4.c | 62 - .../protoview/protocols/tpms/toyota.c | 81 - .../external/protoview/protocols/unknown.c | 326 -- applications/external/protoview/raw_samples.c | 93 - applications/external/protoview/raw_samples.h | 33 - applications/external/protoview/signal.c | 692 ---- applications/external/protoview/signal_file.c | 138 - applications/external/protoview/ui.c | 145 - applications/external/protoview/view_build.c | 248 -- .../external/protoview/view_direct_sampling.c | 173 - applications/external/protoview/view_info.c | 332 -- .../external/protoview/view_raw_signal.c | 114 - .../external/protoview/view_settings.c | 119 - .../external/sentry_safe/application.fam | 14 - .../external/sentry_safe/safe_10px.png | Bin 2378 -> 0 bytes .../external/sentry_safe/sentry_safe.c | 169 - .../external/signal_generator/application.fam | 12 - .../icons/SmallArrowDown_3x5.png | Bin 3592 -> 0 bytes .../icons/SmallArrowUp_3x5.png | Bin 7976 -> 0 bytes .../scenes/signal_gen_scene.c | 30 - .../scenes/signal_gen_scene.h | 29 - .../scenes/signal_gen_scene_config.h | 3 - .../scenes/signal_gen_scene_mco.c | 145 - .../scenes/signal_gen_scene_pwm.c | 79 - .../scenes/signal_gen_scene_start.c | 55 - .../signal_generator/signal_gen_10px.png | Bin 6082 -> 0 bytes .../signal_generator/signal_gen_app.c | 93 - .../signal_generator/signal_gen_app_i.h | 46 - .../signal_generator/views/signal_gen_pwm.c | 310 -- .../signal_generator/views/signal_gen_pwm.h | 21 - .../external/snake_game/application.fam | 11 - .../external/snake_game/snake_10px.png | Bin 158 -> 0 bytes applications/external/snake_game/snake_game.c | 409 --- .../external/solitaire/application.fam | 15 - .../solitaire/assets/card_graphics.png | Bin 409 -> 0 bytes .../solitaire/assets/solitaire_main.png | Bin 1158 -> 0 bytes applications/external/solitaire/common/card.c | 353 -- applications/external/solitaire/common/card.h | 192 - applications/external/solitaire/common/dml.c | 53 - applications/external/solitaire/common/dml.h | 116 - applications/external/solitaire/common/menu.c | 103 - applications/external/solitaire/common/menu.h | 77 - .../external/solitaire/common/queue.c | 69 - .../external/solitaire/common/queue.h | 70 - applications/external/solitaire/common/ui.c | 257 -- applications/external/solitaire/common/ui.h | 105 - applications/external/solitaire/defines.h | 57 - applications/external/solitaire/solitaire.c | 570 --- .../external/solitaire/solitaire_10px.png | Bin 121 -> 0 bytes .../spectrum_analyzer/application.fam | 14 - .../helpers/radio_device_loader.c | 64 - .../helpers/radio_device_loader.h | 15 - .../spectrum_analyzer/spectrum_10px.png | Bin 135 -> 0 bytes .../spectrum_analyzer/spectrum_analyzer.c | 611 ---- .../spectrum_analyzer/spectrum_analyzer.h | 84 - .../spectrum_analyzer_worker.c | 345 -- .../spectrum_analyzer_worker.h | 35 - .../external/spi_mem_manager/application.fam | 17 - .../images/ChipLooking_64x64/frame_01.png | Bin 7231 -> 0 bytes .../images/ChipLooking_64x64/frame_02.png | Bin 6909 -> 0 bytes .../images/ChipLooking_64x64/frame_03.png | Bin 7308 -> 0 bytes .../images/ChipLooking_64x64/frame_rate | 1 - .../spi_mem_manager/images/Dip8_10px.png | Bin 195 -> 0 bytes .../spi_mem_manager/images/Dip8_32x36.png | Bin 977 -> 0 bytes .../images/DolphinMafia_115x62.png | Bin 2504 -> 0 bytes .../images/DolphinNice_96x59.png | Bin 2459 -> 0 bytes .../images/SDQuestion_35x43.png | Bin 1950 -> 0 bytes .../images/Wiring_SPI_128x64.png | Bin 4446 -> 0 bytes .../spi_mem_manager/lib/spi/spi_mem_chip.c | 105 - .../spi_mem_manager/lib/spi/spi_mem_chip.h | 34 - .../lib/spi/spi_mem_chip_arr.c | 1399 -------- .../spi_mem_manager/lib/spi/spi_mem_chip_i.h | 85 - .../spi_mem_manager/lib/spi/spi_mem_tools.c | 152 - .../spi_mem_manager/lib/spi/spi_mem_tools.h | 14 - .../spi_mem_manager/lib/spi/spi_mem_worker.c | 129 - .../spi_mem_manager/lib/spi/spi_mem_worker.h | 54 - .../lib/spi/spi_mem_worker_i.h | 24 - .../lib/spi/spi_mem_worker_modes.c | 214 -- .../spi_mem_manager/scenes/spi_mem_scene.c | 30 - .../spi_mem_manager/scenes/spi_mem_scene.h | 29 - .../scenes/spi_mem_scene_about.c | 42 - .../scenes/spi_mem_scene_chip_detect.c | 37 - .../scenes/spi_mem_scene_chip_detect_fail.c | 57 - .../scenes/spi_mem_scene_chip_detected.c | 94 - .../scenes/spi_mem_scene_chip_error.c | 52 - .../scenes/spi_mem_scene_config.h | 21 - .../scenes/spi_mem_scene_delete_confirm.c | 62 - .../scenes/spi_mem_scene_erase.c | 65 - .../scenes/spi_mem_scene_file_info.c | 29 - .../scenes/spi_mem_scene_read.c | 57 - .../scenes/spi_mem_scene_read_filename.c | 46 - .../scenes/spi_mem_scene_saved_file_menu.c | 76 - .../scenes/spi_mem_scene_select_file.c | 22 - .../scenes/spi_mem_scene_select_model.c | 45 - .../scenes/spi_mem_scene_select_vendor.c | 70 - .../scenes/spi_mem_scene_start.c | 84 - .../scenes/spi_mem_scene_storage_error.c | 56 - .../scenes/spi_mem_scene_success.c | 40 - .../scenes/spi_mem_scene_verify.c | 59 - .../scenes/spi_mem_scene_verify_error.c | 43 - .../scenes/spi_mem_scene_wiring.c | 18 - .../scenes/spi_mem_scene_write.c | 58 - .../external/spi_mem_manager/spi_mem_app.c | 112 - .../external/spi_mem_manager/spi_mem_app.h | 3 - .../external/spi_mem_manager/spi_mem_app_i.h | 78 - .../external/spi_mem_manager/spi_mem_files.c | 68 - .../external/spi_mem_manager/spi_mem_files.h | 13 - .../external/spi_mem_manager/tools/README.md | 7 - .../spi_mem_manager/tools/chiplist/LICENSE | 22 - .../tools/chiplist/chiplist.xml | 984 ----- .../spi_mem_manager/tools/chiplist_convert.py | 109 - .../views/spi_mem_view_detect.c | 64 - .../views/spi_mem_view_detect.h | 9 - .../views/spi_mem_view_progress.c | 230 -- .../views/spi_mem_view_progress.h | 26 - applications/external/subbrute | 1 - applications/external/swd_probe/.gitignore | 52 - applications/external/swd_probe/LICENSE.txt | 674 ---- applications/external/swd_probe/README.md | 17 - applications/external/swd_probe/adi.c | 1016 ------ applications/external/swd_probe/adi.h | 34 - .../external/swd_probe/application.fam | 15 - .../swd_probe/icons/ButtonDown_7x4.png | Bin 102 -> 0 bytes .../external/swd_probe/icons/ButtonUp_7x4.png | Bin 102 -> 0 bytes applications/external/swd_probe/icons/app.png | Bin 179 -> 0 bytes applications/external/swd_probe/icons/swd.png | Bin 169 -> 0 bytes applications/external/swd_probe/jep106.c | 26 - applications/external/swd_probe/jep106.h | 26 - applications/external/swd_probe/jep106.inc | 1791 --------- .../external/swd_probe/model/chip.ply | 216 -- .../external/swd_probe/model/convert.py | 39 - .../external/swd_probe/model/model_chip.h | 108 - .../external/swd_probe/swd_probe_app.c | 3187 ----------------- .../external/swd_probe/swd_probe_app.h | 244 -- applications/external/swd_probe/usb_uart.c | 229 -- applications/external/swd_probe/usb_uart.h | 29 - .../external/tetris_game/application.fam | 14 - .../external/tetris_game/tetris_10px.png | Bin 1625 -> 0 bytes .../external/tetris_game/tetris_game.c | 480 --- applications/external/text_viewer/LICENSE | 21 - applications/external/text_viewer/README.md | 9 - .../external/text_viewer/application.fam | 18 - .../external/text_viewer/icons/text_10px.png | Bin 158 -> 0 bytes .../external/text_viewer/text_viewer.c | 287 -- .../text_viewer/textviewerflipper.PNG | Bin 11796 -> 0 bytes .../external/tictactoe_game/application.fam | 14 - .../tictactoe_game/tictactoe_10px.png | Bin 1605 -> 0 bytes .../external/tictactoe_game/tictactoe_game.c | 387 -- applications/external/totp/LICENSE | 21 - applications/external/totp/application.fam | 59 - applications/external/totp/cli/cli.c | 100 - applications/external/totp/cli/cli.h | 19 - applications/external/totp/cli/cli_helpers.c | 123 - applications/external/totp/cli/cli_helpers.h | 116 - .../external/totp/cli/commands/add/add.c | 192 - .../external/totp/cli/commands/add/add.h | 17 - .../totp/cli/commands/automation/automation.c | 186 - .../totp/cli/commands/automation/automation.h | 15 - .../totp/cli/commands/delete/delete.c | 108 - .../totp/cli/commands/delete/delete.h | 16 - .../totp/cli/commands/details/details.c | 94 - .../totp/cli/commands/details/details.h | 14 - .../external/totp/cli/commands/help/help.c | 76 - .../external/totp/cli/commands/help/help.h | 14 - .../external/totp/cli/commands/list/list.c | 58 - .../external/totp/cli/commands/list/list.h | 14 - .../external/totp/cli/commands/move/move.c | 85 - .../external/totp/cli/commands/move/move.h | 15 - .../cli/commands/notification/notification.c | 105 - .../cli/commands/notification/notification.h | 14 - .../external/totp/cli/commands/pin/pin.c | 193 - .../external/totp/cli/commands/pin/pin.h | 14 - .../external/totp/cli/commands/reset/reset.c | 42 - .../external/totp/cli/commands/reset/reset.h | 13 - .../totp/cli/commands/timezone/timezone.c | 54 - .../totp/cli/commands/timezone/timezone.h | 15 - .../totp/cli/commands/update/update.c | 177 - .../totp/cli/commands/update/update.h | 14 - .../totp/cli/common_command_arguments.c | 141 - .../totp/cli/common_command_arguments.h | 106 - .../external/totp/config/app/config.h | 42 - .../external/totp/config/wolfssl/config.h | 34 - .../totp/images/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes .../external/totp/images/hid_ble_31x9.png | Bin 173 -> 0 bytes .../external/totp/images/hid_usb_31x9.png | Bin 174 -> 0 bytes .../totp/images/totp_arrow_left_8x9.png | Bin 149 -> 0 bytes .../totp/images/totp_arrow_right_8x9.png | Bin 149 -> 0 bytes .../external/totp/lib/base32/base32.c | 60 - .../external/totp/lib/base32/base32.h | 40 - .../external/totp/lib/base64/base64.c | 73 - .../external/totp/lib/base64/base64.h | 14 - .../totp/lib/fonts/712serif/712serif.c | 941 ----- .../totp/lib/fonts/712serif/712serif.h | 8 - .../external/totp/lib/fonts/available_fonts.c | 23 - .../external/totp/lib/fonts/available_fonts.h | 7 - .../totp/lib/fonts/bedstead/bedstead.c | 1057 ------ .../totp/lib/fonts/bedstead/bedstead.h | 8 - .../external/totp/lib/fonts/dpcomic/dpcomic.c | 1115 ------ .../external/totp/lib/fonts/dpcomic/dpcomic.h | 7 - .../external/totp/lib/fonts/font_info.h | 24 - .../totp/lib/fonts/funclimbing/funclimbing.c | 1173 ------ .../totp/lib/fonts/funclimbing/funclimbing.h | 7 - .../totp/lib/fonts/graph35pix/graph35pix.c | 941 ----- .../totp/lib/fonts/graph35pix/graph35pix.h | 8 - .../lib/fonts/karma_future/karma_future.c | 1173 ------ .../lib/fonts/karma_future/karma_future.h | 8 - .../totp/lib/fonts/mode_nine/mode_nine.c | 942 ----- .../totp/lib/fonts/mode_nine/mode_nine.h | 8 - .../totp/lib/fonts/pixelflag/pixelflag.c | 1115 ------ .../totp/lib/fonts/pixelflag/pixelflag.h | 7 - .../totp/lib/fonts/redhat_mono/redhat_mono.c | 1058 ------ .../totp/lib/fonts/redhat_mono/redhat_mono.h | 8 - .../external/totp/lib/fonts/zector/zector.c | 1057 ------ .../external/totp/lib/fonts/zector/zector.h | 8 - .../external/totp/lib/polyfills/memset_s.c | 22 - .../external/totp/lib/polyfills/memset_s.h | 23 - .../external/totp/lib/polyfills/strnlen.c | 11 - .../external/totp/lib/polyfills/strnlen.h | 6 - .../external/totp/lib/roll_value/roll_value.c | 28 - .../external/totp/lib/roll_value/roll_value.h | 59 - .../totp/lib/timezone_utils/timezone_utils.c | 16 - .../totp/lib/timezone_utils/timezone_utils.h | 18 - applications/external/totp/lib/wolfssl | 1 - .../external/totp/services/config/config.c | 728 ---- .../external/totp/services/config/config.h | 104 - .../services/config/config_file_context.h | 3 - .../external/totp/services/config/constants.h | 24 - .../config/migrations/common_migration.c | 199 - .../config/migrations/common_migration.h | 13 - .../services/config/token_info_iterator.c | 552 --- .../services/config/token_info_iterator.h | 123 - .../external/totp/services/convert/convert.h | 4 - .../totp/services/crypto/common_types.h | 23 - .../external/totp/services/crypto/constants.h | 14 - .../totp/services/crypto/crypto_facade.c | 118 - .../totp/services/crypto/crypto_facade.h | 59 - .../external/totp/services/crypto/crypto_v1.c | 145 - .../external/totp/services/crypto/crypto_v1.h | 55 - .../external/totp/services/crypto/crypto_v2.c | 190 - .../external/totp/services/crypto/crypto_v2.h | 55 - .../external/totp/services/crypto/crypto_v3.c | 195 - .../external/totp/services/crypto/crypto_v3.h | 52 - .../external/totp/services/crypto/polyfills.h | 14 - .../totp/services/idle_timeout/idle_timeout.c | 66 - .../totp/services/idle_timeout/idle_timeout.h | 44 - .../external/totp/services/totp/totp.c | 125 - .../external/totp/services/totp/totp.h | 55 - applications/external/totp/totp_10px.png | Bin 496 -> 0 bytes applications/external/totp/totp_app.c | 288 -- .../totp/types/automation_kb_layout.h | 8 - .../external/totp/types/automation_method.h | 13 - applications/external/totp/types/common.c | 3 - applications/external/totp/types/common.h | 3 - .../external/totp/types/crypto_settings.h | 41 - applications/external/totp/types/event_type.h | 6 - .../external/totp/types/notification_method.h | 9 - .../external/totp/types/plugin_event.h | 10 - .../external/totp/types/plugin_state.h | 93 - applications/external/totp/types/token_info.c | 207 -- applications/external/totp/types/token_info.h | 272 -- .../external/totp/types/user_pin_codes.h | 10 - .../external/totp/ui/canvas_extensions.c | 33 - .../external/totp/ui/canvas_extensions.h | 22 - .../external/totp/ui/common_dialogs.c | 20 - .../external/totp/ui/common_dialogs.h | 18 - applications/external/totp/ui/constants.h | 6 - .../external/totp/ui/scene_director.c | 134 - .../external/totp/ui/scene_director.h | 41 - .../ui/scenes/add_new_token/totp_input_text.c | 54 - .../ui/scenes/add_new_token/totp_input_text.h | 16 - .../add_new_token/totp_scene_add_new_token.c | 321 -- .../add_new_token/totp_scene_add_new_token.h | 15 - .../scenes/app_settings/totp_app_settings.c | 373 -- .../scenes/app_settings/totp_app_settings.h | 12 - .../authenticate/totp_scene_authenticate.c | 166 - .../authenticate/totp_scene_authenticate.h | 12 - .../totp_scene_generate_token.c | 432 --- .../totp_scene_generate_token.h | 12 - .../external/totp/ui/scenes/standby/standby.c | 12 - .../external/totp/ui/scenes/standby/standby.h | 5 - .../scenes/token_menu/totp_scene_token_menu.c | 193 - .../scenes/token_menu/totp_scene_token_menu.h | 10 - .../external/totp/ui/totp_scenes_enum.h | 47 - applications/external/totp/ui/ui_controls.c | 138 - applications/external/totp/ui/ui_controls.h | 72 - applications/external/totp/version.h | 5 - .../totp/workers/bt_type_code/bt_type_code.c | 235 -- .../totp/workers/bt_type_code/bt_type_code.h | 82 - .../generate_totp_code/generate_totp_code.c | 199 - .../generate_totp_code/generate_totp_code.h | 86 - .../external/totp/workers/type_code_common.c | 109 - .../external/totp/workers/type_code_common.h | 23 - .../workers/usb_type_code/usb_type_code.c | 122 - .../workers/usb_type_code/usb_type_code.h | 62 - applications/external/uart_terminal/LICENSE | 22 - applications/external/uart_terminal/README.md | 50 - .../external/uart_terminal/application.fam | 15 - .../assets/KeyBackspaceSelected_16x9.png | Bin 1812 -> 0 bytes .../assets/KeyBackspace_16x9.png | Bin 1829 -> 0 bytes .../assets/KeySaveSelected_24x11.png | Bin 1853 -> 0 bytes .../uart_terminal/assets/KeySave_24x11.png | Bin 1863 -> 0 bytes .../assets/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../scenes/uart_terminal_scene.c | 30 - .../scenes/uart_terminal_scene.h | 29 - .../scenes/uart_terminal_scene_config.h | 3 - .../uart_terminal_scene_console_output.c | 148 - .../scenes/uart_terminal_scene_start.c | 138 - .../scenes/uart_terminal_scene_text_input.c | 66 - .../external/uart_terminal/uart_terminal.png | Bin 1883 -> 0 bytes .../uart_terminal/uart_terminal_app.c | 104 - .../uart_terminal/uart_terminal_app.h | 11 - .../uart_terminal/uart_terminal_app_i.h | 50 - .../uart_terminal_custom_event.h | 7 - .../uart_terminal/uart_terminal_uart.c | 97 - .../uart_terminal/uart_terminal_uart.h | 14 - .../external/uart_terminal/uart_text_input.c | 683 ---- .../external/uart_terminal/uart_text_input.h | 82 - .../external/uart_terminal/uart_validators.c | 57 - .../external/uart_terminal/uart_validators.h | 21 - applications/external/unitemp/LICENSE.md | 674 ---- applications/external/unitemp/README.md | 24 - applications/external/unitemp/Sensors.c | 653 ---- applications/external/unitemp/Sensors.h | 339 -- applications/external/unitemp/application.fam | 19 - .../external/unitemp/assets/README.MD | 3 - .../external/unitemp/assets/co2_11x14.png | Bin 176 -> 0 bytes .../unitemp/assets/flipper_happy_2_60x38.png | Bin 564 -> 0 bytes .../unitemp/assets/flipper_happy_60x38.png | Bin 563 -> 0 bytes .../unitemp/assets/flipper_sad_60x38.png | Bin 560 -> 0 bytes .../unitemp/assets/heat_index_11x14.png | Bin 1239 -> 0 bytes .../external/unitemp/assets/hum_9x15.png | Bin 198 -> 0 bytes .../external/unitemp/assets/in_hg_15x15.png | Bin 219 -> 0 bytes .../external/unitemp/assets/mm_hg_15x15.png | Bin 207 -> 0 bytes .../external/unitemp/assets/pressure_7x13.png | Bin 169 -> 0 bytes .../external/unitemp/assets/repo_qr_50x50.png | Bin 700 -> 0 bytes .../external/unitemp/assets/sherlok_53x45.png | Bin 695 -> 0 bytes .../external/unitemp/assets/temp_C_11x14.png | Bin 214 -> 0 bytes .../external/unitemp/assets/temp_F_11x14.png | Bin 210 -> 0 bytes applications/external/unitemp/icon.png | Bin 1826 -> 0 bytes .../external/unitemp/interfaces/I2CSensor.c | 131 - .../external/unitemp/interfaces/I2CSensor.h | 128 - .../unitemp/interfaces/OneWireSensor.c | 367 -- .../unitemp/interfaces/OneWireSensor.h | 226 -- .../external/unitemp/interfaces/SPISensor.c | 89 - .../external/unitemp/interfaces/SPISensor.h | 66 - .../unitemp/interfaces/SingleWireSensor.c | 279 -- .../unitemp/interfaces/SingleWireSensor.h | 92 - .../external/unitemp/interfaces/endianness.h | 60 - .../external/unitemp/sensors/AM2320.c | 106 - .../external/unitemp/sensors/AM2320.h | 62 - .../external/unitemp/sensors/BME680.c | 431 --- .../external/unitemp/sensors/BME680.h | 112 - .../external/unitemp/sensors/BMP180.c | 171 - .../external/unitemp/sensors/BMP180.h | 62 - .../external/unitemp/sensors/BMx280.c | 345 -- .../external/unitemp/sensors/BMx280.h | 102 - applications/external/unitemp/sensors/DHT20.c | 154 - applications/external/unitemp/sensors/DHT20.h | 63 - .../external/unitemp/sensors/HDC1080.c | 94 - .../external/unitemp/sensors/HDC1080.h | 62 - .../external/unitemp/sensors/HTU21x.c | 107 - .../external/unitemp/sensors/HTU21x.h | 62 - applications/external/unitemp/sensors/LM75.c | 87 - applications/external/unitemp/sensors/LM75.h | 62 - .../external/unitemp/sensors/MAX31855.c | 93 - .../external/unitemp/sensors/MAX31855.h | 65 - .../external/unitemp/sensors/MAX6675.c | 81 - .../external/unitemp/sensors/MAX6675.h | 65 - applications/external/unitemp/sensors/SCD30.c | 387 -- applications/external/unitemp/sensors/SCD30.h | 59 - applications/external/unitemp/sensors/SCD40.c | 291 -- applications/external/unitemp/sensors/SCD40.h | 59 - applications/external/unitemp/sensors/SHT30.c | 90 - applications/external/unitemp/sensors/SHT30.h | 70 - .../external/unitemp/sensors/Sensors.xlsx | Bin 12642 -> 0 bytes applications/external/unitemp/unitemp.c | 342 -- applications/external/unitemp/unitemp.h | 170 - .../external/unitemp/views/General_view.c | 687 ---- .../external/unitemp/views/MainMenu_view.c | 99 - .../external/unitemp/views/Popup_view.c | 49 - .../unitemp/views/SensorActions_view.c | 125 - .../external/unitemp/views/SensorEdit_view.c | 383 -- .../unitemp/views/SensorNameEdit_view.c | 47 - .../external/unitemp/views/SensorsList_view.c | 163 - .../external/unitemp/views/Settings_view.c | 167 - .../external/unitemp/views/UnitempViews.h | 94 - .../external/unitemp/views/Widgets_view.c | 205 -- applications/external/wav_player/README.md | 6 - .../external/wav_player/application.fam | 14 - .../external/wav_player/images/music_10px.png | Bin 142 -> 0 bytes applications/external/wav_player/wav_10px.png | Bin 1777 -> 0 bytes applications/external/wav_player/wav_parser.c | 88 - applications/external/wav_player/wav_parser.h | 89 - applications/external/wav_player/wav_player.c | 462 --- .../external/wav_player/wav_player_hal.c | 111 - .../external/wav_player/wav_player_hal.h | 25 - .../external/wav_player/wav_player_view.c | 202 -- .../external/wav_player/wav_player_view.h | 78 - .../external/weather_station/application.fam | 13 - .../helpers/radio_device_loader.c | 69 - .../helpers/radio_device_loader.h | 17 - .../helpers/weather_station_event.h | 14 - .../helpers/weather_station_types.h | 49 - .../weather_station/images/Fishing_123x52.png | Bin 966 -> 0 bytes .../weather_station/images/Humid_10x15.png | Bin 3624 -> 0 bytes .../weather_station/images/Humid_8x13.png | Bin 3618 -> 0 bytes .../weather_station/images/Lock_7x8.png | Bin 3597 -> 0 bytes .../images/Pin_back_arrow_10x8.png | Bin 3606 -> 0 bytes .../weather_station/images/Quest_7x8.png | Bin 3675 -> 0 bytes .../images/Scanning_123x52.png | Bin 1690 -> 0 bytes .../weather_station/images/Therm_7x16.png | Bin 3611 -> 0 bytes .../weather_station/images/Timer_11x11.png | Bin 3616 -> 0 bytes .../weather_station/images/Unlock_7x8.png | Bin 3598 -> 0 bytes .../images/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../weather_station/images/station_icon.png | Bin 3607 -> 0 bytes .../protocols/acurite_592txr.c | 298 -- .../protocols/acurite_592txr.h | 80 - .../weather_station/protocols/acurite_606tx.c | 239 -- .../weather_station/protocols/acurite_606tx.h | 80 - .../protocols/acurite_609txc.c | 239 -- .../protocols/acurite_609txc.h | 80 - .../protocols/ambient_weather.c | 268 -- .../protocols/ambient_weather.h | 80 - .../protocols/auriol_hg0601a.c | 248 -- .../protocols/auriol_hg0601a.h | 80 - .../weather_station/protocols/gt_wt_02.c | 255 -- .../weather_station/protocols/gt_wt_02.h | 80 - .../weather_station/protocols/gt_wt_03.c | 330 -- .../weather_station/protocols/gt_wt_03.h | 80 - .../weather_station/protocols/infactory.c | 286 -- .../weather_station/protocols/infactory.h | 80 - .../weather_station/protocols/lacrosse_tx.c | 319 -- .../weather_station/protocols/lacrosse_tx.h | 80 - .../protocols/lacrosse_tx141thbv2.c | 294 -- .../protocols/lacrosse_tx141thbv2.h | 81 - .../weather_station/protocols/nexus_th.c | 254 -- .../weather_station/protocols/nexus_th.h | 80 - .../weather_station/protocols/oregon2.c | 429 --- .../weather_station/protocols/oregon2.h | 6 - .../weather_station/protocols/oregon3.c | 365 -- .../weather_station/protocols/oregon3.h | 6 - .../weather_station/protocols/oregon_v1.c | 321 -- .../weather_station/protocols/oregon_v1.h | 80 - .../protocols/protocol_items.c | 25 - .../protocols/protocol_items.h | 22 - .../weather_station/protocols/thermopro_tx4.c | 251 -- .../weather_station/protocols/thermopro_tx4.h | 80 - .../weather_station/protocols/tx_8300.c | 284 -- .../weather_station/protocols/tx_8300.h | 80 - .../weather_station/protocols/wendox_w6726.c | 299 -- .../weather_station/protocols/wendox_w6726.h | 80 - .../weather_station/protocols/ws_generic.c | 256 -- .../weather_station/protocols/ws_generic.h | 81 - .../scenes/weather_station_receiver.c | 216 -- .../scenes/weather_station_scene.c | 30 - .../scenes/weather_station_scene.h | 29 - .../scenes/weather_station_scene_about.c | 78 - .../scenes/weather_station_scene_config.h | 5 - .../weather_station_scene_receiver_config.c | 223 -- .../weather_station_scene_receiver_info.c | 50 - .../scenes/weather_station_scene_start.c | 58 - .../views/weather_station_receiver.c | 472 --- .../views/weather_station_receiver.h | 39 - .../views/weather_station_receiver_info.c | 245 -- .../views/weather_station_receiver_info.h | 16 - .../weather_station/weather_station_10px.png | Bin 175 -> 0 bytes .../weather_station/weather_station_app.c | 188 - .../weather_station/weather_station_app_i.c | 155 - .../weather_station/weather_station_app_i.h | 76 - .../weather_station/weather_station_history.c | 245 -- .../weather_station/weather_station_history.h | 112 - .../external/wifi_marauder_companion/LICENSE | 674 ---- .../wifi_marauder_companion/ReadMe.md | 35 - .../wifi_marauder_companion/application.fam | 13 - .../assets/DolphinCommon_56x48.png | Bin 1416 -> 0 bytes .../assets/KeyBackspaceSelected_16x9.png | Bin 1812 -> 0 bytes .../assets/KeyBackspace_16x9.png | Bin 1829 -> 0 bytes .../assets/KeyKeyboardSelected_10x11.png | Bin 7210 -> 0 bytes .../assets/KeyKeyboard_10x11.png | Bin 7763 -> 0 bytes .../assets/KeySaveSelected_24x11.png | Bin 1853 -> 0 bytes .../assets/KeySave_24x11.png | Bin 1863 -> 0 bytes .../assets/Text_10x10.png | Bin 158 -> 0 bytes .../assets/WarningDolphin_45x42.png | Bin 1139 -> 0 bytes .../wifi_marauder_companion/docs/README.md | 9 - .../wifi_marauder_companion/docs/changelog.md | 12 - .../file/sequential_file.c | 46 - .../file/sequential_file.h | 15 - .../scenes/wifi_marauder_scene.c | 30 - .../scenes/wifi_marauder_scene.h | 29 - .../scenes/wifi_marauder_scene_config.h | 15 - .../wifi_marauder_scene_console_output.c | 202 -- .../scenes/wifi_marauder_scene_log_viewer.c | 187 - ...ifi_marauder_scene_script_confirm_delete.c | 83 - .../scenes/wifi_marauder_scene_script_edit.c | 125 - .../wifi_marauder_scene_script_edit_list.c | 188 - .../wifi_marauder_scene_script_options.c | 111 - .../wifi_marauder_scene_script_select.c | 90 - .../wifi_marauder_scene_script_settings.c | 87 - .../wifi_marauder_scene_script_stage_add.c | 297 -- .../wifi_marauder_scene_script_stage_edit.c | 203 -- .../wifi_marauder_scene_settings_init.c | 130 - .../wifi_marauder_scene_sniffpmkid_options.c | 117 - .../scenes/wifi_marauder_scene_start.c | 287 -- .../scenes/wifi_marauder_scene_text_input.c | 154 - .../scenes/wifi_marauder_scene_user_input.c | 155 - .../screenshots/marauder-save-pcaps.png | Bin 2336 -> 0 bytes .../screenshots/marauder-script-demo.png | Bin 1599 -> 0 bytes .../screenshots/marauder-topmenu.png | Bin 1752 -> 0 bytes .../wifi_marauder_companion/script/cJSON.c | 2743 -------------- .../wifi_marauder_companion/script/cJSON.h | 321 -- .../menu/wifi_marauder_script_stage_menu.c | 32 - .../menu/wifi_marauder_script_stage_menu.h | 42 - ...wifi_marauder_script_stage_menu_beaconap.c | 27 - ...fi_marauder_script_stage_menu_beaconlist.c | 59 - .../wifi_marauder_script_stage_menu_config.h | 14 - .../wifi_marauder_script_stage_menu_deauth.c | 27 - .../wifi_marauder_script_stage_menu_delay.c | 27 - .../wifi_marauder_script_stage_menu_exec.c | 30 - .../wifi_marauder_script_stage_menu_probe.c | 27 - .../wifi_marauder_script_stage_menu_scan.c | 93 - .../wifi_marauder_script_stage_menu_select.c | 95 - ...i_marauder_script_stage_menu_sniffbeacon.c | 27 - ...i_marauder_script_stage_menu_sniffdeauth.c | 27 - ...wifi_marauder_script_stage_menu_sniffesp.c | 27 - ...fi_marauder_script_stage_menu_sniffpmkid.c | 91 - ...wifi_marauder_script_stage_menu_sniffpwn.c | 27 - ...wifi_marauder_script_stage_menu_sniffraw.c | 27 - .../script/wifi_marauder_script.c | 947 ----- .../script/wifi_marauder_script.h | 257 -- .../script/wifi_marauder_script_executor.c | 307 -- .../script/wifi_marauder_script_executor.h | 6 - .../script/wifi_marauder_script_worker.c | 74 - .../script/wifi_marauder_script_worker.h | 43 - .../wifi_marauder_companion/wifi_10px.png | Bin 1781 -> 0 bytes .../wifi_marauder_app.c | 201 -- .../wifi_marauder_app.h | 13 - .../wifi_marauder_app_i.h | 145 - .../wifi_marauder_custom_event.h | 13 - .../wifi_marauder_text_input.c | 756 ---- .../wifi_marauder_text_input.h | 83 - .../wifi_marauder_uart.c | 115 - .../wifi_marauder_uart.h | 16 - .../wifi_marauder_validators.c | 57 - .../wifi_marauder_validators.h | 21 - .../FlipperZeroWiFiModuleDefines.h | 17 - .../external/wifi_scanner/application.fam | 14 - .../external/wifi_scanner/wifi_10px.png | Bin 1781 -> 0 bytes .../external/wifi_scanner/wifi_scanner.c | 1070 ------ applications/external/zombiez/application.fam | 14 - applications/external/zombiez/zombie_10px.png | Bin 8741 -> 0 bytes applications/external/zombiez/zombiez.c | 402 --- applications/external/zombiez/zombiez.h | 62 - 1426 files changed, 144124 deletions(-) delete mode 100644 applications/external/application.fam delete mode 100644 applications/external/arkanoid/application.fam delete mode 100644 applications/external/arkanoid/arkanoid_10px.png delete mode 100644 applications/external/arkanoid/arkanoid_game.c delete mode 100644 applications/external/avr_isp_programmer/application.fam delete mode 100644 applications/external/avr_isp_programmer/avr_app_icon_10x10.png delete mode 100644 applications/external/avr_isp_programmer/avr_isp_app.c delete mode 100644 applications/external/avr_isp_programmer/avr_isp_app_i.c delete mode 100644 applications/external/avr_isp_programmer/avr_isp_app_i.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp.c delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_event.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_types.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker.c delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker.h delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c delete mode 100644 applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h delete mode 100644 applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c delete mode 100644 applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h delete mode 100644 applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png delete mode 100644 applications/external/avr_isp_programmer/images/avr_wiring.png delete mode 100644 applications/external/avr_isp_programmer/images/chif_not_found_83x37.png delete mode 100644 applications/external/avr_isp_programmer/images/chip_error_70x22.png delete mode 100644 applications/external/avr_isp_programmer/images/chip_long_70x22.png delete mode 100644 applications/external/avr_isp_programmer/images/chip_not_found_83x37.png delete mode 100644 applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png delete mode 100644 applications/external/avr_isp_programmer/images/isp_active_128x53.png delete mode 100644 applications/external/avr_isp_programmer/images/link_waiting_77x56.png delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.c delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c delete mode 100644 applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h delete mode 100644 applications/external/avr_isp_programmer/lib/driver/clock.png delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene.h delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c delete mode 100644 applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_reader.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_reader.h delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_writer.c delete mode 100644 applications/external/avr_isp_programmer/views/avr_isp_view_writer.h delete mode 100644 applications/external/bad_bt/application.fam delete mode 100644 applications/external/bad_bt/bad_bt_app.c delete mode 100644 applications/external/bad_bt/bad_bt_app.h delete mode 100644 applications/external/bad_bt/helpers/ducky_script.c delete mode 100644 applications/external/bad_bt/helpers/ducky_script.h delete mode 100644 applications/external/bad_bt/helpers/ducky_script_commands.c delete mode 100644 applications/external/bad_bt/helpers/ducky_script_i.h delete mode 100644 applications/external/bad_bt/helpers/ducky_script_keycodes.c delete mode 100644 applications/external/bad_bt/images/badbt_10px.png delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene.c delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene.h delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config.c delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config.h delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config_layout.c delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config_mac.c delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_config_name.c delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_error.c delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_file_select.c delete mode 100644 applications/external/bad_bt/scenes/bad_bt_scene_work.c delete mode 100644 applications/external/bad_bt/views/bad_bt_view.c delete mode 100644 applications/external/bad_bt/views/bad_bt_view.h delete mode 100644 applications/external/barcode_gen/LICENSE delete mode 100644 applications/external/barcode_gen/README.md delete mode 100644 applications/external/barcode_gen/application.fam delete mode 100644 applications/external/barcode_gen/barcode_app.c delete mode 100644 applications/external/barcode_gen/barcode_app.h delete mode 100644 applications/external/barcode_gen/barcode_encoding_files/codabar_encodings.txt delete mode 100644 applications/external/barcode_gen/barcode_encoding_files/code128_encodings.txt delete mode 100644 applications/external/barcode_gen/barcode_encoding_files/code128c_encodings.txt delete mode 100644 applications/external/barcode_gen/barcode_encoding_files/code39_encodings.txt delete mode 100644 applications/external/barcode_gen/barcode_utils.c delete mode 100644 applications/external/barcode_gen/barcode_utils.h delete mode 100644 applications/external/barcode_gen/barcode_validator.c delete mode 100644 applications/external/barcode_gen/barcode_validator.h delete mode 100644 applications/external/barcode_gen/encodings.c delete mode 100644 applications/external/barcode_gen/encodings.h delete mode 100644 applications/external/barcode_gen/images/barcode_10.png delete mode 100644 applications/external/barcode_gen/screenshots/Codabar Data Example.png delete mode 100644 applications/external/barcode_gen/screenshots/Creating Barcode.png delete mode 100644 applications/external/barcode_gen/screenshots/Flipper Barcode.png delete mode 100644 applications/external/barcode_gen/screenshots/Flipper Box Barcode.png delete mode 100644 applications/external/barcode_gen/views/barcode_view.c delete mode 100644 applications/external/barcode_gen/views/barcode_view.h delete mode 100644 applications/external/barcode_gen/views/create_view.c delete mode 100644 applications/external/barcode_gen/views/create_view.h delete mode 100644 applications/external/barcode_gen/views/message_view.c delete mode 100644 applications/external/barcode_gen/views/message_view.h delete mode 100644 applications/external/blackjack/application.fam delete mode 100644 applications/external/blackjack/assets/blackjack.png delete mode 100644 applications/external/blackjack/assets/card_graphics.png delete mode 100644 applications/external/blackjack/assets/endscreen.png delete mode 100644 applications/external/blackjack/blackjack.c delete mode 100644 applications/external/blackjack/blackjack_10px.png delete mode 100644 applications/external/blackjack/common/card.c delete mode 100644 applications/external/blackjack/common/card.h delete mode 100644 applications/external/blackjack/common/dml.c delete mode 100644 applications/external/blackjack/common/dml.h delete mode 100644 applications/external/blackjack/common/menu.c delete mode 100644 applications/external/blackjack/common/menu.h delete mode 100644 applications/external/blackjack/common/queue.c delete mode 100644 applications/external/blackjack/common/queue.h delete mode 100644 applications/external/blackjack/common/ui.c delete mode 100644 applications/external/blackjack/common/ui.h delete mode 100644 applications/external/blackjack/defines.h delete mode 100644 applications/external/blackjack/ui.c delete mode 100644 applications/external/blackjack/ui.h delete mode 100644 applications/external/blackjack/util.c delete mode 100644 applications/external/blackjack/util.h delete mode 100644 applications/external/bomberduck/LICENSE delete mode 100644 applications/external/bomberduck/README.md delete mode 100644 applications/external/bomberduck/application.fam delete mode 100644 applications/external/bomberduck/assets/bomb0.png delete mode 100644 applications/external/bomberduck/assets/bomb1.png delete mode 100644 applications/external/bomberduck/assets/bomb2.png delete mode 100644 applications/external/bomberduck/assets/box.png delete mode 100644 applications/external/bomberduck/assets/end.png delete mode 100644 applications/external/bomberduck/assets/enemy1.png delete mode 100644 applications/external/bomberduck/assets/enemyleft.png delete mode 100644 applications/external/bomberduck/assets/enemyright.png delete mode 100644 applications/external/bomberduck/assets/explore.png delete mode 100644 applications/external/bomberduck/assets/playerleft.png delete mode 100644 applications/external/bomberduck/assets/playerright.png delete mode 100644 applications/external/bomberduck/assets/unbreakbox.png delete mode 100644 applications/external/bomberduck/bomb.png delete mode 100644 applications/external/bomberduck/bomberduck.c delete mode 100644 applications/external/camera_suite/application.fam delete mode 100644 applications/external/camera_suite/camera_suite.c delete mode 100644 applications/external/camera_suite/camera_suite.h delete mode 100644 applications/external/camera_suite/docs/CHANGELOG.md delete mode 100644 applications/external/camera_suite/docs/README.md delete mode 100644 applications/external/camera_suite/helpers/camera_suite_custom_event.h delete mode 100644 applications/external/camera_suite/helpers/camera_suite_haptic.c delete mode 100644 applications/external/camera_suite/helpers/camera_suite_haptic.h delete mode 100644 applications/external/camera_suite/helpers/camera_suite_led.c delete mode 100644 applications/external/camera_suite/helpers/camera_suite_led.h delete mode 100644 applications/external/camera_suite/helpers/camera_suite_speaker.c delete mode 100644 applications/external/camera_suite/helpers/camera_suite_speaker.h delete mode 100644 applications/external/camera_suite/helpers/camera_suite_storage.c delete mode 100644 applications/external/camera_suite/helpers/camera_suite_storage.h delete mode 100644 applications/external/camera_suite/icons/camera_suite.png delete mode 100644 applications/external/camera_suite/scenes/camera_suite_scene.c delete mode 100644 applications/external/camera_suite/scenes/camera_suite_scene.h delete mode 100644 applications/external/camera_suite/scenes/camera_suite_scene_camera.c delete mode 100644 applications/external/camera_suite/scenes/camera_suite_scene_config.h delete mode 100644 applications/external/camera_suite/scenes/camera_suite_scene_guide.c delete mode 100644 applications/external/camera_suite/scenes/camera_suite_scene_menu.c delete mode 100644 applications/external/camera_suite/scenes/camera_suite_scene_settings.c delete mode 100644 applications/external/camera_suite/scenes/camera_suite_scene_start.c delete mode 100644 applications/external/camera_suite/screenshots/camera_preview.png delete mode 100644 applications/external/camera_suite/screenshots/guide.png delete mode 100644 applications/external/camera_suite/screenshots/main_menu.png delete mode 100644 applications/external/camera_suite/screenshots/settings.png delete mode 100644 applications/external/camera_suite/screenshots/start_screen.png delete mode 100644 applications/external/camera_suite/views/camera_suite_view_camera.c delete mode 100644 applications/external/camera_suite/views/camera_suite_view_camera.h delete mode 100644 applications/external/camera_suite/views/camera_suite_view_guide.c delete mode 100644 applications/external/camera_suite/views/camera_suite_view_guide.h delete mode 100644 applications/external/camera_suite/views/camera_suite_view_start.c delete mode 100644 applications/external/camera_suite/views/camera_suite_view_start.h delete mode 100644 applications/external/dap_link/README.md delete mode 100644 applications/external/dap_link/application.fam delete mode 100644 applications/external/dap_link/dap_config.h delete mode 100644 applications/external/dap_link/dap_link.c delete mode 100644 applications/external/dap_link/dap_link.h delete mode 100644 applications/external/dap_link/dap_link.png delete mode 100644 applications/external/dap_link/gui/dap_gui.c delete mode 100644 applications/external/dap_link/gui/dap_gui.h delete mode 100644 applications/external/dap_link/gui/dap_gui_custom_event.h delete mode 100644 applications/external/dap_link/gui/dap_gui_i.h delete mode 100644 applications/external/dap_link/gui/scenes/config/dap_scene.c delete mode 100644 applications/external/dap_link/gui/scenes/config/dap_scene.h delete mode 100644 applications/external/dap_link/gui/scenes/config/dap_scene_config.h delete mode 100644 applications/external/dap_link/gui/scenes/dap_scene_about.c delete mode 100644 applications/external/dap_link/gui/scenes/dap_scene_config.c delete mode 100644 applications/external/dap_link/gui/scenes/dap_scene_help.c delete mode 100644 applications/external/dap_link/gui/scenes/dap_scene_main.c delete mode 100644 applications/external/dap_link/gui/views/dap_main_view.c delete mode 100644 applications/external/dap_link/gui/views/dap_main_view.h delete mode 100644 applications/external/dap_link/icons/ActiveConnection_50x64.png delete mode 100644 applications/external/dap_link/icons/ArrowUpEmpty_12x18.png delete mode 100644 applications/external/dap_link/icons/ArrowUpFilled_12x18.png delete mode 160000 applications/external/dap_link/lib/free-dap delete mode 100644 applications/external/dap_link/usb/dap_v2_usb.c delete mode 100644 applications/external/dap_link/usb/dap_v2_usb.h delete mode 100644 applications/external/dap_link/usb/usb_winusb.h delete mode 100644 applications/external/doom/.gitignore delete mode 100644 applications/external/doom/README.md delete mode 100644 applications/external/doom/application.fam delete mode 100644 applications/external/doom/assets.c delete mode 100644 applications/external/doom/assets.h delete mode 100644 applications/external/doom/assets/door2.png delete mode 100644 applications/external/doom/assets/door_inv.png delete mode 100644 applications/external/doom/assets/fire_inv.png delete mode 100644 applications/external/doom/assets/fireball_inv.png delete mode 100644 applications/external/doom/assets/fireball_mask_inv.png delete mode 100644 applications/external/doom/assets/gradient_inv.png delete mode 100644 applications/external/doom/assets/gun_inv.png delete mode 100644 applications/external/doom/assets/gun_mask_inv.png delete mode 100644 applications/external/doom/assets/imp_inv.png delete mode 100644 applications/external/doom/assets/imp_mask_inv.png delete mode 100644 applications/external/doom/assets/item_inv.png delete mode 100644 applications/external/doom/assets/item_mask_inv.png delete mode 100644 applications/external/doom/assets/logo_inv.png delete mode 100644 applications/external/doom/constants.h delete mode 100644 applications/external/doom/display.h delete mode 100644 applications/external/doom/doom.c delete mode 100644 applications/external/doom/doom_10px.png delete mode 100644 applications/external/doom/doom_music_player_worker.c delete mode 100644 applications/external/doom/doom_music_player_worker.h delete mode 100644 applications/external/doom/entities.c delete mode 100644 applications/external/doom/entities.h delete mode 100644 applications/external/doom/img/1.png delete mode 100644 applications/external/doom/img/2.png delete mode 100644 applications/external/doom/img/3.png delete mode 100644 applications/external/doom/level.h delete mode 100644 applications/external/doom/sound.h delete mode 100644 applications/external/doom/types.c delete mode 100644 applications/external/doom/types.h delete mode 100644 applications/external/dtmf_dolphin/LICENSE delete mode 100644 applications/external/dtmf_dolphin/README.md delete mode 100644 applications/external/dtmf_dolphin/application.fam delete mode 100644 applications/external/dtmf_dolphin/dtmf_dolphin.c delete mode 100644 applications/external/dtmf_dolphin/dtmf_dolphin_audio.c delete mode 100644 applications/external/dtmf_dolphin/dtmf_dolphin_audio.h delete mode 100644 applications/external/dtmf_dolphin/dtmf_dolphin_data.c delete mode 100644 applications/external/dtmf_dolphin/dtmf_dolphin_data.h delete mode 100644 applications/external/dtmf_dolphin/dtmf_dolphin_event.h delete mode 100644 applications/external/dtmf_dolphin/dtmf_dolphin_hal.c delete mode 100644 applications/external/dtmf_dolphin/dtmf_dolphin_hal.h delete mode 100644 applications/external/dtmf_dolphin/dtmf_dolphin_i.h delete mode 100644 applications/external/dtmf_dolphin/phone.png delete mode 100644 applications/external/dtmf_dolphin/pics/dialer.jpg delete mode 100644 applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.c delete mode 100644 applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene.h delete mode 100644 applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_config.h delete mode 100644 applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_dialer.c delete mode 100644 applications/external/dtmf_dolphin/scenes/dtmf_dolphin_scene_start.c delete mode 100644 applications/external/dtmf_dolphin/views/dtmf_dolphin_common.h delete mode 100644 applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.c delete mode 100644 applications/external/dtmf_dolphin/views/dtmf_dolphin_dialer.h delete mode 100644 applications/external/esp8266_deauth/FlipperZeroWiFiDeauthModuleDefines.h delete mode 100644 applications/external/esp8266_deauth/application.fam delete mode 100644 applications/external/esp8266_deauth/esp8266_deauth.c delete mode 100644 applications/external/esp8266_deauth/wifi_10px.png delete mode 100644 applications/external/flappy_bird/application.fam delete mode 100644 applications/external/flappy_bird/assets/bird_01.png delete mode 100644 applications/external/flappy_bird/assets/bird_02.png delete mode 100644 applications/external/flappy_bird/assets/bird_03.png delete mode 100644 applications/external/flappy_bird/flappy_10px.png delete mode 100644 applications/external/flappy_bird/flappy_bird.c delete mode 100644 applications/external/flipper_i2ctools/LICENSE delete mode 100644 applications/external/flipper_i2ctools/README.md delete mode 100644 applications/external/flipper_i2ctools/application.fam delete mode 100644 applications/external/flipper_i2ctools/i2cscanner.c delete mode 100644 applications/external/flipper_i2ctools/i2cscanner.h delete mode 100644 applications/external/flipper_i2ctools/i2csender.c delete mode 100644 applications/external/flipper_i2ctools/i2csender.h delete mode 100644 applications/external/flipper_i2ctools/i2csniffer.c delete mode 100644 applications/external/flipper_i2ctools/i2csniffer.h delete mode 100644 applications/external/flipper_i2ctools/i2ctools.c delete mode 100644 applications/external/flipper_i2ctools/i2ctools.gif delete mode 100644 applications/external/flipper_i2ctools/i2ctools.png delete mode 100644 applications/external/flipper_i2ctools/i2ctools_i.h delete mode 100644 applications/external/flipper_i2ctools/images/ButtonDown_7x4.png delete mode 100644 applications/external/flipper_i2ctools/images/ButtonLeft_4x7.png delete mode 100644 applications/external/flipper_i2ctools/images/ButtonRight_4x7.png delete mode 100644 applications/external/flipper_i2ctools/images/ButtonUp_7x4.png delete mode 100644 applications/external/flipper_i2ctools/images/Ok_btn_9x9.png delete mode 100644 applications/external/flipper_i2ctools/images/Voltage_16x16.png delete mode 100644 applications/external/flipper_i2ctools/images/i2ctools_main_76x59.png delete mode 100644 applications/external/flipper_i2ctools/views/infos_view.c delete mode 100644 applications/external/flipper_i2ctools/views/infos_view.h delete mode 100644 applications/external/flipper_i2ctools/views/main_view.c delete mode 100644 applications/external/flipper_i2ctools/views/main_view.h delete mode 100644 applications/external/flipper_i2ctools/views/scanner_view.c delete mode 100644 applications/external/flipper_i2ctools/views/scanner_view.h delete mode 100644 applications/external/flipper_i2ctools/views/sender_view.c delete mode 100644 applications/external/flipper_i2ctools/views/sender_view.h delete mode 100644 applications/external/flipper_i2ctools/views/sniffer_view.c delete mode 100644 applications/external/flipper_i2ctools/views/sniffer_view.h delete mode 100644 applications/external/game15/README.md delete mode 100644 applications/external/game15/application.fam delete mode 100644 applications/external/game15/game15.c delete mode 100644 applications/external/game15/game15_10px.png delete mode 100644 applications/external/game15/images/Game15.png delete mode 100644 applications/external/game15/images/Game15Popup.png delete mode 100644 applications/external/game15/images/Game15Restore.png delete mode 100644 applications/external/game15/sandbox.c delete mode 100644 applications/external/game15/sandbox.h delete mode 100644 applications/external/game_2048/LICENSE delete mode 100644 applications/external/game_2048/README.md delete mode 100644 applications/external/game_2048/application.fam delete mode 100644 applications/external/game_2048/array_utils.c delete mode 100644 applications/external/game_2048/array_utils.h delete mode 100644 applications/external/game_2048/digits.h delete mode 100644 applications/external/game_2048/game_2048.c delete mode 100644 applications/external/game_2048/game_2048.png delete mode 100644 applications/external/game_2048/images/screenshot1.png delete mode 100644 applications/external/game_2048/images/screenshot2.png delete mode 100644 applications/external/gps_nmea_uart/LICENSE delete mode 100644 applications/external/gps_nmea_uart/README.md delete mode 100644 applications/external/gps_nmea_uart/application.fam delete mode 100644 applications/external/gps_nmea_uart/gps.c delete mode 100644 applications/external/gps_nmea_uart/gps_10px.png delete mode 100644 applications/external/gps_nmea_uart/gps_uart.c delete mode 100644 applications/external/gps_nmea_uart/gps_uart.h delete mode 100644 applications/external/gps_nmea_uart/minmea.c delete mode 100644 applications/external/gps_nmea_uart/minmea.h delete mode 100644 applications/external/gps_nmea_uart/ui.png delete mode 100644 applications/external/gps_nmea_uart/wiring.png delete mode 100644 applications/external/hc_sr04/application.fam delete mode 100644 applications/external/hc_sr04/dist_sensor10px.png delete mode 100644 applications/external/hc_sr04/hc_sr04.c delete mode 100644 applications/external/heap_defence_game/application.fam delete mode 100644 applications/external/heap_defence_game/assets_images/Background_128x64.png delete mode 100644 applications/external/heap_defence_game/assets_images/Box1_10x10.png delete mode 100644 applications/external/heap_defence_game/assets_images/Box2_10x10.png delete mode 100644 applications/external/heap_defence_game/assets_images/Box3_10x10.png delete mode 100644 applications/external/heap_defence_game/assets_images/Box4_10x10.png delete mode 100644 applications/external/heap_defence_game/assets_images/Box5_10x10.png delete mode 100644 applications/external/heap_defence_game/assets_images/Box6p_10x10.png delete mode 100644 applications/external/heap_defence_game/assets_images/Box7p_10x10.png delete mode 100644 applications/external/heap_defence_game/assets_images/Box8p_10x10.png delete mode 100644 applications/external/heap_defence_game/assets_images/Game_over_128x64.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_01.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_02.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_03.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_04.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_05.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_06.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_07.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_game_over_128x64/frame_rate delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_01.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_02.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_block_left_10x20/frame_rate delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_01.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_02.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_block_right_10x20/frame_rate delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_01.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_02.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_03.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_04.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_left_10x20/frame_rate delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_01.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_02.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_03.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_04.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_person_right_10x20/frame_rate delete mode 100644 applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_01.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_02.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_03.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_04.png delete mode 100644 applications/external/heap_defence_game/assets_images/HD_start_128x64/frame_rate delete mode 100644 applications/external/heap_defence_game/assets_images/Person4_1_10x20.png delete mode 100644 applications/external/heap_defence_game/assets_images/Person4_2_10x20.png delete mode 100644 applications/external/heap_defence_game/assets_images/Person5_1_10x20.png delete mode 100644 applications/external/heap_defence_game/assets_images/Person5_2_10x20.png delete mode 100644 applications/external/heap_defence_game/assets_images/Start_128x64.png delete mode 100644 applications/external/heap_defence_game/box.png delete mode 100644 applications/external/heap_defence_game/heap_defence.c delete mode 100644 applications/external/heap_defence_game/hede_assets.c delete mode 100644 applications/external/heap_defence_game/hede_assets.h delete mode 100644 applications/external/hex_viewer/LICENSE delete mode 100644 applications/external/hex_viewer/application.fam delete mode 100644 applications/external/hex_viewer/hex_viewer.c delete mode 100644 applications/external/hex_viewer/icons/hex_10px.png delete mode 100644 applications/external/hid_app/application.fam delete mode 100644 applications/external/hid_app/assets/Arr_dwn_7x9.png delete mode 100644 applications/external/hid_app/assets/Arr_up_7x9.png delete mode 100644 applications/external/hid_app/assets/Ble_connected_15x15.png delete mode 100644 applications/external/hid_app/assets/Ble_disconnected_15x15.png delete mode 100644 applications/external/hid_app/assets/ButtonDown_7x4.png delete mode 100644 applications/external/hid_app/assets/ButtonF10_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF11_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF12_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF1_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF2_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF3_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF4_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF5_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF6_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF7_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF8_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonF9_5x8.png delete mode 100644 applications/external/hid_app/assets/ButtonLeft_4x7.png delete mode 100644 applications/external/hid_app/assets/ButtonRight_4x7.png delete mode 100644 applications/external/hid_app/assets/ButtonUp_7x4.png delete mode 100644 applications/external/hid_app/assets/Button_18x18.png delete mode 100644 applications/external/hid_app/assets/Circles_47x47.png delete mode 100644 applications/external/hid_app/assets/KB_key_Alt_17x10.png delete mode 100644 applications/external/hid_app/assets/KB_key_Cmd_17x10.png delete mode 100644 applications/external/hid_app/assets/KB_key_Ctl_17x10.png delete mode 100644 applications/external/hid_app/assets/KB_key_Del_17x10.png delete mode 100644 applications/external/hid_app/assets/KB_key_Esc_17x10.png delete mode 100644 applications/external/hid_app/assets/KB_key_Tab_17x10.png delete mode 100644 applications/external/hid_app/assets/Left_mouse_icon_9x9.png delete mode 100644 applications/external/hid_app/assets/Like_def_11x9.png delete mode 100644 applications/external/hid_app/assets/Like_pressed_17x17.png delete mode 100644 applications/external/hid_app/assets/Ok_btn_9x9.png delete mode 100644 applications/external/hid_app/assets/Ok_btn_pressed_13x13.png delete mode 100644 applications/external/hid_app/assets/OutCircles_70x51.png delete mode 100644 applications/external/hid_app/assets/Pause_icon_9x9.png delete mode 100644 applications/external/hid_app/assets/Pin_arrow_down_7x9.png delete mode 100644 applications/external/hid_app/assets/Pin_arrow_left_9x7.png delete mode 100644 applications/external/hid_app/assets/Pin_arrow_right_9x7.png delete mode 100644 applications/external/hid_app/assets/Pin_arrow_up_7x9.png delete mode 100644 applications/external/hid_app/assets/Pin_back_arrow_10x10.png delete mode 100644 applications/external/hid_app/assets/Pin_back_arrow_10x8.png delete mode 100644 applications/external/hid_app/assets/Pressed_Button_13x13.png delete mode 100644 applications/external/hid_app/assets/Pressed_Button_19x19.png delete mode 100644 applications/external/hid_app/assets/Right_mouse_icon_9x9.png delete mode 100644 applications/external/hid_app/assets/S_DOWN_31x15.png delete mode 100644 applications/external/hid_app/assets/S_LEFT_15x31.png delete mode 100644 applications/external/hid_app/assets/S_RIGHT_15x31.png delete mode 100644 applications/external/hid_app/assets/S_UP_31x15.png delete mode 100644 applications/external/hid_app/assets/Space_60x18.png delete mode 100644 applications/external/hid_app/assets/Space_65x18.png delete mode 100644 applications/external/hid_app/assets/Voldwn_6x6.png delete mode 100644 applications/external/hid_app/assets/Volup_8x6.png delete mode 100644 applications/external/hid_app/hid.c delete mode 100644 applications/external/hid_app/hid.h delete mode 100644 applications/external/hid_app/hid_ble_10px.png delete mode 100644 applications/external/hid_app/hid_usb_10px.png delete mode 100644 applications/external/hid_app/views.h delete mode 100644 applications/external/hid_app/views/hid_keyboard.c delete mode 100644 applications/external/hid_app/views/hid_keyboard.h delete mode 100644 applications/external/hid_app/views/hid_keynote.c delete mode 100644 applications/external/hid_app/views/hid_keynote.h delete mode 100644 applications/external/hid_app/views/hid_media.c delete mode 100644 applications/external/hid_app/views/hid_media.h delete mode 100644 applications/external/hid_app/views/hid_mouse.c delete mode 100644 applications/external/hid_app/views/hid_mouse.h delete mode 100644 applications/external/hid_app/views/hid_mouse_clicker.c delete mode 100644 applications/external/hid_app/views/hid_mouse_clicker.h delete mode 100644 applications/external/hid_app/views/hid_mouse_jiggler.c delete mode 100644 applications/external/hid_app/views/hid_mouse_jiggler.h delete mode 100644 applications/external/hid_app/views/hid_numpad.c delete mode 100644 applications/external/hid_app/views/hid_numpad.h delete mode 100644 applications/external/hid_app/views/hid_tikshorts.c delete mode 100644 applications/external/hid_app/views/hid_tikshorts.h delete mode 100644 applications/external/ir_scope/application.fam delete mode 100644 applications/external/ir_scope/ir_scope.c delete mode 100644 applications/external/ir_scope/ir_scope.png delete mode 100644 applications/external/jetpack_joyride/application.fam delete mode 100644 applications/external/jetpack_joyride/assets/air_vent.png delete mode 100644 applications/external/jetpack_joyride/assets/alert/frame_01.png delete mode 100644 applications/external/jetpack_joyride/assets/alert/frame_02.png delete mode 100644 applications/external/jetpack_joyride/assets/alert/frame_rate delete mode 100644 applications/external/jetpack_joyride/assets/barry/frame_01.png delete mode 100644 applications/external/jetpack_joyride/assets/barry/frame_02.png delete mode 100644 applications/external/jetpack_joyride/assets/barry/frame_03.png delete mode 100644 applications/external/jetpack_joyride/assets/barry/frame_rate delete mode 100644 applications/external/jetpack_joyride/assets/barry_infill.png delete mode 100644 applications/external/jetpack_joyride/assets/bg1.png delete mode 100644 applications/external/jetpack_joyride/assets/bg2.png delete mode 100644 applications/external/jetpack_joyride/assets/bg3.png delete mode 100644 applications/external/jetpack_joyride/assets/coin.png delete mode 100644 applications/external/jetpack_joyride/assets/coin_infill.png delete mode 100644 applications/external/jetpack_joyride/assets/dead_scientist.png delete mode 100644 applications/external/jetpack_joyride/assets/dead_scientist_infill.png delete mode 100644 applications/external/jetpack_joyride/assets/door.png delete mode 100644 applications/external/jetpack_joyride/assets/missile/frame_01.png delete mode 100644 applications/external/jetpack_joyride/assets/missile/frame_rate delete mode 100644 applications/external/jetpack_joyride/assets/missile_infill.png delete mode 100644 applications/external/jetpack_joyride/assets/pillar.png delete mode 100644 applications/external/jetpack_joyride/assets/scientist_left.png delete mode 100644 applications/external/jetpack_joyride/assets/scientist_left_infill.png delete mode 100644 applications/external/jetpack_joyride/assets/scientist_right.png delete mode 100644 applications/external/jetpack_joyride/assets/scientist_right_infill.png delete mode 100644 applications/external/jetpack_joyride/icon.png delete mode 100644 applications/external/jetpack_joyride/includes/background_asset.c delete mode 100644 applications/external/jetpack_joyride/includes/background_assets.h delete mode 100644 applications/external/jetpack_joyride/includes/barry.c delete mode 100644 applications/external/jetpack_joyride/includes/barry.h delete mode 100644 applications/external/jetpack_joyride/includes/coin.c delete mode 100644 applications/external/jetpack_joyride/includes/coin.h delete mode 100644 applications/external/jetpack_joyride/includes/game_sprites.h delete mode 100644 applications/external/jetpack_joyride/includes/game_state.c delete mode 100644 applications/external/jetpack_joyride/includes/game_state.h delete mode 100644 applications/external/jetpack_joyride/includes/missile.c delete mode 100644 applications/external/jetpack_joyride/includes/missile.h delete mode 100644 applications/external/jetpack_joyride/includes/particle.c delete mode 100644 applications/external/jetpack_joyride/includes/particle.h delete mode 100644 applications/external/jetpack_joyride/includes/point.h delete mode 100644 applications/external/jetpack_joyride/includes/scientist.c delete mode 100644 applications/external/jetpack_joyride/includes/scientist.h delete mode 100644 applications/external/jetpack_joyride/includes/states.h delete mode 100644 applications/external/jetpack_joyride/jetpack.c delete mode 100644 applications/external/lightmeter/LICENSE delete mode 100644 applications/external/lightmeter/README.md delete mode 100644 applications/external/lightmeter/application.fam delete mode 100644 applications/external/lightmeter/docs/README.md delete mode 100644 applications/external/lightmeter/docs/changelog.md delete mode 100644 applications/external/lightmeter/gui/scenes/config/lightmeter_scene.c delete mode 100644 applications/external/lightmeter/gui/scenes/config/lightmeter_scene.h delete mode 100644 applications/external/lightmeter/gui/scenes/config/lightmeter_scene_config.h delete mode 100644 applications/external/lightmeter/gui/scenes/lightmeter_scene_about.c delete mode 100644 applications/external/lightmeter/gui/scenes/lightmeter_scene_config.c delete mode 100644 applications/external/lightmeter/gui/scenes/lightmeter_scene_help.c delete mode 100644 applications/external/lightmeter/gui/scenes/lightmeter_scene_main.c delete mode 100644 applications/external/lightmeter/gui/views/main_view.c delete mode 100644 applications/external/lightmeter/gui/views/main_view.h delete mode 100644 applications/external/lightmeter/icons/T_10x14.png delete mode 100644 applications/external/lightmeter/icons/f_10x14.png delete mode 100644 applications/external/lightmeter/lib/BH1750/BH1750.c delete mode 100644 applications/external/lightmeter/lib/BH1750/BH1750.h delete mode 100644 applications/external/lightmeter/lib/BH1750/LICENSE delete mode 100644 applications/external/lightmeter/lib/BH1750/README.md delete mode 100644 applications/external/lightmeter/lib/BH1750/docs/BH1750.pdf delete mode 100644 applications/external/lightmeter/lib/MAX44009/MAX44009.c delete mode 100644 applications/external/lightmeter/lib/MAX44009/MAX44009.h delete mode 100644 applications/external/lightmeter/lightmeter.c delete mode 100644 applications/external/lightmeter/lightmeter.h delete mode 100644 applications/external/lightmeter/lightmeter.png delete mode 100644 applications/external/lightmeter/lightmeter_config.h delete mode 100644 applications/external/lightmeter/lightmeter_helper.c delete mode 100644 applications/external/lightmeter/lightmeter_helper.h delete mode 100644 applications/external/metronome/README.md delete mode 100644 applications/external/metronome/application.fam delete mode 100644 applications/external/metronome/gui_extensions.c delete mode 100644 applications/external/metronome/gui_extensions.h delete mode 100644 applications/external/metronome/images/ButtonUp_7x4.png delete mode 100644 applications/external/metronome/img/screenshot.png delete mode 100644 applications/external/metronome/img/wave_left_4x14.png delete mode 100644 applications/external/metronome/img/wave_right_4x14.png delete mode 100644 applications/external/metronome/metronome.c delete mode 100644 applications/external/metronome/metronome_icon.png delete mode 100644 applications/external/mfkey32/application.fam delete mode 100644 applications/external/mfkey32/images/mfkey.png delete mode 100644 applications/external/mfkey32/mfkey.png delete mode 100644 applications/external/mfkey32/mfkey32.c delete mode 100644 applications/external/mifare_nested/LICENSE.md delete mode 100644 applications/external/mifare_nested/README.md delete mode 100644 applications/external/mifare_nested/TODO.md delete mode 100644 applications/external/mifare_nested/application.fam delete mode 100644 applications/external/mifare_nested/assets/ApplyTag.png delete mode 100644 applications/external/mifare_nested/assets/DolphinCry.png delete mode 100644 applications/external/mifare_nested/assets/DolphinSuccess.png delete mode 100644 applications/external/mifare_nested/assets/Loading.png delete mode 100644 applications/external/mifare_nested/assets/icon.png delete mode 100644 applications/external/mifare_nested/lib/crypto1/crypto1.c delete mode 100644 applications/external/mifare_nested/lib/crypto1/crypto1.h delete mode 100644 applications/external/mifare_nested/lib/nested/nested.c delete mode 100644 applications/external/mifare_nested/lib/nested/nested.h delete mode 100644 applications/external/mifare_nested/lib/parity/parity.c delete mode 100644 applications/external/mifare_nested/lib/parity/parity.h delete mode 100644 applications/external/mifare_nested/mifare_nested.c delete mode 100644 applications/external/mifare_nested/mifare_nested.h delete mode 100644 applications/external/mifare_nested/mifare_nested_i.h delete mode 100644 applications/external/mifare_nested/mifare_nested_worker.c delete mode 100644 applications/external/mifare_nested/mifare_nested_worker.h delete mode 100644 applications/external/mifare_nested/mifare_nested_worker_i.h delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene.h delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_about.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_added_keys.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_check.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_check_keys.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_collecting.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_config.h delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_failed.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_need_collection.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_need_key_recovery.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_no_keys.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_no_nonces_collected.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_nonces_collected.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_settings.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_start.c delete mode 100644 applications/external/mifare_nested/scenes/mifare_nested_scene_static_encrypted_nonce.c delete mode 100644 applications/external/minesweeper/LICENSE delete mode 100644 applications/external/minesweeper/README.md delete mode 100644 applications/external/minesweeper/application.fam delete mode 100644 applications/external/minesweeper/assets.h delete mode 100644 applications/external/minesweeper/assets/asset delete mode 100644 applications/external/minesweeper/assets/mockup.png delete mode 100644 applications/external/minesweeper/assets/tile_0.png delete mode 100644 applications/external/minesweeper/assets/tile_0.xbm delete mode 100644 applications/external/minesweeper/assets/tile_1.png delete mode 100644 applications/external/minesweeper/assets/tile_1.xbm delete mode 100644 applications/external/minesweeper/assets/tile_2.png delete mode 100644 applications/external/minesweeper/assets/tile_2.xbm delete mode 100644 applications/external/minesweeper/assets/tile_3.png delete mode 100644 applications/external/minesweeper/assets/tile_3.xbm delete mode 100644 applications/external/minesweeper/assets/tile_4.png delete mode 100644 applications/external/minesweeper/assets/tile_4.xbm delete mode 100644 applications/external/minesweeper/assets/tile_5.png delete mode 100644 applications/external/minesweeper/assets/tile_5.xbm delete mode 100644 applications/external/minesweeper/assets/tile_6.png delete mode 100644 applications/external/minesweeper/assets/tile_6.xbm delete mode 100644 applications/external/minesweeper/assets/tile_7.png delete mode 100644 applications/external/minesweeper/assets/tile_7.xbm delete mode 100644 applications/external/minesweeper/assets/tile_8.png delete mode 100644 applications/external/minesweeper/assets/tile_8.xbm delete mode 100644 applications/external/minesweeper/assets/tile_empty.png delete mode 100644 applications/external/minesweeper/assets/tile_flag.png delete mode 100644 applications/external/minesweeper/assets/tile_flag.xbm delete mode 100644 applications/external/minesweeper/assets/tile_mine.png delete mode 100644 applications/external/minesweeper/assets/tile_mine.xbm delete mode 100644 applications/external/minesweeper/assets/tile_uncleared.png delete mode 100644 applications/external/minesweeper/assets/tile_uncleared.xbm delete mode 100644 applications/external/minesweeper/img/screenshot.png delete mode 100644 applications/external/minesweeper/minesweeper.c delete mode 100644 applications/external/minesweeper/minesweeper_icon.png delete mode 100644 applications/external/morse_code/application.fam delete mode 100644 applications/external/morse_code/morse_code.c delete mode 100644 applications/external/morse_code/morse_code_10px.png delete mode 100644 applications/external/morse_code/morse_code_worker.c delete mode 100644 applications/external/morse_code/morse_code_worker.h delete mode 100644 applications/external/mousejacker/application.fam delete mode 100644 applications/external/mousejacker/images/badusb_10px.png delete mode 100644 applications/external/mousejacker/images/sub1_10px.png delete mode 100644 applications/external/mousejacker/lib/nrf24/nrf24.c delete mode 100644 applications/external/mousejacker/lib/nrf24/nrf24.h delete mode 100644 applications/external/mousejacker/mouse_10px.png delete mode 100644 applications/external/mousejacker/mousejacker.c delete mode 100644 applications/external/mousejacker/mousejacker_ducky.c delete mode 100644 applications/external/mousejacker/mousejacker_ducky.h delete mode 100644 applications/external/multi_converter/application.fam delete mode 100644 applications/external/multi_converter/converter_10px.png delete mode 100644 applications/external/multi_converter/multi_converter.c delete mode 100644 applications/external/multi_converter/multi_converter_definitions.h delete mode 100644 applications/external/multi_converter/multi_converter_mode_display.c delete mode 100644 applications/external/multi_converter/multi_converter_mode_display.h delete mode 100644 applications/external/multi_converter/multi_converter_mode_select.c delete mode 100644 applications/external/multi_converter/multi_converter_mode_select.h delete mode 100644 applications/external/multi_converter/multi_converter_units.c delete mode 100644 applications/external/multi_converter/multi_converter_units.h delete mode 160000 applications/external/multi_fuzzer delete mode 100644 applications/external/music_player/application.fam delete mode 100644 applications/external/music_player/icons/music_10px.png delete mode 100644 applications/external/music_player/music_player.c delete mode 100644 applications/external/nfc_magic/application.fam delete mode 100644 applications/external/nfc_magic/assets/DolphinCommon_56x48.png delete mode 100644 applications/external/nfc_magic/assets/DolphinNice_96x59.png delete mode 100644 applications/external/nfc_magic/assets/Loading_24.png delete mode 100644 applications/external/nfc_magic/assets/NFC_manual_60x50.png delete mode 100644 applications/external/nfc_magic/lib/magic/classic_gen1.c delete mode 100644 applications/external/nfc_magic/lib/magic/classic_gen1.h delete mode 100644 applications/external/nfc_magic/lib/magic/common.c delete mode 100644 applications/external/nfc_magic/lib/magic/common.h delete mode 100644 applications/external/nfc_magic/lib/magic/gen4.c delete mode 100644 applications/external/nfc_magic/lib/magic/gen4.h delete mode 100644 applications/external/nfc_magic/lib/magic/types.c delete mode 100644 applications/external/nfc_magic/lib/magic/types.h delete mode 100644 applications/external/nfc_magic/nfc_magic.c delete mode 100644 applications/external/nfc_magic/nfc_magic.h delete mode 100644 applications/external/nfc_magic/nfc_magic_i.h delete mode 100644 applications/external/nfc_magic/nfc_magic_worker.c delete mode 100644 applications/external/nfc_magic/nfc_magic_worker.h delete mode 100644 applications/external/nfc_magic/nfc_magic_worker_i.h delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene.h delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_actions.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_check.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_config.h delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_file_select.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_gen4_actions.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_key_input.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_magic_info.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_new_key_input.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_not_magic.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_rekey.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_rekey_fail.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_start.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_success.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_wipe.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_wipe_fail.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_write.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_write_confirm.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_write_fail.c delete mode 100644 applications/external/nfc_magic/scenes/nfc_magic_scene_wrong_card.c delete mode 100644 applications/external/nfc_maker/application.fam delete mode 100644 applications/external/nfc_maker/assets/DolphinNice_96x59.png delete mode 100644 applications/external/nfc_maker/assets/KeyBackspaceSelected_16x9.png delete mode 100644 applications/external/nfc_maker/assets/KeyBackspace_16x9.png delete mode 100644 applications/external/nfc_maker/assets/KeyKeyboardSelected_10x11.png delete mode 100644 applications/external/nfc_maker/assets/KeyKeyboard_10x11.png delete mode 100644 applications/external/nfc_maker/assets/KeySaveSelected_24x11.png delete mode 100644 applications/external/nfc_maker/assets/KeySave_24x11.png delete mode 100644 applications/external/nfc_maker/assets/WarningDolphin_45x42.png delete mode 100644 applications/external/nfc_maker/nfc_maker.c delete mode 100644 applications/external/nfc_maker/nfc_maker.h delete mode 100644 applications/external/nfc_maker/nfc_maker_10px.png delete mode 100644 applications/external/nfc_maker/nfc_maker_text_input.c delete mode 100644 applications/external/nfc_maker/nfc_maker_text_input.h delete mode 100644 applications/external/nfc_maker/nfc_maker_validators.c delete mode 100644 applications/external/nfc_maker/nfc_maker_validators.h delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene.h delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_bluetooth.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_config.h delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_contact.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_contact_last.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_contact_mail.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_contact_phone.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_contact_url.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_https.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_mail.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_phone.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_result.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_save.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_start.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_text.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_url.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_auth.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_encr.c delete mode 100644 applications/external/nfc_maker/scenes/nfc_maker_scene_wifi_pass.c delete mode 100644 applications/external/nfc_maker/strnlen.c delete mode 100644 applications/external/nfc_maker/strnlen.h delete mode 100644 applications/external/nfc_rfid_detector/application.fam delete mode 100644 applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_event.h delete mode 100644 applications/external/nfc_rfid_detector/helpers/nfc_rfid_detector_types.h delete mode 100644 applications/external/nfc_rfid_detector/images/Modern_reader_18x34.png delete mode 100644 applications/external/nfc_rfid_detector/images/Move_flipper_26x39.png delete mode 100644 applications/external/nfc_rfid_detector/images/NFC_detect_45x30.png delete mode 100644 applications/external/nfc_rfid_detector/images/Rfid_detect_45x30.png delete mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_10px.png delete mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app.c delete mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.c delete mode 100644 applications/external/nfc_rfid_detector/nfc_rfid_detector_app_i.h delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.c delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene.h delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_about.c delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_config.h delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_field_presence.c delete mode 100644 applications/external/nfc_rfid_detector/scenes/nfc_rfid_detector_scene_start.c delete mode 100644 applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.c delete mode 100644 applications/external/nfc_rfid_detector/views/nfc_rfid_detector_view_field_presence.h delete mode 100644 applications/external/nrfsniff/application.fam delete mode 100644 applications/external/nrfsniff/lib/nrf24/nrf24.c delete mode 100644 applications/external/nrfsniff/lib/nrf24/nrf24.h delete mode 100644 applications/external/nrfsniff/nrfsniff.c delete mode 100644 applications/external/nrfsniff/nrfsniff_10px.png delete mode 100644 applications/external/picopass/125_10px.png delete mode 100644 applications/external/picopass/application.fam delete mode 100644 applications/external/picopass/helpers/iclass_elite_dict.c delete mode 100644 applications/external/picopass/helpers/iclass_elite_dict.h delete mode 100644 applications/external/picopass/icons/DolphinMafia_115x62.png delete mode 100644 applications/external/picopass/icons/DolphinNice_96x59.png delete mode 100644 applications/external/picopass/icons/Nfc_10px.png delete mode 100644 applications/external/picopass/icons/RFIDDolphinReceive_97x61.png delete mode 100644 applications/external/picopass/icons/RFIDDolphinSend_97x61.png delete mode 100644 applications/external/picopass/lib/loclass/optimized_cipher.c delete mode 100644 applications/external/picopass/lib/loclass/optimized_cipher.h delete mode 100644 applications/external/picopass/lib/loclass/optimized_cipherutils.c delete mode 100644 applications/external/picopass/lib/loclass/optimized_cipherutils.h delete mode 100644 applications/external/picopass/lib/loclass/optimized_elite.c delete mode 100644 applications/external/picopass/lib/loclass/optimized_elite.h delete mode 100644 applications/external/picopass/lib/loclass/optimized_ikeys.c delete mode 100644 applications/external/picopass/lib/loclass/optimized_ikeys.h delete mode 100644 applications/external/picopass/picopass.c delete mode 100644 applications/external/picopass/picopass.h delete mode 100644 applications/external/picopass/picopass_device.c delete mode 100644 applications/external/picopass/picopass_device.h delete mode 100644 applications/external/picopass/picopass_i.h delete mode 100644 applications/external/picopass/picopass_keys.c delete mode 100644 applications/external/picopass/picopass_keys.h delete mode 100644 applications/external/picopass/picopass_worker.c delete mode 100644 applications/external/picopass/picopass_worker.h delete mode 100644 applications/external/picopass/picopass_worker_i.h delete mode 100644 applications/external/picopass/rfal_picopass.c delete mode 100644 applications/external/picopass/rfal_picopass.h delete mode 100644 applications/external/picopass/scenes/picopass_scene.c delete mode 100644 applications/external/picopass/scenes/picopass_scene.h delete mode 100644 applications/external/picopass/scenes/picopass_scene_card_menu.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_config.h delete mode 100644 applications/external/picopass/scenes/picopass_scene_delete.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_delete_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_device_info.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_file_select.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_key_menu.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_read_card.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_read_card_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_read_factory_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_save_name.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_save_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_saved_menu.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_start.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_write_card.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_write_card_success.c delete mode 100644 applications/external/picopass/scenes/picopass_scene_write_key.c delete mode 100644 applications/external/picopass/views/dict_attack.c delete mode 100644 applications/external/picopass/views/dict_attack.h delete mode 100644 applications/external/playlist/application.fam delete mode 100644 applications/external/playlist/canvas_helper.c delete mode 100644 applications/external/playlist/canvas_helper.h delete mode 100644 applications/external/playlist/helpers/radio_device_loader.c delete mode 100644 applications/external/playlist/helpers/radio_device_loader.h delete mode 100644 applications/external/playlist/images/ButtonRight_4x7.png delete mode 100644 applications/external/playlist/images/sub1_10px.png delete mode 100644 applications/external/playlist/playlist.c delete mode 100644 applications/external/playlist/playlist_10px.png delete mode 100644 applications/external/playlist/playlist_file.c delete mode 100644 applications/external/playlist/playlist_file.h delete mode 100644 applications/external/pocsag_pager/application.fam delete mode 100644 applications/external/pocsag_pager/helpers/pocsag_pager_event.h delete mode 100644 applications/external/pocsag_pager/helpers/pocsag_pager_types.h delete mode 100644 applications/external/pocsag_pager/helpers/radio_device_loader.c delete mode 100644 applications/external/pocsag_pager/helpers/radio_device_loader.h delete mode 100644 applications/external/pocsag_pager/images/Fishing_123x52.png delete mode 100644 applications/external/pocsag_pager/images/Lock_7x8.png delete mode 100644 applications/external/pocsag_pager/images/Message_8x7.png delete mode 100644 applications/external/pocsag_pager/images/Pin_back_arrow_10x8.png delete mode 100644 applications/external/pocsag_pager/images/Quest_7x8.png delete mode 100644 applications/external/pocsag_pager/images/Scanning_123x52.png delete mode 100644 applications/external/pocsag_pager/images/Unlock_7x8.png delete mode 100644 applications/external/pocsag_pager/images/WarningDolphin_45x42.png delete mode 100644 applications/external/pocsag_pager/pocsag_pager_10px.png delete mode 100644 applications/external/pocsag_pager/pocsag_pager_app.c delete mode 100644 applications/external/pocsag_pager/pocsag_pager_app_i.c delete mode 100644 applications/external/pocsag_pager/pocsag_pager_app_i.h delete mode 100644 applications/external/pocsag_pager/pocsag_pager_history.c delete mode 100644 applications/external/pocsag_pager/pocsag_pager_history.h delete mode 100644 applications/external/pocsag_pager/protocols/pcsg_generic.c delete mode 100644 applications/external/pocsag_pager/protocols/pcsg_generic.h delete mode 100644 applications/external/pocsag_pager/protocols/pocsag.c delete mode 100644 applications/external/pocsag_pager/protocols/pocsag.h delete mode 100644 applications/external/pocsag_pager/protocols/protocol_items.c delete mode 100644 applications/external/pocsag_pager/protocols/protocol_items.h delete mode 100644 applications/external/pocsag_pager/scenes/pocsag_pager_receiver.c delete mode 100644 applications/external/pocsag_pager/scenes/pocsag_pager_scene.c delete mode 100644 applications/external/pocsag_pager/scenes/pocsag_pager_scene.h delete mode 100644 applications/external/pocsag_pager/scenes/pocsag_pager_scene_about.c delete mode 100644 applications/external/pocsag_pager/scenes/pocsag_pager_scene_config.h delete mode 100644 applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_config.c delete mode 100644 applications/external/pocsag_pager/scenes/pocsag_pager_scene_receiver_info.c delete mode 100644 applications/external/pocsag_pager/scenes/pocsag_pager_scene_start.c delete mode 100644 applications/external/pocsag_pager/views/pocsag_pager_receiver.c delete mode 100644 applications/external/pocsag_pager/views/pocsag_pager_receiver.h delete mode 100644 applications/external/pocsag_pager/views/pocsag_pager_receiver_info.c delete mode 100644 applications/external/pocsag_pager/views/pocsag_pager_receiver_info.h delete mode 100644 applications/external/protoview/LICENSE delete mode 100644 applications/external/protoview/README.md delete mode 100644 applications/external/protoview/app.c delete mode 100644 applications/external/protoview/app.h delete mode 100644 applications/external/protoview/app_subghz.c delete mode 100644 applications/external/protoview/appicon.png delete mode 100644 applications/external/protoview/application.fam delete mode 100644 applications/external/protoview/crc.c delete mode 100644 applications/external/protoview/custom_presets.h delete mode 100644 applications/external/protoview/fields.c delete mode 100644 applications/external/protoview/helpers/radio_device_loader.c delete mode 100644 applications/external/protoview/helpers/radio_device_loader.h delete mode 100644 applications/external/protoview/images/ProtoViewSignal.jpg delete mode 100644 applications/external/protoview/images/protoview_1.jpg delete mode 100644 applications/external/protoview/images/protoview_2.jpg delete mode 100644 applications/external/protoview/protocols/b4b1.c delete mode 100644 applications/external/protoview/protocols/keeloq.c delete mode 100644 applications/external/protoview/protocols/oregon2.c delete mode 100644 applications/external/protoview/protocols/pvchat.c delete mode 100644 applications/external/protoview/protocols/tpms/citroen.c delete mode 100644 applications/external/protoview/protocols/tpms/ford.c delete mode 100644 applications/external/protoview/protocols/tpms/renault.c delete mode 100644 applications/external/protoview/protocols/tpms/schrader.c delete mode 100644 applications/external/protoview/protocols/tpms/schrader_eg53ma4.c delete mode 100644 applications/external/protoview/protocols/tpms/toyota.c delete mode 100644 applications/external/protoview/protocols/unknown.c delete mode 100644 applications/external/protoview/raw_samples.c delete mode 100644 applications/external/protoview/raw_samples.h delete mode 100644 applications/external/protoview/signal.c delete mode 100644 applications/external/protoview/signal_file.c delete mode 100644 applications/external/protoview/ui.c delete mode 100644 applications/external/protoview/view_build.c delete mode 100644 applications/external/protoview/view_direct_sampling.c delete mode 100644 applications/external/protoview/view_info.c delete mode 100644 applications/external/protoview/view_raw_signal.c delete mode 100644 applications/external/protoview/view_settings.c delete mode 100644 applications/external/sentry_safe/application.fam delete mode 100644 applications/external/sentry_safe/safe_10px.png delete mode 100644 applications/external/sentry_safe/sentry_safe.c delete mode 100644 applications/external/signal_generator/application.fam delete mode 100644 applications/external/signal_generator/icons/SmallArrowDown_3x5.png delete mode 100644 applications/external/signal_generator/icons/SmallArrowUp_3x5.png delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene.c delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene.h delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene_config.h delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene_mco.c delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene_pwm.c delete mode 100644 applications/external/signal_generator/scenes/signal_gen_scene_start.c delete mode 100644 applications/external/signal_generator/signal_gen_10px.png delete mode 100644 applications/external/signal_generator/signal_gen_app.c delete mode 100644 applications/external/signal_generator/signal_gen_app_i.h delete mode 100644 applications/external/signal_generator/views/signal_gen_pwm.c delete mode 100644 applications/external/signal_generator/views/signal_gen_pwm.h delete mode 100644 applications/external/snake_game/application.fam delete mode 100644 applications/external/snake_game/snake_10px.png delete mode 100644 applications/external/snake_game/snake_game.c delete mode 100644 applications/external/solitaire/application.fam delete mode 100644 applications/external/solitaire/assets/card_graphics.png delete mode 100644 applications/external/solitaire/assets/solitaire_main.png delete mode 100644 applications/external/solitaire/common/card.c delete mode 100644 applications/external/solitaire/common/card.h delete mode 100644 applications/external/solitaire/common/dml.c delete mode 100644 applications/external/solitaire/common/dml.h delete mode 100644 applications/external/solitaire/common/menu.c delete mode 100644 applications/external/solitaire/common/menu.h delete mode 100644 applications/external/solitaire/common/queue.c delete mode 100644 applications/external/solitaire/common/queue.h delete mode 100644 applications/external/solitaire/common/ui.c delete mode 100644 applications/external/solitaire/common/ui.h delete mode 100644 applications/external/solitaire/defines.h delete mode 100644 applications/external/solitaire/solitaire.c delete mode 100644 applications/external/solitaire/solitaire_10px.png delete mode 100644 applications/external/spectrum_analyzer/application.fam delete mode 100644 applications/external/spectrum_analyzer/helpers/radio_device_loader.c delete mode 100644 applications/external/spectrum_analyzer/helpers/radio_device_loader.h delete mode 100644 applications/external/spectrum_analyzer/spectrum_10px.png delete mode 100644 applications/external/spectrum_analyzer/spectrum_analyzer.c delete mode 100644 applications/external/spectrum_analyzer/spectrum_analyzer.h delete mode 100644 applications/external/spectrum_analyzer/spectrum_analyzer_worker.c delete mode 100644 applications/external/spectrum_analyzer/spectrum_analyzer_worker.h delete mode 100644 applications/external/spi_mem_manager/application.fam delete mode 100644 applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_01.png delete mode 100644 applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_02.png delete mode 100644 applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_03.png delete mode 100644 applications/external/spi_mem_manager/images/ChipLooking_64x64/frame_rate delete mode 100644 applications/external/spi_mem_manager/images/Dip8_10px.png delete mode 100644 applications/external/spi_mem_manager/images/Dip8_32x36.png delete mode 100644 applications/external/spi_mem_manager/images/DolphinMafia_115x62.png delete mode 100644 applications/external/spi_mem_manager/images/DolphinNice_96x59.png delete mode 100644 applications/external/spi_mem_manager/images/SDQuestion_35x43.png delete mode 100644 applications/external/spi_mem_manager/images/Wiring_SPI_128x64.png delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_chip.c delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_chip.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_chip_arr.c delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_chip_i.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_tools.c delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_tools.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_worker.c delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_worker.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_worker_i.h delete mode 100644 applications/external/spi_mem_manager/lib/spi/spi_mem_worker_modes.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene.h delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_about.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_chip_error.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_config.h delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_erase.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_file_info.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_read.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_read_filename.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_select_file.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_select_model.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_start.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_storage_error.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_success.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_verify.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_verify_error.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_wiring.c delete mode 100644 applications/external/spi_mem_manager/scenes/spi_mem_scene_write.c delete mode 100644 applications/external/spi_mem_manager/spi_mem_app.c delete mode 100644 applications/external/spi_mem_manager/spi_mem_app.h delete mode 100644 applications/external/spi_mem_manager/spi_mem_app_i.h delete mode 100644 applications/external/spi_mem_manager/spi_mem_files.c delete mode 100644 applications/external/spi_mem_manager/spi_mem_files.h delete mode 100644 applications/external/spi_mem_manager/tools/README.md delete mode 100644 applications/external/spi_mem_manager/tools/chiplist/LICENSE delete mode 100644 applications/external/spi_mem_manager/tools/chiplist/chiplist.xml delete mode 100755 applications/external/spi_mem_manager/tools/chiplist_convert.py delete mode 100644 applications/external/spi_mem_manager/views/spi_mem_view_detect.c delete mode 100644 applications/external/spi_mem_manager/views/spi_mem_view_detect.h delete mode 100644 applications/external/spi_mem_manager/views/spi_mem_view_progress.c delete mode 100644 applications/external/spi_mem_manager/views/spi_mem_view_progress.h delete mode 160000 applications/external/subbrute delete mode 100644 applications/external/swd_probe/.gitignore delete mode 100644 applications/external/swd_probe/LICENSE.txt delete mode 100644 applications/external/swd_probe/README.md delete mode 100644 applications/external/swd_probe/adi.c delete mode 100644 applications/external/swd_probe/adi.h delete mode 100644 applications/external/swd_probe/application.fam delete mode 100644 applications/external/swd_probe/icons/ButtonDown_7x4.png delete mode 100644 applications/external/swd_probe/icons/ButtonUp_7x4.png delete mode 100644 applications/external/swd_probe/icons/app.png delete mode 100644 applications/external/swd_probe/icons/swd.png delete mode 100644 applications/external/swd_probe/jep106.c delete mode 100644 applications/external/swd_probe/jep106.h delete mode 100644 applications/external/swd_probe/jep106.inc delete mode 100644 applications/external/swd_probe/model/chip.ply delete mode 100644 applications/external/swd_probe/model/convert.py delete mode 100644 applications/external/swd_probe/model/model_chip.h delete mode 100644 applications/external/swd_probe/swd_probe_app.c delete mode 100644 applications/external/swd_probe/swd_probe_app.h delete mode 100644 applications/external/swd_probe/usb_uart.c delete mode 100644 applications/external/swd_probe/usb_uart.h delete mode 100644 applications/external/tetris_game/application.fam delete mode 100644 applications/external/tetris_game/tetris_10px.png delete mode 100644 applications/external/tetris_game/tetris_game.c delete mode 100644 applications/external/text_viewer/LICENSE delete mode 100644 applications/external/text_viewer/README.md delete mode 100644 applications/external/text_viewer/application.fam delete mode 100644 applications/external/text_viewer/icons/text_10px.png delete mode 100644 applications/external/text_viewer/text_viewer.c delete mode 100644 applications/external/text_viewer/textviewerflipper.PNG delete mode 100644 applications/external/tictactoe_game/application.fam delete mode 100644 applications/external/tictactoe_game/tictactoe_10px.png delete mode 100644 applications/external/tictactoe_game/tictactoe_game.c delete mode 100644 applications/external/totp/LICENSE delete mode 100644 applications/external/totp/application.fam delete mode 100644 applications/external/totp/cli/cli.c delete mode 100644 applications/external/totp/cli/cli.h delete mode 100644 applications/external/totp/cli/cli_helpers.c delete mode 100644 applications/external/totp/cli/cli_helpers.h delete mode 100644 applications/external/totp/cli/commands/add/add.c delete mode 100644 applications/external/totp/cli/commands/add/add.h delete mode 100644 applications/external/totp/cli/commands/automation/automation.c delete mode 100644 applications/external/totp/cli/commands/automation/automation.h delete mode 100644 applications/external/totp/cli/commands/delete/delete.c delete mode 100644 applications/external/totp/cli/commands/delete/delete.h delete mode 100644 applications/external/totp/cli/commands/details/details.c delete mode 100644 applications/external/totp/cli/commands/details/details.h delete mode 100644 applications/external/totp/cli/commands/help/help.c delete mode 100644 applications/external/totp/cli/commands/help/help.h delete mode 100644 applications/external/totp/cli/commands/list/list.c delete mode 100644 applications/external/totp/cli/commands/list/list.h delete mode 100644 applications/external/totp/cli/commands/move/move.c delete mode 100644 applications/external/totp/cli/commands/move/move.h delete mode 100644 applications/external/totp/cli/commands/notification/notification.c delete mode 100644 applications/external/totp/cli/commands/notification/notification.h delete mode 100644 applications/external/totp/cli/commands/pin/pin.c delete mode 100644 applications/external/totp/cli/commands/pin/pin.h delete mode 100644 applications/external/totp/cli/commands/reset/reset.c delete mode 100644 applications/external/totp/cli/commands/reset/reset.h delete mode 100644 applications/external/totp/cli/commands/timezone/timezone.c delete mode 100644 applications/external/totp/cli/commands/timezone/timezone.h delete mode 100644 applications/external/totp/cli/commands/update/update.c delete mode 100644 applications/external/totp/cli/commands/update/update.h delete mode 100644 applications/external/totp/cli/common_command_arguments.c delete mode 100644 applications/external/totp/cli/common_command_arguments.h delete mode 100644 applications/external/totp/config/app/config.h delete mode 100644 applications/external/totp/config/wolfssl/config.h delete mode 100644 applications/external/totp/images/DolphinCommon_56x48.png delete mode 100644 applications/external/totp/images/hid_ble_31x9.png delete mode 100644 applications/external/totp/images/hid_usb_31x9.png delete mode 100644 applications/external/totp/images/totp_arrow_left_8x9.png delete mode 100644 applications/external/totp/images/totp_arrow_right_8x9.png delete mode 100644 applications/external/totp/lib/base32/base32.c delete mode 100644 applications/external/totp/lib/base32/base32.h delete mode 100644 applications/external/totp/lib/base64/base64.c delete mode 100644 applications/external/totp/lib/base64/base64.h delete mode 100644 applications/external/totp/lib/fonts/712serif/712serif.c delete mode 100644 applications/external/totp/lib/fonts/712serif/712serif.h delete mode 100644 applications/external/totp/lib/fonts/available_fonts.c delete mode 100644 applications/external/totp/lib/fonts/available_fonts.h delete mode 100644 applications/external/totp/lib/fonts/bedstead/bedstead.c delete mode 100644 applications/external/totp/lib/fonts/bedstead/bedstead.h delete mode 100644 applications/external/totp/lib/fonts/dpcomic/dpcomic.c delete mode 100644 applications/external/totp/lib/fonts/dpcomic/dpcomic.h delete mode 100644 applications/external/totp/lib/fonts/font_info.h delete mode 100644 applications/external/totp/lib/fonts/funclimbing/funclimbing.c delete mode 100644 applications/external/totp/lib/fonts/funclimbing/funclimbing.h delete mode 100644 applications/external/totp/lib/fonts/graph35pix/graph35pix.c delete mode 100644 applications/external/totp/lib/fonts/graph35pix/graph35pix.h delete mode 100644 applications/external/totp/lib/fonts/karma_future/karma_future.c delete mode 100644 applications/external/totp/lib/fonts/karma_future/karma_future.h delete mode 100644 applications/external/totp/lib/fonts/mode_nine/mode_nine.c delete mode 100644 applications/external/totp/lib/fonts/mode_nine/mode_nine.h delete mode 100644 applications/external/totp/lib/fonts/pixelflag/pixelflag.c delete mode 100644 applications/external/totp/lib/fonts/pixelflag/pixelflag.h delete mode 100644 applications/external/totp/lib/fonts/redhat_mono/redhat_mono.c delete mode 100644 applications/external/totp/lib/fonts/redhat_mono/redhat_mono.h delete mode 100644 applications/external/totp/lib/fonts/zector/zector.c delete mode 100644 applications/external/totp/lib/fonts/zector/zector.h delete mode 100644 applications/external/totp/lib/polyfills/memset_s.c delete mode 100644 applications/external/totp/lib/polyfills/memset_s.h delete mode 100644 applications/external/totp/lib/polyfills/strnlen.c delete mode 100644 applications/external/totp/lib/polyfills/strnlen.h delete mode 100644 applications/external/totp/lib/roll_value/roll_value.c delete mode 100644 applications/external/totp/lib/roll_value/roll_value.h delete mode 100644 applications/external/totp/lib/timezone_utils/timezone_utils.c delete mode 100644 applications/external/totp/lib/timezone_utils/timezone_utils.h delete mode 160000 applications/external/totp/lib/wolfssl delete mode 100644 applications/external/totp/services/config/config.c delete mode 100644 applications/external/totp/services/config/config.h delete mode 100644 applications/external/totp/services/config/config_file_context.h delete mode 100644 applications/external/totp/services/config/constants.h delete mode 100644 applications/external/totp/services/config/migrations/common_migration.c delete mode 100644 applications/external/totp/services/config/migrations/common_migration.h delete mode 100644 applications/external/totp/services/config/token_info_iterator.c delete mode 100644 applications/external/totp/services/config/token_info_iterator.h delete mode 100644 applications/external/totp/services/convert/convert.h delete mode 100644 applications/external/totp/services/crypto/common_types.h delete mode 100644 applications/external/totp/services/crypto/constants.h delete mode 100644 applications/external/totp/services/crypto/crypto_facade.c delete mode 100644 applications/external/totp/services/crypto/crypto_facade.h delete mode 100644 applications/external/totp/services/crypto/crypto_v1.c delete mode 100644 applications/external/totp/services/crypto/crypto_v1.h delete mode 100644 applications/external/totp/services/crypto/crypto_v2.c delete mode 100644 applications/external/totp/services/crypto/crypto_v2.h delete mode 100644 applications/external/totp/services/crypto/crypto_v3.c delete mode 100644 applications/external/totp/services/crypto/crypto_v3.h delete mode 100644 applications/external/totp/services/crypto/polyfills.h delete mode 100644 applications/external/totp/services/idle_timeout/idle_timeout.c delete mode 100644 applications/external/totp/services/idle_timeout/idle_timeout.h delete mode 100644 applications/external/totp/services/totp/totp.c delete mode 100644 applications/external/totp/services/totp/totp.h delete mode 100644 applications/external/totp/totp_10px.png delete mode 100644 applications/external/totp/totp_app.c delete mode 100644 applications/external/totp/types/automation_kb_layout.h delete mode 100644 applications/external/totp/types/automation_method.h delete mode 100644 applications/external/totp/types/common.c delete mode 100644 applications/external/totp/types/common.h delete mode 100644 applications/external/totp/types/crypto_settings.h delete mode 100644 applications/external/totp/types/event_type.h delete mode 100644 applications/external/totp/types/notification_method.h delete mode 100644 applications/external/totp/types/plugin_event.h delete mode 100644 applications/external/totp/types/plugin_state.h delete mode 100644 applications/external/totp/types/token_info.c delete mode 100644 applications/external/totp/types/token_info.h delete mode 100644 applications/external/totp/types/user_pin_codes.h delete mode 100644 applications/external/totp/ui/canvas_extensions.c delete mode 100644 applications/external/totp/ui/canvas_extensions.h delete mode 100644 applications/external/totp/ui/common_dialogs.c delete mode 100644 applications/external/totp/ui/common_dialogs.h delete mode 100644 applications/external/totp/ui/constants.h delete mode 100644 applications/external/totp/ui/scene_director.c delete mode 100644 applications/external/totp/ui/scene_director.h delete mode 100644 applications/external/totp/ui/scenes/add_new_token/totp_input_text.c delete mode 100644 applications/external/totp/ui/scenes/add_new_token/totp_input_text.h delete mode 100644 applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.c delete mode 100644 applications/external/totp/ui/scenes/add_new_token/totp_scene_add_new_token.h delete mode 100644 applications/external/totp/ui/scenes/app_settings/totp_app_settings.c delete mode 100644 applications/external/totp/ui/scenes/app_settings/totp_app_settings.h delete mode 100644 applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.c delete mode 100644 applications/external/totp/ui/scenes/authenticate/totp_scene_authenticate.h delete mode 100644 applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.c delete mode 100644 applications/external/totp/ui/scenes/generate_token/totp_scene_generate_token.h delete mode 100644 applications/external/totp/ui/scenes/standby/standby.c delete mode 100644 applications/external/totp/ui/scenes/standby/standby.h delete mode 100644 applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.c delete mode 100644 applications/external/totp/ui/scenes/token_menu/totp_scene_token_menu.h delete mode 100644 applications/external/totp/ui/totp_scenes_enum.h delete mode 100644 applications/external/totp/ui/ui_controls.c delete mode 100644 applications/external/totp/ui/ui_controls.h delete mode 100644 applications/external/totp/version.h delete mode 100644 applications/external/totp/workers/bt_type_code/bt_type_code.c delete mode 100644 applications/external/totp/workers/bt_type_code/bt_type_code.h delete mode 100644 applications/external/totp/workers/generate_totp_code/generate_totp_code.c delete mode 100644 applications/external/totp/workers/generate_totp_code/generate_totp_code.h delete mode 100644 applications/external/totp/workers/type_code_common.c delete mode 100644 applications/external/totp/workers/type_code_common.h delete mode 100644 applications/external/totp/workers/usb_type_code/usb_type_code.c delete mode 100644 applications/external/totp/workers/usb_type_code/usb_type_code.h delete mode 100644 applications/external/uart_terminal/LICENSE delete mode 100644 applications/external/uart_terminal/README.md delete mode 100644 applications/external/uart_terminal/application.fam delete mode 100644 applications/external/uart_terminal/assets/KeyBackspaceSelected_16x9.png delete mode 100644 applications/external/uart_terminal/assets/KeyBackspace_16x9.png delete mode 100644 applications/external/uart_terminal/assets/KeySaveSelected_24x11.png delete mode 100644 applications/external/uart_terminal/assets/KeySave_24x11.png delete mode 100644 applications/external/uart_terminal/assets/WarningDolphin_45x42.png delete mode 100644 applications/external/uart_terminal/scenes/uart_terminal_scene.c delete mode 100644 applications/external/uart_terminal/scenes/uart_terminal_scene.h delete mode 100644 applications/external/uart_terminal/scenes/uart_terminal_scene_config.h delete mode 100644 applications/external/uart_terminal/scenes/uart_terminal_scene_console_output.c delete mode 100644 applications/external/uart_terminal/scenes/uart_terminal_scene_start.c delete mode 100644 applications/external/uart_terminal/scenes/uart_terminal_scene_text_input.c delete mode 100644 applications/external/uart_terminal/uart_terminal.png delete mode 100644 applications/external/uart_terminal/uart_terminal_app.c delete mode 100644 applications/external/uart_terminal/uart_terminal_app.h delete mode 100644 applications/external/uart_terminal/uart_terminal_app_i.h delete mode 100644 applications/external/uart_terminal/uart_terminal_custom_event.h delete mode 100644 applications/external/uart_terminal/uart_terminal_uart.c delete mode 100644 applications/external/uart_terminal/uart_terminal_uart.h delete mode 100644 applications/external/uart_terminal/uart_text_input.c delete mode 100644 applications/external/uart_terminal/uart_text_input.h delete mode 100644 applications/external/uart_terminal/uart_validators.c delete mode 100644 applications/external/uart_terminal/uart_validators.h delete mode 100644 applications/external/unitemp/LICENSE.md delete mode 100644 applications/external/unitemp/README.md delete mode 100644 applications/external/unitemp/Sensors.c delete mode 100644 applications/external/unitemp/Sensors.h delete mode 100644 applications/external/unitemp/application.fam delete mode 100644 applications/external/unitemp/assets/README.MD delete mode 100644 applications/external/unitemp/assets/co2_11x14.png delete mode 100644 applications/external/unitemp/assets/flipper_happy_2_60x38.png delete mode 100644 applications/external/unitemp/assets/flipper_happy_60x38.png delete mode 100644 applications/external/unitemp/assets/flipper_sad_60x38.png delete mode 100644 applications/external/unitemp/assets/heat_index_11x14.png delete mode 100644 applications/external/unitemp/assets/hum_9x15.png delete mode 100644 applications/external/unitemp/assets/in_hg_15x15.png delete mode 100644 applications/external/unitemp/assets/mm_hg_15x15.png delete mode 100644 applications/external/unitemp/assets/pressure_7x13.png delete mode 100644 applications/external/unitemp/assets/repo_qr_50x50.png delete mode 100644 applications/external/unitemp/assets/sherlok_53x45.png delete mode 100644 applications/external/unitemp/assets/temp_C_11x14.png delete mode 100644 applications/external/unitemp/assets/temp_F_11x14.png delete mode 100644 applications/external/unitemp/icon.png delete mode 100644 applications/external/unitemp/interfaces/I2CSensor.c delete mode 100644 applications/external/unitemp/interfaces/I2CSensor.h delete mode 100644 applications/external/unitemp/interfaces/OneWireSensor.c delete mode 100644 applications/external/unitemp/interfaces/OneWireSensor.h delete mode 100644 applications/external/unitemp/interfaces/SPISensor.c delete mode 100644 applications/external/unitemp/interfaces/SPISensor.h delete mode 100644 applications/external/unitemp/interfaces/SingleWireSensor.c delete mode 100644 applications/external/unitemp/interfaces/SingleWireSensor.h delete mode 100644 applications/external/unitemp/interfaces/endianness.h delete mode 100644 applications/external/unitemp/sensors/AM2320.c delete mode 100644 applications/external/unitemp/sensors/AM2320.h delete mode 100644 applications/external/unitemp/sensors/BME680.c delete mode 100644 applications/external/unitemp/sensors/BME680.h delete mode 100644 applications/external/unitemp/sensors/BMP180.c delete mode 100644 applications/external/unitemp/sensors/BMP180.h delete mode 100644 applications/external/unitemp/sensors/BMx280.c delete mode 100644 applications/external/unitemp/sensors/BMx280.h delete mode 100644 applications/external/unitemp/sensors/DHT20.c delete mode 100644 applications/external/unitemp/sensors/DHT20.h delete mode 100644 applications/external/unitemp/sensors/HDC1080.c delete mode 100644 applications/external/unitemp/sensors/HDC1080.h delete mode 100644 applications/external/unitemp/sensors/HTU21x.c delete mode 100644 applications/external/unitemp/sensors/HTU21x.h delete mode 100644 applications/external/unitemp/sensors/LM75.c delete mode 100644 applications/external/unitemp/sensors/LM75.h delete mode 100644 applications/external/unitemp/sensors/MAX31855.c delete mode 100644 applications/external/unitemp/sensors/MAX31855.h delete mode 100644 applications/external/unitemp/sensors/MAX6675.c delete mode 100644 applications/external/unitemp/sensors/MAX6675.h delete mode 100644 applications/external/unitemp/sensors/SCD30.c delete mode 100644 applications/external/unitemp/sensors/SCD30.h delete mode 100644 applications/external/unitemp/sensors/SCD40.c delete mode 100644 applications/external/unitemp/sensors/SCD40.h delete mode 100644 applications/external/unitemp/sensors/SHT30.c delete mode 100644 applications/external/unitemp/sensors/SHT30.h delete mode 100644 applications/external/unitemp/sensors/Sensors.xlsx delete mode 100644 applications/external/unitemp/unitemp.c delete mode 100644 applications/external/unitemp/unitemp.h delete mode 100644 applications/external/unitemp/views/General_view.c delete mode 100644 applications/external/unitemp/views/MainMenu_view.c delete mode 100644 applications/external/unitemp/views/Popup_view.c delete mode 100644 applications/external/unitemp/views/SensorActions_view.c delete mode 100644 applications/external/unitemp/views/SensorEdit_view.c delete mode 100644 applications/external/unitemp/views/SensorNameEdit_view.c delete mode 100644 applications/external/unitemp/views/SensorsList_view.c delete mode 100644 applications/external/unitemp/views/Settings_view.c delete mode 100644 applications/external/unitemp/views/UnitempViews.h delete mode 100644 applications/external/unitemp/views/Widgets_view.c delete mode 100644 applications/external/wav_player/README.md delete mode 100644 applications/external/wav_player/application.fam delete mode 100644 applications/external/wav_player/images/music_10px.png delete mode 100644 applications/external/wav_player/wav_10px.png delete mode 100644 applications/external/wav_player/wav_parser.c delete mode 100644 applications/external/wav_player/wav_parser.h delete mode 100644 applications/external/wav_player/wav_player.c delete mode 100644 applications/external/wav_player/wav_player_hal.c delete mode 100644 applications/external/wav_player/wav_player_hal.h delete mode 100644 applications/external/wav_player/wav_player_view.c delete mode 100644 applications/external/wav_player/wav_player_view.h delete mode 100644 applications/external/weather_station/application.fam delete mode 100644 applications/external/weather_station/helpers/radio_device_loader.c delete mode 100644 applications/external/weather_station/helpers/radio_device_loader.h delete mode 100644 applications/external/weather_station/helpers/weather_station_event.h delete mode 100644 applications/external/weather_station/helpers/weather_station_types.h delete mode 100644 applications/external/weather_station/images/Fishing_123x52.png delete mode 100644 applications/external/weather_station/images/Humid_10x15.png delete mode 100644 applications/external/weather_station/images/Humid_8x13.png delete mode 100644 applications/external/weather_station/images/Lock_7x8.png delete mode 100644 applications/external/weather_station/images/Pin_back_arrow_10x8.png delete mode 100644 applications/external/weather_station/images/Quest_7x8.png delete mode 100644 applications/external/weather_station/images/Scanning_123x52.png delete mode 100644 applications/external/weather_station/images/Therm_7x16.png delete mode 100644 applications/external/weather_station/images/Timer_11x11.png delete mode 100644 applications/external/weather_station/images/Unlock_7x8.png delete mode 100644 applications/external/weather_station/images/WarningDolphin_45x42.png delete mode 100644 applications/external/weather_station/images/station_icon.png delete mode 100644 applications/external/weather_station/protocols/acurite_592txr.c delete mode 100644 applications/external/weather_station/protocols/acurite_592txr.h delete mode 100644 applications/external/weather_station/protocols/acurite_606tx.c delete mode 100644 applications/external/weather_station/protocols/acurite_606tx.h delete mode 100644 applications/external/weather_station/protocols/acurite_609txc.c delete mode 100644 applications/external/weather_station/protocols/acurite_609txc.h delete mode 100644 applications/external/weather_station/protocols/ambient_weather.c delete mode 100644 applications/external/weather_station/protocols/ambient_weather.h delete mode 100644 applications/external/weather_station/protocols/auriol_hg0601a.c delete mode 100644 applications/external/weather_station/protocols/auriol_hg0601a.h delete mode 100644 applications/external/weather_station/protocols/gt_wt_02.c delete mode 100644 applications/external/weather_station/protocols/gt_wt_02.h delete mode 100644 applications/external/weather_station/protocols/gt_wt_03.c delete mode 100644 applications/external/weather_station/protocols/gt_wt_03.h delete mode 100644 applications/external/weather_station/protocols/infactory.c delete mode 100644 applications/external/weather_station/protocols/infactory.h delete mode 100644 applications/external/weather_station/protocols/lacrosse_tx.c delete mode 100644 applications/external/weather_station/protocols/lacrosse_tx.h delete mode 100644 applications/external/weather_station/protocols/lacrosse_tx141thbv2.c delete mode 100644 applications/external/weather_station/protocols/lacrosse_tx141thbv2.h delete mode 100644 applications/external/weather_station/protocols/nexus_th.c delete mode 100644 applications/external/weather_station/protocols/nexus_th.h delete mode 100644 applications/external/weather_station/protocols/oregon2.c delete mode 100644 applications/external/weather_station/protocols/oregon2.h delete mode 100644 applications/external/weather_station/protocols/oregon3.c delete mode 100644 applications/external/weather_station/protocols/oregon3.h delete mode 100644 applications/external/weather_station/protocols/oregon_v1.c delete mode 100644 applications/external/weather_station/protocols/oregon_v1.h delete mode 100644 applications/external/weather_station/protocols/protocol_items.c delete mode 100644 applications/external/weather_station/protocols/protocol_items.h delete mode 100644 applications/external/weather_station/protocols/thermopro_tx4.c delete mode 100644 applications/external/weather_station/protocols/thermopro_tx4.h delete mode 100644 applications/external/weather_station/protocols/tx_8300.c delete mode 100644 applications/external/weather_station/protocols/tx_8300.h delete mode 100644 applications/external/weather_station/protocols/wendox_w6726.c delete mode 100644 applications/external/weather_station/protocols/wendox_w6726.h delete mode 100644 applications/external/weather_station/protocols/ws_generic.c delete mode 100644 applications/external/weather_station/protocols/ws_generic.h delete mode 100644 applications/external/weather_station/scenes/weather_station_receiver.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene.h delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_about.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_config.h delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_receiver_config.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_receiver_info.c delete mode 100644 applications/external/weather_station/scenes/weather_station_scene_start.c delete mode 100644 applications/external/weather_station/views/weather_station_receiver.c delete mode 100644 applications/external/weather_station/views/weather_station_receiver.h delete mode 100644 applications/external/weather_station/views/weather_station_receiver_info.c delete mode 100644 applications/external/weather_station/views/weather_station_receiver_info.h delete mode 100644 applications/external/weather_station/weather_station_10px.png delete mode 100644 applications/external/weather_station/weather_station_app.c delete mode 100644 applications/external/weather_station/weather_station_app_i.c delete mode 100644 applications/external/weather_station/weather_station_app_i.h delete mode 100644 applications/external/weather_station/weather_station_history.c delete mode 100644 applications/external/weather_station/weather_station_history.h delete mode 100644 applications/external/wifi_marauder_companion/LICENSE delete mode 100644 applications/external/wifi_marauder_companion/ReadMe.md delete mode 100644 applications/external/wifi_marauder_companion/application.fam delete mode 100644 applications/external/wifi_marauder_companion/assets/DolphinCommon_56x48.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeyKeyboardSelected_10x11.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeyKeyboard_10x11.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png delete mode 100644 applications/external/wifi_marauder_companion/assets/KeySave_24x11.png delete mode 100644 applications/external/wifi_marauder_companion/assets/Text_10x10.png delete mode 100644 applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png delete mode 100644 applications/external/wifi_marauder_companion/docs/README.md delete mode 100644 applications/external/wifi_marauder_companion/docs/changelog.md delete mode 100644 applications/external/wifi_marauder_companion/file/sequential_file.c delete mode 100644 applications/external/wifi_marauder_companion/file/sequential_file.h delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene.h delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_config.h delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_console_output.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_log_viewer.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_confirm_delete.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_edit_list.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_options.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_select.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_settings.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_add.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_script_stage_edit.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_settings_init.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_sniffpmkid_options.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_start.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c delete mode 100644 applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_user_input.c delete mode 100755 applications/external/wifi_marauder_companion/screenshots/marauder-save-pcaps.png delete mode 100755 applications/external/wifi_marauder_companion/screenshots/marauder-script-demo.png delete mode 100755 applications/external/wifi_marauder_companion/screenshots/marauder-topmenu.png delete mode 100644 applications/external/wifi_marauder_companion/script/cJSON.c delete mode 100644 applications/external/wifi_marauder_companion/script/cJSON.h delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu.h delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconap.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_beaconlist.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_config.h delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_deauth.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_delay.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_exec.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_probe.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_scan.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_select.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffbeacon.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffdeauth.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffesp.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpmkid.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffpwn.c delete mode 100644 applications/external/wifi_marauder_companion/script/menu/wifi_marauder_script_stage_menu_sniffraw.c delete mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script.c delete mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script.h delete mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.c delete mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_executor.h delete mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.c delete mode 100644 applications/external/wifi_marauder_companion/script/wifi_marauder_script_worker.h delete mode 100644 applications/external/wifi_marauder_companion/wifi_10px.png delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_app.c delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_app.h delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_app_i.h delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_custom_event.h delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_text_input.c delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_text_input.h delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_uart.c delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_uart.h delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_validators.c delete mode 100644 applications/external/wifi_marauder_companion/wifi_marauder_validators.h delete mode 100644 applications/external/wifi_scanner/FlipperZeroWiFiModuleDefines.h delete mode 100644 applications/external/wifi_scanner/application.fam delete mode 100644 applications/external/wifi_scanner/wifi_10px.png delete mode 100644 applications/external/wifi_scanner/wifi_scanner.c delete mode 100644 applications/external/zombiez/application.fam delete mode 100644 applications/external/zombiez/zombie_10px.png delete mode 100644 applications/external/zombiez/zombiez.c delete mode 100644 applications/external/zombiez/zombiez.h diff --git a/.gitmodules b/.gitmodules index 323ade223..52cf4a207 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,12 +26,6 @@ [submodule "lib/cxxheaderparser"] path = lib/cxxheaderparser url = https://github.com/robotpy/cxxheaderparser.git -[submodule "applications/external/dap_link/lib/free-dap"] - path = applications/external/dap_link/lib/free-dap - url = https://github.com/ataradov/free-dap.git -[submodule "applications/external/subbrute"] - path = applications/external/subbrute - url = https://github.com/DarkFlippers/flipperzero-subbrute.git [submodule "lib/heatshrink"] path = lib/heatshrink url = https://github.com/flipperdevices/heatshrink.git @@ -44,9 +38,3 @@ [submodule "lib/stm32wb_copro"] path = lib/stm32wb_copro url = https://github.com/flipperdevices/stm32wb_copro.git -[submodule "applications/external/multi_fuzzer"] - path = applications/external/multi_fuzzer - url = https://github.com/DarkFlippers/Multi_Fuzzer.git -[submodule "applications/external/totp/lib/wolfssl"] - path = applications/external/totp/lib/wolfssl - url = https://github.com/wolfSSL/wolfssl.git diff --git a/applications/external/application.fam b/applications/external/application.fam deleted file mode 100644 index 12dc1cc1a..000000000 --- a/applications/external/application.fam +++ /dev/null @@ -1,6 +0,0 @@ -# Placeholder -App( - appid="external_apps", - name="External apps bundle", - apptype=FlipperAppType.METAPACKAGE, -) diff --git a/applications/external/arkanoid/application.fam b/applications/external/arkanoid/application.fam deleted file mode 100644 index cdd5f4e8a..000000000 --- a/applications/external/arkanoid/application.fam +++ /dev/null @@ -1,14 +0,0 @@ -App( - appid="arkanoid", - name="Arkanoid", - apptype=FlipperAppType.EXTERNAL, - entry_point="arkanoid_game_app", - requires=["gui"], - stack_size=1 * 1024, - order=30, - fap_icon="arkanoid_10px.png", - fap_category="Games", - fap_author="@xMasterX & @gotnull", - fap_version="1.0", - fap_description="Arkanoid Game", -) diff --git a/applications/external/arkanoid/arkanoid_10px.png b/applications/external/arkanoid/arkanoid_10px.png deleted file mode 100644 index 344d2db737b25c786418f9432c86a207ee3db4e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1622 zcmcIlO>f*p7}=0deKf;k~nbzIG` zO|#v;tx`gXP8{7Jm{O0}p4C$81A(_Z8hQtvyR|QP^3k{~Gf&s2)2TLXX)+(_hU>aI zp*p1)5V$x=OE$x4abv~M5e3iVtc+!rSK6{ zjbh?r!|c#LK8RS!&IguJJUlrMGzh$IT9hp1qTBHih|=OX@+fWF z)MgB~Ovl1vXxcawG{i02X&a0?f`ojb3+y|RA64nA?3ZmDNe&!MhCIg{M#OeFZVz3K z4acN7q)vpH(c+ZYhG6zGN?`jikKxNQvX%6BPTKsWCS^HRox8i;CC-BpmZno_i -#include -#include -#include -#include -#include -#include -#include - -#define TAG "Arkanoid" - -#define FLIPPER_LCD_WIDTH 128 -#define FLIPPER_LCD_HEIGHT 64 -#define MAX_SPEED 3 - -typedef enum { EventTypeTick, EventTypeKey } EventType; - -typedef struct { - //Brick Bounds used in collision detection - int leftBrick; - int rightBrick; - int topBrick; - int bottomBrick; - bool isHit[4][13]; //Array of if bricks are hit or not -} BrickState; - -typedef struct { - int dx; //Initial movement of ball - int dy; //Initial movement of ball - int xb; //Balls starting possition - int yb; //Balls starting possition - bool released; //If the ball has been released by the player - //Ball Bounds used in collision detection - int leftBall; - int rightBall; - int topBall; - int bottomBall; -} BallState; - -typedef struct { - FuriMutex* mutex; - BallState ball_state; - BrickState brick_state; - NotificationApp* notify; - unsigned int COLUMNS; //Columns of bricks - unsigned int ROWS; //Rows of bricks - bool initialDraw; //If the inital draw has happened - int xPaddle; //X position of paddle - char text[16]; //General string buffer - bool bounced; //Used to fix double bounce glitch - int lives; //Amount of lives - int level; //Current level - unsigned int score; //Score for the game - unsigned int brickCount; //Amount of bricks hit - int tick; //Tick counter - bool gameStarted; // Did the game start? - int speed; // Ball speed -} ArkanoidState; - -typedef struct { - EventType type; - InputEvent input; -} GameEvent; - -static const NotificationSequence sequence_short_sound = { - &message_note_c5, - &message_delay_50, - &message_sound_off, - NULL, -}; - -// generate number in range [min,max) -int rand_range(int min, int max) { - return min + rand() % (max - min); -} - -void move_ball(Canvas* canvas, ArkanoidState* st) { - st->tick++; - - int current_speed = abs(st->speed - 1 - MAX_SPEED); - if(st->tick % current_speed != 0 && st->tick % (current_speed + 1) != 0) { - return; - } - - if(st->ball_state.released) { - //Move ball - if(abs(st->ball_state.dx) == 2) { - st->ball_state.xb += st->ball_state.dx / 2; - // 2x speed is really 1.5 speed - if((st->tick / current_speed) % 2 == 0) st->ball_state.xb += st->ball_state.dx / 2; - } else { - st->ball_state.xb += st->ball_state.dx; - } - st->ball_state.yb = st->ball_state.yb + st->ball_state.dy; - - //Set bounds - st->ball_state.leftBall = st->ball_state.xb; - st->ball_state.rightBall = st->ball_state.xb + 2; - st->ball_state.topBall = st->ball_state.yb; - st->ball_state.bottomBall = st->ball_state.yb + 2; - - //Bounce off top edge - if(st->ball_state.yb <= 0) { - st->ball_state.yb = 2; - st->ball_state.dy = -st->ball_state.dy; - } - - //Lose a life if bottom edge hit - if(st->ball_state.yb >= FLIPPER_LCD_HEIGHT) { - canvas_draw_frame(canvas, st->xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1); - st->xPaddle = 54; - st->ball_state.yb = 60; - st->ball_state.released = false; - st->lives--; - st->gameStarted = false; - - if(rand_range(0, 2) == 0) { - st->ball_state.dx = 1; - } else { - st->ball_state.dx = -1; - } - } - - //Bounce off left side - if(st->ball_state.xb <= 0) { - st->ball_state.xb = 2; - st->ball_state.dx = -st->ball_state.dx; - } - - //Bounce off right side - if(st->ball_state.xb >= FLIPPER_LCD_WIDTH - 2) { - st->ball_state.xb = FLIPPER_LCD_WIDTH - 4; - st->ball_state.dx = -st->ball_state.dx; - // arduboy.tunes.tone(523, 250); - } - - //Bounce off paddle - if(st->ball_state.xb + 1 >= st->xPaddle && st->ball_state.xb <= st->xPaddle + 12 && - st->ball_state.yb + 2 >= FLIPPER_LCD_HEIGHT - 1 && - st->ball_state.yb <= FLIPPER_LCD_HEIGHT) { - st->ball_state.dy = -st->ball_state.dy; - st->ball_state.dx = - ((st->ball_state.xb - (st->xPaddle + 6)) / 3); //Applies spin on the ball - // prevent straight bounce, but not prevent roguuemaster from stealing - if(st->ball_state.dx == 0) { - st->ball_state.dx = (rand_range(0, 2) == 1) ? 1 : -1; - } - } - - //Bounce off Bricks - for(unsigned int row = 0; row < st->ROWS; row++) { - for(unsigned int column = 0; column < st->COLUMNS; column++) { - if(!st->brick_state.isHit[row][column]) { - //Sets Brick bounds - st->brick_state.leftBrick = 10 * column; - st->brick_state.rightBrick = 10 * column + 10; - st->brick_state.topBrick = 6 * row + 1; - st->brick_state.bottomBrick = 6 * row + 7; - - //If A collison has occured - if(st->ball_state.topBall <= st->brick_state.bottomBrick && - st->ball_state.bottomBall >= st->brick_state.topBrick && - st->ball_state.leftBall <= st->brick_state.rightBrick && - st->ball_state.rightBall >= st->brick_state.leftBrick) { - st->score += st->level; - // Blink led when we hit some brick - notification_message(st->notify, &sequence_short_sound); - //notification_message(st->notify, &sequence_blink_white_100); - - st->brickCount++; - st->brick_state.isHit[row][column] = true; - canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4); - - //Vertical collision - if(st->ball_state.bottomBall > st->brick_state.bottomBrick || - st->ball_state.topBall < st->brick_state.topBrick) { - //Only bounce once each ball move - if(!st->bounced) { - st->ball_state.dy = -st->ball_state.dy; - st->ball_state.yb += st->ball_state.dy; - st->bounced = true; - } - } - - //Hoizontal collision - if(st->ball_state.leftBall < st->brick_state.leftBrick || - st->ball_state.rightBall > st->brick_state.rightBrick) { - //Only bounce once brick each ball move - if(!st->bounced) { - st->ball_state.dx = -st->ball_state.dx; - st->ball_state.xb += st->ball_state.dx; - st->bounced = true; - } - } - } - } - } - } - - //Reset Bounce - st->bounced = false; - } else { - //Ball follows paddle - st->ball_state.xb = st->xPaddle + 5; - } -} - -void draw_lives(Canvas* canvas, ArkanoidState* arkanoid_state) { - if(arkanoid_state->lives == 3) { - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7); - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 8); - - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 11); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 11); - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 12); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 12); - - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 15); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 15); - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 16); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 16); - } else if(arkanoid_state->lives == 2) { - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7); - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 8); - - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 11); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 11); - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 12); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 12); - } else { - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 7); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 7); - canvas_draw_dot(canvas, 4, FLIPPER_LCD_HEIGHT - 8); - canvas_draw_dot(canvas, 3, FLIPPER_LCD_HEIGHT - 8); - } -} - -void draw_score(Canvas* canvas, ArkanoidState* arkanoid_state) { - snprintf(arkanoid_state->text, sizeof(arkanoid_state->text), "%u", arkanoid_state->score); - canvas_draw_str_aligned( - canvas, - FLIPPER_LCD_WIDTH - 2, - FLIPPER_LCD_HEIGHT - 6, - AlignRight, - AlignBottom, - arkanoid_state->text); -} - -void draw_ball(Canvas* canvas, ArkanoidState* ast) { - canvas_draw_dot(canvas, ast->ball_state.xb, ast->ball_state.yb); - canvas_draw_dot(canvas, ast->ball_state.xb + 1, ast->ball_state.yb); - canvas_draw_dot(canvas, ast->ball_state.xb, ast->ball_state.yb + 1); - canvas_draw_dot(canvas, ast->ball_state.xb + 1, ast->ball_state.yb + 1); - - move_ball(canvas, ast); -} - -void draw_paddle(Canvas* canvas, ArkanoidState* arkanoid_state) { - canvas_draw_frame(canvas, arkanoid_state->xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1); -} - -void reset_level(Canvas* canvas, ArkanoidState* arkanoid_state) { - //Undraw paddle - canvas_draw_frame(canvas, arkanoid_state->xPaddle, FLIPPER_LCD_HEIGHT - 1, 11, 1); - - //Undraw ball - canvas_draw_dot(canvas, arkanoid_state->ball_state.xb, arkanoid_state->ball_state.yb); - canvas_draw_dot(canvas, arkanoid_state->ball_state.xb + 1, arkanoid_state->ball_state.yb); - canvas_draw_dot(canvas, arkanoid_state->ball_state.xb, arkanoid_state->ball_state.yb + 1); - canvas_draw_dot(canvas, arkanoid_state->ball_state.xb + 1, arkanoid_state->ball_state.yb + 1); - - //Alter various variables to reset the game - arkanoid_state->xPaddle = 54; - arkanoid_state->ball_state.yb = 60; - arkanoid_state->brickCount = 0; - arkanoid_state->ball_state.released = false; - arkanoid_state->gameStarted = false; - - // Reset all brick hit states - for(unsigned int row = 0; row < arkanoid_state->ROWS; row++) { - for(unsigned int column = 0; column < arkanoid_state->COLUMNS; column++) { - arkanoid_state->brick_state.isHit[row][column] = false; - } - } -} - -static void arkanoid_state_init(ArkanoidState* arkanoid_state) { - // Init notification - arkanoid_state->notify = furi_record_open(RECORD_NOTIFICATION); - - // Set the initial game state - arkanoid_state->COLUMNS = 13; - arkanoid_state->ROWS = 4; - arkanoid_state->ball_state.dx = -1; - arkanoid_state->ball_state.dy = -1; - arkanoid_state->speed = 2; - arkanoid_state->bounced = false; - arkanoid_state->lives = 3; - arkanoid_state->level = 1; - arkanoid_state->score = 0; - arkanoid_state->COLUMNS = 13; - arkanoid_state->COLUMNS = 13; - - // Reset initial state - arkanoid_state->initialDraw = false; - arkanoid_state->gameStarted = false; -} - -static void arkanoid_draw_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - ArkanoidState* arkanoid_state = ctx; - furi_mutex_acquire(arkanoid_state->mutex, FuriWaitForever); - - //Initial level draw - if(!arkanoid_state->initialDraw) { - arkanoid_state->initialDraw = true; - - // Set default font for text - canvas_set_font(canvas, FontSecondary); - - //Draws the new level - reset_level(canvas, arkanoid_state); - } - - //Draws new bricks and resets their values - for(unsigned int row = 0; row < arkanoid_state->ROWS; row++) { - for(unsigned int column = 0; column < arkanoid_state->COLUMNS; column++) { - if(!arkanoid_state->brick_state.isHit[row][column]) { - canvas_draw_frame(canvas, 10 * column, 2 + 6 * row, 8, 4); - } - } - } - - if(arkanoid_state->lives > 0) { - draw_paddle(canvas, arkanoid_state); - draw_ball(canvas, arkanoid_state); - draw_score(canvas, arkanoid_state); - draw_lives(canvas, arkanoid_state); - - if(arkanoid_state->brickCount == arkanoid_state->ROWS * arkanoid_state->COLUMNS) { - arkanoid_state->level++; - reset_level(canvas, arkanoid_state); - } - } else { - reset_level(canvas, arkanoid_state); - arkanoid_state->initialDraw = false; - arkanoid_state->lives = 3; - arkanoid_state->score = 0; - } - - furi_mutex_release(arkanoid_state->mutex); -} - -static void arkanoid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - GameEvent event = {.type = EventTypeKey, .input = *input_event}; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -static void arkanoid_update_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - GameEvent event = {.type = EventTypeTick}; - furi_message_queue_put(event_queue, &event, 0); -} - -int32_t arkanoid_game_app(void* p) { - UNUSED(p); - int32_t return_code = 0; - - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); - - ArkanoidState* arkanoid_state = malloc(sizeof(ArkanoidState)); - arkanoid_state_init(arkanoid_state); - - arkanoid_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!arkanoid_state->mutex) { - FURI_LOG_E(TAG, "Cannot create mutex\r\n"); - return_code = 255; - goto free_and_exit; - } - - // Set system callbacks - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, arkanoid_draw_callback, arkanoid_state); - view_port_input_callback_set(view_port, arkanoid_input_callback, event_queue); - - FuriTimer* timer = - furi_timer_alloc(arkanoid_update_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 22); - - // Open GUI and register view_port - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - // Call dolphin deed on game start - dolphin_deed(DolphinDeedPluginGameStart); - - GameEvent event; - for(bool processing = true; processing;) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - furi_mutex_acquire(arkanoid_state->mutex, FuriWaitForever); - - if(event_status == FuriStatusOk) { - // Key events - if(event.type == EventTypeKey) { - if(event.input.type == InputTypePress || event.input.type == InputTypeLong || - event.input.type == InputTypeRepeat) { - switch(event.input.key) { - case InputKeyBack: - processing = false; - break; - case InputKeyRight: - if(arkanoid_state->xPaddle < FLIPPER_LCD_WIDTH - 12) { - arkanoid_state->xPaddle += 8; - } - break; - case InputKeyLeft: - if(arkanoid_state->xPaddle > 0) { - arkanoid_state->xPaddle -= 8; - } - break; - case InputKeyUp: - if(arkanoid_state->speed < MAX_SPEED) { - arkanoid_state->speed++; - } - break; - case InputKeyDown: - if(arkanoid_state->speed > 1) { - arkanoid_state->speed--; - } - break; - case InputKeyOk: - if(arkanoid_state->gameStarted == false) { - //Release ball if FIRE pressed - arkanoid_state->ball_state.released = true; - - //Apply random direction to ball on release - if(rand_range(0, 2) == 0) { - arkanoid_state->ball_state.dx = 1; - } else { - arkanoid_state->ball_state.dx = -1; - } - - //Makes sure the ball heads upwards - arkanoid_state->ball_state.dy = -1; - //start the game flag - arkanoid_state->gameStarted = true; - } - break; - default: - break; - } - } - } - } - - view_port_update(view_port); - furi_mutex_release(arkanoid_state->mutex); - } - furi_timer_free(timer); - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - view_port_free(view_port); - furi_mutex_free(arkanoid_state->mutex); - -free_and_exit: - free(arkanoid_state); - furi_message_queue_free(event_queue); - - return return_code; -} diff --git a/applications/external/avr_isp_programmer/application.fam b/applications/external/avr_isp_programmer/application.fam deleted file mode 100644 index 19556d03d..000000000 --- a/applications/external/avr_isp_programmer/application.fam +++ /dev/null @@ -1,17 +0,0 @@ -App( - appid="avr_isp", - name="AVR Flasher", - apptype=FlipperAppType.EXTERNAL, - entry_point="avr_isp_app", - requires=["gui"], - stack_size=4 * 1024, - order=20, - fap_icon="avr_app_icon_10x10.png", - fap_category="GPIO", - fap_icon_assets="images", - fap_private_libs=[ - Lib( - name="driver", - ), - ], -) diff --git a/applications/external/avr_isp_programmer/avr_app_icon_10x10.png b/applications/external/avr_isp_programmer/avr_app_icon_10x10.png deleted file mode 100644 index 533787fe3569f70196d3f71e8373102dfd3967a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3614 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 Vpqb^9w@m;7?5&;gRaoD1{{|C}E&c!i diff --git a/applications/external/avr_isp_programmer/avr_isp_app.c b/applications/external/avr_isp_programmer/avr_isp_app.c deleted file mode 100644 index 740dc3610..000000000 --- a/applications/external/avr_isp_programmer/avr_isp_app.c +++ /dev/null @@ -1,179 +0,0 @@ -#include "avr_isp_app_i.h" - -static bool avr_isp_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - AvrIspApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool avr_isp_app_back_event_callback(void* context) { - furi_assert(context); - AvrIspApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void avr_isp_app_tick_event_callback(void* context) { - furi_assert(context); - AvrIspApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -AvrIspApp* avr_isp_app_alloc() { - AvrIspApp* app = malloc(sizeof(AvrIspApp)); - - app->file_path = furi_string_alloc(); - furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); - app->error = AvrIspErrorNoError; - - // GUI - app->gui = furi_record_open(RECORD_GUI); - - // View Dispatcher - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&avr_isp_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, avr_isp_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, avr_isp_app_back_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, avr_isp_app_tick_event_callback, 100); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - // Open Notification record - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - // SubMenu - app->submenu = submenu_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, AvrIspViewSubmenu, submenu_get_view(app->submenu)); - - // Widget - app->widget = widget_alloc(); - view_dispatcher_add_view(app->view_dispatcher, AvrIspViewWidget, widget_get_view(app->widget)); - - // Text Input - app->text_input = text_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, AvrIspViewTextInput, text_input_get_view(app->text_input)); - - // Popup - app->popup = popup_alloc(); - view_dispatcher_add_view(app->view_dispatcher, AvrIspViewPopup, popup_get_view(app->popup)); - - //Dialog - app->dialogs = furi_record_open(RECORD_DIALOGS); - - // Programmer view - app->avr_isp_programmer_view = avr_isp_programmer_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - AvrIspViewProgrammer, - avr_isp_programmer_view_get_view(app->avr_isp_programmer_view)); - - // Reader view - app->avr_isp_reader_view = avr_isp_reader_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - AvrIspViewReader, - avr_isp_reader_view_get_view(app->avr_isp_reader_view)); - - // Writer view - app->avr_isp_writer_view = avr_isp_writer_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - AvrIspViewWriter, - avr_isp_writer_view_get_view(app->avr_isp_writer_view)); - - // Chip detect view - app->avr_isp_chip_detect_view = avr_isp_chip_detect_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - AvrIspViewChipDetect, - avr_isp_chip_detect_view_get_view(app->avr_isp_chip_detect_view)); - - // Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering - uint8_t attempts = 0; - while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { - furi_hal_power_enable_otg(); - furi_delay_ms(10); - } - - scene_manager_next_scene(app->scene_manager, AvrIspSceneStart); - - return app; -} //-V773 - -void avr_isp_app_free(AvrIspApp* app) { - furi_assert(app); - - // Disable 5v power - if(furi_hal_power_is_otg_enabled()) { - furi_hal_power_disable_otg(); - } - - // Submenu - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewSubmenu); - submenu_free(app->submenu); - - // Widget - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWidget); - widget_free(app->widget); - - // TextInput - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewTextInput); - text_input_free(app->text_input); - - // Popup - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewPopup); - popup_free(app->popup); - - //Dialog - furi_record_close(RECORD_DIALOGS); - - // Programmer view - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewProgrammer); - avr_isp_programmer_view_free(app->avr_isp_programmer_view); - - // Reader view - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewReader); - avr_isp_reader_view_free(app->avr_isp_reader_view); - - // Writer view - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWriter); - avr_isp_writer_view_free(app->avr_isp_writer_view); - - // Chip detect view - view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewChipDetect); - avr_isp_chip_detect_view_free(app->avr_isp_chip_detect_view); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Notifications - furi_record_close(RECORD_NOTIFICATION); - app->notifications = NULL; - - // Close records - furi_record_close(RECORD_GUI); - - // Path strings - furi_string_free(app->file_path); - - free(app); -} - -int32_t avr_isp_app(void* p) { - UNUSED(p); - AvrIspApp* avr_isp_app = avr_isp_app_alloc(); - - view_dispatcher_run(avr_isp_app->view_dispatcher); - - avr_isp_app_free(avr_isp_app); - - return 0; -} diff --git a/applications/external/avr_isp_programmer/avr_isp_app_i.c b/applications/external/avr_isp_programmer/avr_isp_app_i.c deleted file mode 100644 index 7a7fa6d7f..000000000 --- a/applications/external/avr_isp_programmer/avr_isp_app_i.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "avr_isp_app_i.h" -#include -#include - -#define TAG "AvrIsp" - -bool avr_isp_load_from_file(AvrIspApp* app) { - furi_assert(app); - - FuriString* file_path = furi_string_alloc(); - FuriString* file_name = furi_string_alloc(); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, AVR_ISP_APP_EXTENSION, &I_avr_app_icon_10x10); - browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; - - // Input events and views are managed by file_select - bool res = dialog_file_browser_show(app->dialogs, file_path, app->file_path, &browser_options); - - if(res) { - path_extract_dirname(furi_string_get_cstr(file_path), app->file_path); - path_extract_filename(file_path, file_name, true); - strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME); - } - - furi_string_free(file_name); - furi_string_free(file_path); - - return res; -} diff --git a/applications/external/avr_isp_programmer/avr_isp_app_i.h b/applications/external/avr_isp_programmer/avr_isp_app_i.h deleted file mode 100644 index 17c69f8f2..000000000 --- a/applications/external/avr_isp_programmer/avr_isp_app_i.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "helpers/avr_isp_types.h" -#include - -#include "scenes/avr_isp_scene.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "views/avr_isp_view_programmer.h" -#include "views/avr_isp_view_reader.h" -#include "views/avr_isp_view_writer.h" -#include "views/avr_isp_view_chip_detect.h" - -#define AVR_ISP_MAX_LEN_NAME 64 - -typedef struct { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Popup* popup; - Submenu* submenu; - Widget* widget; - TextInput* text_input; - FuriString* file_path; - char file_name_tmp[AVR_ISP_MAX_LEN_NAME]; - AvrIspProgrammerView* avr_isp_programmer_view; - AvrIspReaderView* avr_isp_reader_view; - AvrIspWriterView* avr_isp_writer_view; - AvrIspChipDetectView* avr_isp_chip_detect_view; - AvrIspError error; -} AvrIspApp; - -bool avr_isp_load_from_file(AvrIspApp* app); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.c b/applications/external/avr_isp_programmer/helpers/avr_isp.c deleted file mode 100644 index 283c17bfd..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.c +++ /dev/null @@ -1,496 +0,0 @@ -#include "avr_isp.h" -#include "../lib/driver/avr_isp_prog_cmd.h" -#include "../lib/driver/avr_isp_spi_sw.h" - -#include - -#define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 -#define TAG "AvrIsp" - -struct AvrIsp { - AvrIspSpiSw* spi; - bool pmode; - AvrIspCallback callback; - void* context; -}; - -AvrIsp* avr_isp_alloc(void) { - AvrIsp* instance = malloc(sizeof(AvrIsp)); - return instance; -} - -void avr_isp_free(AvrIsp* instance) { - furi_assert(instance); - - if(instance->spi) avr_isp_end_pmode(instance); - free(instance); -} - -void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context) { - furi_assert(instance); - furi_assert(context); - - instance->callback = callback; - instance->context = context; -} - -uint8_t avr_isp_spi_transaction( - AvrIsp* instance, - uint8_t cmd, - uint8_t addr_hi, - uint8_t addr_lo, - uint8_t data) { - furi_assert(instance); - - avr_isp_spi_sw_txrx(instance->spi, cmd); - avr_isp_spi_sw_txrx(instance->spi, addr_hi); - avr_isp_spi_sw_txrx(instance->spi, addr_lo); - return avr_isp_spi_sw_txrx(instance->spi, data); -} - -static bool avr_isp_set_pmode(AvrIsp* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { - furi_assert(instance); - - uint8_t res = 0; - avr_isp_spi_sw_txrx(instance->spi, a); - avr_isp_spi_sw_txrx(instance->spi, b); - res = avr_isp_spi_sw_txrx(instance->spi, c); - avr_isp_spi_sw_txrx(instance->spi, d); - return res == 0x53; -} - -void avr_isp_end_pmode(AvrIsp* instance) { - furi_assert(instance); - - if(instance->pmode) { - avr_isp_spi_sw_res_set(instance->spi, true); - // We're about to take the target out of reset - // so configure SPI pins as input - if(instance->spi) avr_isp_spi_sw_free(instance->spi); - instance->spi = NULL; - } - - instance->pmode = false; -} - -static bool avr_isp_start_pmode(AvrIsp* instance, AvrIspSpiSwSpeed spi_speed) { - furi_assert(instance); - - // Reset target before driving PIN_SCK or PIN_MOSI - - // SPI.begin() will configure SS as output, - // so SPI master mode is selected. - // We have defined RESET as pin 10, - // which for many arduino's is not the SS pin. - // So we have to configure RESET as output here, - // (reset_target() first sets the correct level) - if(instance->spi) avr_isp_spi_sw_free(instance->spi); - instance->spi = avr_isp_spi_sw_init(spi_speed); - - avr_isp_spi_sw_res_set(instance->spi, false); - // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm": - - // Pulse RESET after PIN_SCK is low: - avr_isp_spi_sw_sck_set(instance->spi, false); - - // discharge PIN_SCK, value arbitrally chosen - furi_delay_ms(20); - avr_isp_spi_sw_res_set(instance->spi, true); - - // Pulse must be minimum 2 target CPU speed cycles - // so 100 usec is ok for CPU speeds above 20KHz - furi_delay_ms(1); - - avr_isp_spi_sw_res_set(instance->spi, false); - - // Send the enable programming command: - // datasheet: must be > 20 msec - furi_delay_ms(50); - if(avr_isp_set_pmode(instance, AVR_ISP_SET_PMODE)) { - instance->pmode = true; - return true; - } - return false; -} - -bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { - furi_assert(instance); - - AvrIspSpiSwSpeed spi_speed[] = { - AvrIspSpiSwSpeed1Mhz, - AvrIspSpiSwSpeed400Khz, - AvrIspSpiSwSpeed250Khz, - AvrIspSpiSwSpeed125Khz, - AvrIspSpiSwSpeed60Khz, - AvrIspSpiSwSpeed40Khz, - AvrIspSpiSwSpeed20Khz, - AvrIspSpiSwSpeed10Khz, - AvrIspSpiSwSpeed5Khz, - AvrIspSpiSwSpeed1Khz, - }; - for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { - if(avr_isp_start_pmode(instance, spi_speed[i])) { - AvrIspSignature sig = avr_isp_read_signature(instance); - AvrIspSignature sig_examination = avr_isp_read_signature(instance); //-V656 - uint8_t y = 0; - while(y < 8) { - if(memcmp((uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspSignature)) != - 0) - break; - sig_examination = avr_isp_read_signature(instance); - y++; - } - if(y == 8) { - if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { - if(i < (COUNT_OF(spi_speed) - 1)) { - avr_isp_end_pmode(instance); - i++; - return avr_isp_start_pmode(instance, spi_speed[i]); - } - } - return true; - } - } - } - - if(instance->spi) { - avr_isp_spi_sw_free(instance->spi); - instance->spi = NULL; - } - - return false; -} - -static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) { - furi_assert(instance); - - avr_isp_spi_transaction(instance, AVR_ISP_COMMIT(addr)); - /* polling flash */ - if(data == 0xFF) { - furi_delay_ms(5); - } else { - /* polling flash */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { - break; - }; - } - } -} - -static uint16_t avr_isp_current_page(AvrIsp* instance, uint32_t addr, uint16_t page_size) { - furi_assert(instance); - - uint16_t page = 0; - switch(page_size) { - case 32: - page = addr & 0xFFFFFFF0; - break; - case 64: - page = addr & 0xFFFFFFE0; - break; - case 128: - page = addr & 0xFFFFFFC0; - break; - case 256: - page = addr & 0xFFFFFF80; - break; - - default: - page = addr; - break; - } - - return page; -} - -static bool avr_isp_flash_write_pages( - AvrIsp* instance, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - size_t x = 0; - uint16_t page = avr_isp_current_page(instance, addr, page_size); - - while(x < data_size) { - if(page != avr_isp_current_page(instance, addr, page_size)) { - avr_isp_commit(instance, page, data[x - 1]); - page = avr_isp_current_page(instance, addr, page_size); - } - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_LO(addr, data[x++])); - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_HI(addr, data[x++])); - addr++; - } - avr_isp_commit(instance, page, data[x - 1]); - return true; -} - -bool avr_isp_erase_chip(AvrIsp* instance) { - furi_assert(instance); - - bool ret = false; - if(!instance->pmode) avr_isp_auto_set_spi_speed_start_pmode(instance); - if(instance->pmode) { - avr_isp_spi_transaction(instance, AVR_ISP_ERASE_CHIP); - furi_delay_ms(100); - avr_isp_end_pmode(instance); - ret = true; - } - return ret; -} - -static bool - avr_isp_eeprom_write(AvrIsp* instance, uint16_t addr, uint8_t* data, uint32_t data_size) { - furi_assert(instance); - - for(uint16_t i = 0; i < data_size; i++) { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, data[i])); - furi_delay_ms(10); - addr++; - } - return true; -} - -bool avr_isp_write_page( - AvrIsp* instance, - uint32_t mem_type, - uint32_t mem_size, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - bool ret = false; - switch(mem_type) { - case STK_SET_FLASH_TYPE: - if((addr + data_size / 2) <= mem_size) { - ret = avr_isp_flash_write_pages(instance, addr, page_size, data, data_size); - } - break; - - case STK_SET_EEPROM_TYPE: - if((addr + data_size) <= mem_size) { - ret = avr_isp_eeprom_write(instance, addr, data, data_size); - } - break; - - default: - furi_crash(TAG " Incorrect mem type."); - break; - } - - return ret; -} - -static bool avr_isp_flash_read_page( - AvrIsp* instance, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - if(page_size > data_size) return false; - for(uint16_t i = 0; i < page_size; i += 2) { - data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(addr)); - data[i + 1] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)); - addr++; - } - return true; -} - -static bool avr_isp_eeprom_read_page( - AvrIsp* instance, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - if(page_size > data_size) return false; - for(uint16_t i = 0; i < page_size; i++) { - data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr)); - addr++; - } - return true; -} - -bool avr_isp_read_page( - AvrIsp* instance, - uint32_t mem_type, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - - bool res = false; - if(mem_type == STK_SET_FLASH_TYPE) - res = avr_isp_flash_read_page(instance, addr, page_size, data, data_size); - if(mem_type == STK_SET_EEPROM_TYPE) - res = avr_isp_eeprom_read_page(instance, addr, page_size, data, data_size); - - return res; -} - -AvrIspSignature avr_isp_read_signature(AvrIsp* instance) { - furi_assert(instance); - - AvrIspSignature signature; - signature.vendor = avr_isp_spi_transaction(instance, AVR_ISP_READ_VENDOR); - signature.part_family = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); - signature.part_number = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); - return signature; -} - -uint8_t avr_isp_read_lock_byte(AvrIsp* instance) { - furi_assert(instance); - - uint8_t data = 0; - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 300) { - data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE); - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) { - break; - }; - data = 0x00; - } - return data; -} - -bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) { - furi_assert(instance); - - bool ret = false; - if(avr_isp_read_lock_byte(instance) == lock) { - ret = true; - } else { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_LOCK_BYTE(lock)); - /* polling lock byte */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) { - ret = true; - break; - }; - } - } - return ret; -} - -uint8_t avr_isp_read_fuse_low(AvrIsp* instance) { - furi_assert(instance); - - uint8_t data = 0; - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 300) { - data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW); - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) { - break; - }; - data = 0x00; - } - return data; -} - -bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) { - furi_assert(instance); - - bool ret = false; - if(avr_isp_read_fuse_low(instance) == lfuse) { - ret = true; - } else { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_LOW(lfuse)); - /* polling fuse */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) { - ret = true; - break; - }; - } - } - return ret; -} - -uint8_t avr_isp_read_fuse_high(AvrIsp* instance) { - furi_assert(instance); - - uint8_t data = 0; - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 300) { - data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH); - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) { - break; - }; - data = 0x00; - } - return data; -} - -bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) { - furi_assert(instance); - - bool ret = false; - if(avr_isp_read_fuse_high(instance) == hfuse) { - ret = true; - } else { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_HIGH(hfuse)); - /* polling fuse */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) { - ret = true; - break; - }; - } - } - return ret; -} - -uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) { - furi_assert(instance); - - uint8_t data = 0; - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 300) { - data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED); - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) { - break; - }; - data = 0x00; - } - return data; -} - -bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) { - furi_assert(instance); - - bool ret = false; - if(avr_isp_read_fuse_extended(instance) == efuse) { - ret = true; - } else { - avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_EXTENDED(efuse)); - /* polling fuse */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) { - ret = true; - break; - }; - } - } - return ret; -} - -void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr) { - furi_assert(instance); - - avr_isp_spi_transaction(instance, AVR_ISP_EXTENDED_ADDR(extended_addr)); - furi_delay_ms(10); -} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp.h b/applications/external/avr_isp_programmer/helpers/avr_isp.h deleted file mode 100644 index 476fc3d64..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include - -typedef struct AvrIsp AvrIsp; -typedef void (*AvrIspCallback)(void* context); - -struct AvrIspSignature { - uint8_t vendor; - uint8_t part_family; - uint8_t part_number; -}; - -typedef struct AvrIspSignature AvrIspSignature; - -AvrIsp* avr_isp_alloc(void); - -void avr_isp_free(AvrIsp* instance); - -void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context); - -bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance); - -AvrIspSignature avr_isp_read_signature(AvrIsp* instance); - -void avr_isp_end_pmode(AvrIsp* instance); - -bool avr_isp_erase_chip(AvrIsp* instance); - -uint8_t avr_isp_spi_transaction( - AvrIsp* instance, - uint8_t cmd, - uint8_t addr_hi, - uint8_t addr_lo, - uint8_t data); - -bool avr_isp_read_page( - AvrIsp* instance, - uint32_t memtype, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size); - -bool avr_isp_write_page( - AvrIsp* instance, - uint32_t mem_type, - uint32_t mem_size, - uint16_t addr, - uint16_t page_size, - uint8_t* data, - uint32_t data_size); - -uint8_t avr_isp_read_lock_byte(AvrIsp* instance); - -bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock); - -uint8_t avr_isp_read_fuse_low(AvrIsp* instance); - -bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse); - -uint8_t avr_isp_read_fuse_high(AvrIsp* instance); - -bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse); - -uint8_t avr_isp_read_fuse_extended(AvrIsp* instance); - -bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse); - -void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_event.h b/applications/external/avr_isp_programmer/helpers/avr_isp_event.h deleted file mode 100644 index c704b5b35..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_event.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -typedef enum { - //SubmenuIndex - SubmenuIndexAvrIspProgrammer = 10, - SubmenuIndexAvrIspReader, - SubmenuIndexAvrIspWriter, - SubmenuIndexAvrIsWiring, - SubmenuIndexAvrIspAbout, - - //AvrIspCustomEvent - AvrIspCustomEventSceneChipDetectOk = 100, - AvrIspCustomEventSceneReadingOk, - AvrIspCustomEventSceneWritingOk, - AvrIspCustomEventSceneErrorVerification, - AvrIspCustomEventSceneErrorReading, - AvrIspCustomEventSceneErrorWriting, - AvrIspCustomEventSceneErrorWritingFuse, - AvrIspCustomEventSceneInputName, - AvrIspCustomEventSceneSuccess, - AvrIspCustomEventSceneExit, - AvrIspCustomEventSceneExitStartMenu, -} AvrIspCustomEvent; diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_types.h b/applications/external/avr_isp_programmer/helpers/avr_isp_types.h deleted file mode 100644 index 5e174ec3b..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_types.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -#define AVR_ISP_VERSION_APP "0.1" -#define AVR_ISP_DEVELOPED "SkorP" -#define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" - -#define AVR_ISP_APP_FILE_VERSION 1 -#define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR" -#define AVR_ISP_APP_EXTENSION ".avr" - -typedef enum { - //AvrIspViewVariableItemList, - AvrIspViewSubmenu, - AvrIspViewProgrammer, - AvrIspViewReader, - AvrIspViewWriter, - AvrIspViewWidget, - AvrIspViewPopup, - AvrIspViewTextInput, - AvrIspViewChipDetect, -} AvrIspView; - -typedef enum { - AvrIspErrorNoError, - AvrIspErrorReading, - AvrIspErrorWriting, - AvrIspErrorVerification, - AvrIspErrorWritingFuse, -} AvrIspError; \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.c deleted file mode 100644 index dfe1f43c2..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.c +++ /dev/null @@ -1,266 +0,0 @@ -#include "avr_isp_worker.h" -#include -#include "../lib/driver/avr_isp_prog.h" -#include "../lib/driver/avr_isp_prog_cmd.h" -#include "../lib/driver/avr_isp_chip_arr.h" - -#include - -#define TAG "AvrIspWorker" - -typedef enum { - AvrIspWorkerEvtStop = (1 << 0), - - AvrIspWorkerEvtRx = (1 << 1), - AvrIspWorkerEvtTxCoplete = (1 << 2), - AvrIspWorkerEvtTx = (1 << 3), - AvrIspWorkerEvtState = (1 << 4), - - //AvrIspWorkerEvtCfg = (1 << 5), - -} AvrIspWorkerEvt; - -struct AvrIspWorker { - FuriThread* thread; - volatile bool worker_running; - uint8_t connect_usb; - AvrIspWorkerCallback callback; - void* context; -}; - -#define AVR_ISP_WORKER_PROG_ALL_EVENTS (AvrIspWorkerEvtStop) -#define AVR_ISP_WORKER_ALL_EVENTS \ - (AvrIspWorkerEvtTx | AvrIspWorkerEvtTxCoplete | AvrIspWorkerEvtRx | AvrIspWorkerEvtStop | \ - AvrIspWorkerEvtState) - -//########################/* VCP CDC */############################################# -#include "usb_cdc.h" -#include -#include -#include - -#define AVR_ISP_VCP_CDC_CH 1 -#define AVR_ISP_VCP_CDC_PKT_LEN CDC_DATA_SZ -#define AVR_ISP_VCP_UART_RX_BUF_SIZE (AVR_ISP_VCP_CDC_PKT_LEN * 5) - -static void vcp_on_cdc_tx_complete(void* context); -static void vcp_on_cdc_rx(void* context); -static void vcp_state_callback(void* context, uint8_t state); -static void vcp_on_cdc_control_line(void* context, uint8_t state); -static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config); - -static const CdcCallbacks cdc_cb = { - vcp_on_cdc_tx_complete, - vcp_on_cdc_rx, - vcp_state_callback, - vcp_on_cdc_control_line, - vcp_on_line_config, -}; - -/* VCP callbacks */ - -static void vcp_on_cdc_tx_complete(void* context) { - furi_assert(context); - AvrIspWorker* instance = context; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTxCoplete); -} - -static void vcp_on_cdc_rx(void* context) { - furi_assert(context); - AvrIspWorker* instance = context; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); -} - -static void vcp_state_callback(void* context, uint8_t state) { - UNUSED(context); - - AvrIspWorker* instance = context; - instance->connect_usb = state; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtState); -} - -static void vcp_on_cdc_control_line(void* context, uint8_t state) { - UNUSED(context); - UNUSED(state); -} - -static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) { - UNUSED(context); - UNUSED(config); -} - -static void avr_isp_worker_vcp_cdc_init(void* context) { - furi_hal_usb_unlock(); - Cli* cli = furi_record_open(RECORD_CLI); - //close cli - cli_session_close(cli); - //disable callbacks VCP_CDC=0 - furi_hal_cdc_set_callbacks(0, NULL, NULL); - //set 2 cdc - furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); - //open cli VCP_CDC=0 - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); - - furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, (CdcCallbacks*)&cdc_cb, context); -} - -static void avr_isp_worker_vcp_cdc_deinit(void) { - //disable callbacks AVR_ISP_VCP_CDC_CH - furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, NULL, NULL); - - Cli* cli = furi_record_open(RECORD_CLI); - //close cli - cli_session_close(cli); - furi_hal_usb_unlock(); - //set 1 cdc - furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); - //open cli VCP_CDC=0 - cli_session_open(cli, &cli_vcp); - furi_record_close(RECORD_CLI); -} - -//################################################################################# - -static int32_t avr_isp_worker_prog_thread(void* context) { - AvrIspProg* prog = context; - FURI_LOG_D(TAG, "AvrIspProgWorker Start"); - while(1) { - if(furi_thread_flags_get() & AvrIspWorkerEvtStop) break; - avr_isp_prog_avrisp(prog); - } - FURI_LOG_D(TAG, "AvrIspProgWorker Stop"); - return 0; -} - -static void avr_isp_worker_prog_tx_data(void* context) { - furi_assert(context); - AvrIspWorker* instance = context; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTx); -} - -/** Worker thread - * - * @param context - * @return exit code - */ -static int32_t avr_isp_worker_thread(void* context) { - AvrIspWorker* instance = context; - avr_isp_worker_vcp_cdc_init(instance); - - /* start PWM on &gpio_ext_pa4 */ - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); - - AvrIspProg* prog = avr_isp_prog_init(); - avr_isp_prog_set_tx_callback(prog, avr_isp_worker_prog_tx_data, instance); - - uint8_t buf[AVR_ISP_VCP_UART_RX_BUF_SIZE]; - size_t len = 0; - - FuriThread* prog_thread = - furi_thread_alloc_ex("AvrIspProgWorker", 1024, avr_isp_worker_prog_thread, prog); - furi_thread_start(prog_thread); - - FURI_LOG_D(TAG, "Start"); - - while(instance->worker_running) { - uint32_t events = - furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); - - if(events & AvrIspWorkerEvtRx) { - if(avr_isp_prog_spaces_rx(prog) >= AVR_ISP_VCP_CDC_PKT_LEN) { - len = furi_hal_cdc_receive(AVR_ISP_VCP_CDC_CH, buf, AVR_ISP_VCP_CDC_PKT_LEN); - // for(uint8_t i = 0; i < len; i++) { - // FURI_LOG_I(TAG, "--> %X", buf[i]); - // } - avr_isp_prog_rx(prog, buf, len); - } else { - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); - } - } - - if((events & AvrIspWorkerEvtTxCoplete) || (events & AvrIspWorkerEvtTx)) { - len = avr_isp_prog_tx(prog, buf, AVR_ISP_VCP_CDC_PKT_LEN); - - // for(uint8_t i = 0; i < len; i++) { - // FURI_LOG_I(TAG, "<-- %X", buf[i]); - // } - - if(len > 0) furi_hal_cdc_send(AVR_ISP_VCP_CDC_CH, buf, len); - } - - if(events & AvrIspWorkerEvtStop) { - break; - } - - if(events & AvrIspWorkerEvtState) { - if(instance->callback) - instance->callback(instance->context, (bool)instance->connect_usb); - } - } - - FURI_LOG_D(TAG, "Stop"); - - furi_thread_flags_set(furi_thread_get_id(prog_thread), AvrIspWorkerEvtStop); - avr_isp_prog_exit(prog); - furi_delay_ms(10); - furi_thread_join(prog_thread); - furi_thread_free(prog_thread); - - avr_isp_prog_free(prog); - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - avr_isp_worker_vcp_cdc_deinit(); - return 0; -} - -AvrIspWorker* avr_isp_worker_alloc(void* context) { - furi_assert(context); - UNUSED(context); - AvrIspWorker* instance = malloc(sizeof(AvrIspWorker)); - - instance->thread = furi_thread_alloc_ex("AvrIspWorker", 2048, avr_isp_worker_thread, instance); - return instance; -} - -void avr_isp_worker_free(AvrIspWorker* instance) { - furi_assert(instance); - - furi_check(!instance->worker_running); - furi_thread_free(instance->thread); - free(instance); -} - -void avr_isp_worker_set_callback( - AvrIspWorker* instance, - AvrIspWorkerCallback callback, - void* context) { - furi_assert(instance); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_worker_start(AvrIspWorker* instance) { - furi_assert(instance); - furi_assert(!instance->worker_running); - - instance->worker_running = true; - - furi_thread_start(instance->thread); -} - -void avr_isp_worker_stop(AvrIspWorker* instance) { - furi_assert(instance); - furi_assert(instance->worker_running); - - instance->worker_running = false; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtStop); - - furi_thread_join(instance->thread); -} - -bool avr_isp_worker_is_running(AvrIspWorker* instance) { - furi_assert(instance); - - return instance->worker_running; -} diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h b/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h deleted file mode 100644 index cd9897dff..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include - -typedef struct AvrIspWorker AvrIspWorker; - -typedef void (*AvrIspWorkerCallback)(void* context, bool connect_usb); - -/** Allocate AvrIspWorker - * - * @param context AvrIsp* context - * @return AvrIspWorker* - */ -AvrIspWorker* avr_isp_worker_alloc(void* context); - -/** Free AvrIspWorker - * - * @param instance AvrIspWorker instance - */ -void avr_isp_worker_free(AvrIspWorker* instance); - -/** Callback AvrIspWorker - * - * @param instance AvrIspWorker instance - * @param callback AvrIspWorkerOverrunCallback callback - * @param context - */ -void avr_isp_worker_set_callback( - AvrIspWorker* instance, - AvrIspWorkerCallback callback, - void* context); - -/** Start AvrIspWorker - * - * @param instance AvrIspWorker instance - */ -void avr_isp_worker_start(AvrIspWorker* instance); - -/** Stop AvrIspWorker - * - * @param instance AvrIspWorker instance - */ -void avr_isp_worker_stop(AvrIspWorker* instance); - -/** Check if worker is running - * @param instance AvrIspWorker instance - * @return bool - true if running - */ -bool avr_isp_worker_is_running(AvrIspWorker* instance); diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c deleted file mode 100644 index 121f08565..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c +++ /dev/null @@ -1,1157 +0,0 @@ -#include "avr_isp_worker_rw.h" -#include -#include "avr_isp_types.h" -#include "avr_isp.h" -#include "../lib/driver/avr_isp_prog_cmd.h" -#include "../lib/driver/avr_isp_chip_arr.h" - -#include "flipper_i32hex_file.h" -#include - -#include - -#define TAG "AvrIspWorkerRW" - -#define NAME_PATERN_FLASH_FILE "flash.hex" -#define NAME_PATERN_EEPROM_FILE "eeprom.hex" - -struct AvrIspWorkerRW { - AvrIsp* avr_isp; - FuriThread* thread; - volatile bool worker_running; - - uint32_t chip_arr_ind; - bool chip_detect; - uint8_t lfuse; - uint8_t hfuse; - uint8_t efuse; - uint8_t lock; - float progress_flash; - float progress_eeprom; - const char* file_path; - const char* file_name; - AvrIspSignature signature; - AvrIspWorkerRWCallback callback; - void* context; - - AvrIspWorkerRWStatusCallback callback_status; - void* context_status; -}; - -typedef enum { - AvrIspWorkerRWEvtStop = (1 << 0), - - AvrIspWorkerRWEvtReading = (1 << 1), - AvrIspWorkerRWEvtVerification = (1 << 2), - AvrIspWorkerRWEvtWriting = (1 << 3), - AvrIspWorkerRWEvtWritingFuse = (1 << 4), - -} AvrIspWorkerRWEvt; -#define AVR_ISP_WORKER_ALL_EVENTS \ - (AvrIspWorkerRWEvtWritingFuse | AvrIspWorkerRWEvtWriting | AvrIspWorkerRWEvtVerification | \ - AvrIspWorkerRWEvtReading | AvrIspWorkerRWEvtStop) - -/** Worker thread - * - * @param context - * @return exit code - */ -static int32_t avr_isp_worker_rw_thread(void* context) { - AvrIspWorkerRW* instance = context; - - /* start PWM on &gpio_ext_pa4 */ - if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); - } - - FURI_LOG_D(TAG, "Start"); - - while(1) { - uint32_t events = - furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); - - if(events & AvrIspWorkerRWEvtStop) { - break; - } - - if(events & AvrIspWorkerRWEvtWritingFuse) { - if(avr_isp_worker_rw_write_fuse(instance, instance->file_path, instance->file_name)) { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusEndWritingFuse); - } else { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusErrorWritingFuse); - } - } - - if(events & AvrIspWorkerRWEvtWriting) { - if(avr_isp_worker_rw_write_dump(instance, instance->file_path, instance->file_name)) { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusEndWriting); - } else { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusErrorWriting); - } - } - - if(events & AvrIspWorkerRWEvtVerification) { - if(avr_isp_worker_rw_verification(instance, instance->file_path, instance->file_name)) { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusEndVerification); - } else { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusErrorVerification); - } - } - - if(events & AvrIspWorkerRWEvtReading) { - if(avr_isp_worker_rw_read_dump(instance, instance->file_path, instance->file_name)) { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusEndReading); - } else { - if(instance->callback_status) - instance->callback_status( - instance->context_status, AvrIspWorkerRWStatusErrorReading); - } - } - } - FURI_LOG_D(TAG, "Stop"); - - if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - } - - return 0; -} - -bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance) { - furi_assert(instance); - - FURI_LOG_D(TAG, "Detecting AVR chip"); - - instance->chip_detect = false; - instance->chip_arr_ind = avr_isp_chip_arr_size + 1; - - /* start PWM on &gpio_ext_pa4 */ - bool was_pwm_enabled = false; - if(!furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4)) { - furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); - } else { - was_pwm_enabled = true; - } - - do { - if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); - break; - } - instance->signature = avr_isp_read_signature(instance->avr_isp); - - if(instance->signature.vendor != 0x1E) { - //No detect chip - } else { - for(uint32_t ind = 0; ind < avr_isp_chip_arr_size; ind++) { - if(avr_isp_chip_arr[ind].avrarch != F_AVR8) continue; - if(avr_isp_chip_arr[ind].sigs[1] == instance->signature.part_family) { - if(avr_isp_chip_arr[ind].sigs[2] == instance->signature.part_number) { - FURI_LOG_D(TAG, "Detect AVR chip = \"%s\"", avr_isp_chip_arr[ind].name); - FURI_LOG_D( - TAG, - "Signature = 0x%02X 0x%02X 0x%02X", - instance->signature.vendor, - instance->signature.part_family, - instance->signature.part_number); - - switch(avr_isp_chip_arr[ind].nfuses) { - case 1: - instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); - FURI_LOG_D(TAG, "Lfuse = %02X", instance->lfuse); - break; - case 2: - instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); - instance->hfuse = avr_isp_read_fuse_high(instance->avr_isp); - FURI_LOG_D( - TAG, "Lfuse = %02X Hfuse = %02X", instance->lfuse, instance->hfuse); - break; - case 3: - instance->lfuse = avr_isp_read_fuse_low(instance->avr_isp); - instance->hfuse = avr_isp_read_fuse_high(instance->avr_isp); - instance->efuse = avr_isp_read_fuse_extended(instance->avr_isp); - FURI_LOG_D( - TAG, - "Lfuse = %02X Hfuse = %02X Efuse = %02X", - instance->lfuse, - instance->hfuse, - instance->efuse); - break; - default: - break; - } - if(avr_isp_chip_arr[ind].nlocks == 1) { - instance->lock = avr_isp_read_lock_byte(instance->avr_isp); - FURI_LOG_D(TAG, "Lock = %02X", instance->lock); - } - instance->chip_detect = true; - instance->chip_arr_ind = ind; - break; - } - } - } - } - avr_isp_end_pmode(instance->avr_isp); - - } while(0); - - if(furi_hal_pwm_is_running(FuriHalPwmOutputIdLptim2PA4) && !was_pwm_enabled) { - furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); - } - - if(instance->callback) { - if(instance->chip_arr_ind > avr_isp_chip_arr_size) { - instance->callback(instance->context, "No detect", instance->chip_detect, 0); - } else if(instance->chip_arr_ind < avr_isp_chip_arr_size) { - instance->callback( - instance->context, - avr_isp_chip_arr[instance->chip_arr_ind].name, - instance->chip_detect, - avr_isp_chip_arr[instance->chip_arr_ind].flashsize); - } else { - instance->callback(instance->context, "Unknown", instance->chip_detect, 0); - } - } - - return instance->chip_detect; -} - -AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context) { - furi_assert(context); - UNUSED(context); - - AvrIspWorkerRW* instance = malloc(sizeof(AvrIspWorkerRW)); - instance->avr_isp = avr_isp_alloc(); - - instance->thread = - furi_thread_alloc_ex("AvrIspWorkerRW", 4096, avr_isp_worker_rw_thread, instance); - - instance->chip_detect = false; - instance->lfuse = 0; - instance->hfuse = 0; - instance->efuse = 0; - instance->lock = 0; - instance->progress_flash = 0.0f; - instance->progress_eeprom = 0.0f; - - return instance; -} - -void avr_isp_worker_rw_free(AvrIspWorkerRW* instance) { - furi_assert(instance); - - avr_isp_free(instance->avr_isp); - - furi_check(!instance->worker_running); - furi_thread_free(instance->thread); - - free(instance); -} - -void avr_isp_worker_rw_start(AvrIspWorkerRW* instance) { - furi_assert(instance); - furi_assert(!instance->worker_running); - - instance->worker_running = true; - - furi_thread_start(instance->thread); -} - -void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance) { - furi_assert(instance); - furi_assert(instance->worker_running); - - instance->worker_running = false; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtStop); - - furi_thread_join(instance->thread); -} - -bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance) { - furi_assert(instance); - - return instance->worker_running; -} - -void avr_isp_worker_rw_set_callback( - AvrIspWorkerRW* instance, - AvrIspWorkerRWCallback callback, - void* context) { - furi_assert(instance); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_worker_rw_set_callback_status( - AvrIspWorkerRW* instance, - AvrIspWorkerRWStatusCallback callback_status, - void* context_status) { - furi_assert(instance); - - instance->callback_status = callback_status; - instance->context_status = context_status; -} - -float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance) { - furi_assert(instance); - - return instance->progress_flash; -} - -float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance) { - furi_assert(instance); - - return instance->progress_eeprom; -} - -static void avr_isp_worker_rw_get_dump_flash(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_check(instance->avr_isp); - - FURI_LOG_D(TAG, "Dump FLASH %s", file_path); - - FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_write( - file_path, avr_isp_chip_arr[instance->chip_arr_ind].flashoffset); - - uint8_t data[272] = {0}; - bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); - uint8_t extended_addr = 0; - - for(int32_t i = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; - i < avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2; - i += avr_isp_chip_arr[instance->chip_arr_ind].pagesize / 2) { - if(send_extended_addr) { - if(extended_addr <= ((i >> 16) & 0xFF)) { - avr_isp_write_extended_addr(instance->avr_isp, extended_addr); - extended_addr = ((i >> 16) & 0xFF) + 1; - } - } - avr_isp_read_page( - instance->avr_isp, - STK_SET_FLASH_TYPE, - (uint16_t)i, - avr_isp_chip_arr[instance->chip_arr_ind].pagesize, - data, - sizeof(data)); - flipper_i32hex_file_bin_to_i32hex_set_data( - flipper_hex_flash, data, avr_isp_chip_arr[instance->chip_arr_ind].pagesize); - //FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); - instance->progress_flash = - (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); - } - flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_flash); - //FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_flash)); - flipper_i32hex_file_close(flipper_hex_flash); - instance->progress_flash = 1.0f; -} - -static void avr_isp_worker_rw_get_dump_eeprom(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_check(instance->avr_isp); - - FURI_LOG_D(TAG, "Dump EEPROM %s", file_path); - - FlipperI32HexFile* flipper_hex_eeprom = flipper_i32hex_file_open_write( - file_path, avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset); - - int32_t size_data = 32; - uint8_t data[256] = {0}; - - if(size_data > avr_isp_chip_arr[instance->chip_arr_ind].eepromsize) - size_data = avr_isp_chip_arr[instance->chip_arr_ind].eepromsize; - - for(int32_t i = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; - i < avr_isp_chip_arr[instance->chip_arr_ind].eepromsize; - i += size_data) { - avr_isp_read_page( - instance->avr_isp, STK_SET_EEPROM_TYPE, (uint16_t)i, size_data, data, sizeof(data)); - flipper_i32hex_file_bin_to_i32hex_set_data(flipper_hex_eeprom, data, size_data); - FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_eeprom)); - instance->progress_eeprom = - (float)(i) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); - } - flipper_i32hex_file_bin_to_i32hex_set_end_line(flipper_hex_eeprom); - FURI_LOG_D(TAG, "%s", flipper_i32hex_file_get_string(flipper_hex_eeprom)); - flipper_i32hex_file_close(flipper_hex_eeprom); - instance->progress_eeprom = 1.0f; -} - -bool avr_isp_worker_rw_read_dump( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - furi_assert(file_path); - furi_assert(file_name); - - FURI_LOG_D(TAG, "Read dump chip"); - - instance->progress_flash = 0.0f; - instance->progress_eeprom = 0.0f; - bool ret = false; - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - FuriString* file_path_name = furi_string_alloc(); - - if(!avr_isp_worker_rw_detect_chip(instance)) { - FURI_LOG_E(TAG, "No detect AVR chip"); - } else { - do { - furi_string_printf( - file_path_name, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); - if(!flipper_format_file_open_always( - flipper_format, furi_string_get_cstr(file_path_name))) { - FURI_LOG_E(TAG, "flipper_format_file_open_always"); - break; - } - if(!flipper_format_write_header_cstr( - flipper_format, AVR_ISP_APP_FILE_TYPE, AVR_ISP_APP_FILE_VERSION)) { - FURI_LOG_E(TAG, "flipper_format_write_header_cstr"); - break; - } - if(!flipper_format_write_string_cstr( - flipper_format, "Chip name", avr_isp_chip_arr[instance->chip_arr_ind].name)) { - FURI_LOG_E(TAG, "Chip name"); - break; - } - if(!flipper_format_write_hex( - flipper_format, - "Signature", - (uint8_t*)&instance->signature, - sizeof(AvrIspSignature))) { - FURI_LOG_E(TAG, "Unable to add Signature"); - break; - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { - if(!flipper_format_write_hex(flipper_format, "Lfuse", &instance->lfuse, 1)) { - FURI_LOG_E(TAG, "Unable to add Lfuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { - if(!flipper_format_write_hex(flipper_format, "Hfuse", &instance->hfuse, 1)) { - FURI_LOG_E(TAG, "Unable to add Hfuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { - if(!flipper_format_write_hex(flipper_format, "Efuse", &instance->efuse, 1)) { - FURI_LOG_E(TAG, "Unable to add Efuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { - if(!flipper_format_write_hex(flipper_format, "Lock", &instance->lock, 1)) { - FURI_LOG_E(TAG, "Unable to add Lock"); - break; - } - } - furi_string_printf(file_path_name, "%s_%s", file_name, NAME_PATERN_FLASH_FILE); - if(!flipper_format_write_string_cstr( - flipper_format, "Dump_flash", furi_string_get_cstr(file_path_name))) { - FURI_LOG_E(TAG, "Unable to add Dump_flash"); - break; - } - - if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { - furi_string_printf(file_path_name, "%s_%s", file_name, NAME_PATERN_EEPROM_FILE); - if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { - if(!flipper_format_write_string_cstr( - flipper_format, "Dump_eeprom", furi_string_get_cstr(file_path_name))) { - FURI_LOG_E(TAG, "Unable to add Dump_eeprom"); - break; - } - } - } - ret = true; - } while(false); - } - - flipper_format_free(flipper_format); - furi_record_close(RECORD_STORAGE); - - if(ret) { - if(avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - //Dump flash - furi_string_printf( - file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_FLASH_FILE); - avr_isp_worker_rw_get_dump_flash(instance, furi_string_get_cstr(file_path_name)); - //Dump eeprom - if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { - furi_string_printf( - file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_EEPROM_FILE); - avr_isp_worker_rw_get_dump_eeprom(instance, furi_string_get_cstr(file_path_name)); - } - - avr_isp_end_pmode(instance->avr_isp); - } - } - - furi_string_free(file_path_name); - - return true; -} - -void avr_isp_worker_rw_read_dump_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtReading); -} - -static bool avr_isp_worker_rw_verification_flash(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - FURI_LOG_D(TAG, "Verification flash %s", file_path); - - instance->progress_flash = 0.0; - bool ret = true; - - FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_read(file_path); - - uint8_t data_read_flash[272] = {0}; - uint8_t data_read_hex[272] = {0}; - - uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; - bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); - uint8_t extended_addr = 0; - - FlipperI32HexFileRet flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_flash, data_read_hex, sizeof(data_read_hex)); - - while(((flipper_hex_ret.status == FlipperI32HexFileStatusData) || - (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) && - ret) { - switch(flipper_hex_ret.status) { - case FlipperI32HexFileStatusData: - - if(send_extended_addr) { - if(extended_addr <= ((addr >> 16) & 0xFF)) { - avr_isp_write_extended_addr(instance->avr_isp, extended_addr); - extended_addr = ((addr >> 16) & 0xFF) + 1; - } - } - - avr_isp_read_page( - instance->avr_isp, - STK_SET_FLASH_TYPE, - (uint16_t)addr, - flipper_hex_ret.data_size, - data_read_flash, - sizeof(data_read_flash)); - - if(memcmp(data_read_hex, data_read_flash, flipper_hex_ret.data_size) != 0) { - ret = false; - - FURI_LOG_E(TAG, "Verification flash error"); - FURI_LOG_E(TAG, "Addr: 0x%04lX", addr); - for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { - FURI_LOG_RAW_E("%02X ", data_read_hex[i]); - } - FURI_LOG_RAW_E("\r\n"); - for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { - FURI_LOG_RAW_E("%02X ", data_read_flash[i]); - } - FURI_LOG_RAW_E("\r\n"); - } - - addr += flipper_hex_ret.data_size / 2; - instance->progress_flash = - (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); - break; - - case FlipperI32HexFileStatusUdateAddr: - addr = (data_read_hex[0] << 24 | data_read_hex[1] << 16) / 2; - break; - - default: - furi_crash(TAG " Incorrect status."); - break; - } - - flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_flash, data_read_hex, sizeof(data_read_hex)); - } - - flipper_i32hex_file_close(flipper_hex_flash); - instance->progress_flash = 1.0f; - - return ret; -} - -static bool - avr_isp_worker_rw_verification_eeprom(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - FURI_LOG_D(TAG, "Verification eeprom %s", file_path); - - instance->progress_eeprom = 0.0; - bool ret = true; - - FlipperI32HexFile* flipper_hex_eeprom = flipper_i32hex_file_open_read(file_path); - - uint8_t data_read_eeprom[272] = {0}; - uint8_t data_read_hex[272] = {0}; - - uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; - - FlipperI32HexFileRet flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_eeprom, data_read_hex, sizeof(data_read_hex)); - - while(((flipper_hex_ret.status == FlipperI32HexFileStatusData) || - (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) && - ret) { - switch(flipper_hex_ret.status) { - case FlipperI32HexFileStatusData: - avr_isp_read_page( - instance->avr_isp, - STK_SET_EEPROM_TYPE, - (uint16_t)addr, - flipper_hex_ret.data_size, - data_read_eeprom, - sizeof(data_read_eeprom)); - - if(memcmp(data_read_hex, data_read_eeprom, flipper_hex_ret.data_size) != 0) { - ret = false; - FURI_LOG_E(TAG, "Verification eeprom error"); - FURI_LOG_E(TAG, "Addr: 0x%04lX", addr); - for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { - FURI_LOG_RAW_E("%02X ", data_read_hex[i]); - } - FURI_LOG_RAW_E("\r\n"); - for(uint32_t i = 0; i < flipper_hex_ret.data_size; i++) { - FURI_LOG_RAW_E("%02X ", data_read_eeprom[i]); - } - FURI_LOG_RAW_E("\r\n"); - } - - addr += flipper_hex_ret.data_size; - instance->progress_eeprom = - (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); - break; - - case FlipperI32HexFileStatusUdateAddr: - addr = (data_read_hex[0] << 24 | data_read_hex[1] << 16); - break; - - default: - furi_crash(TAG " Incorrect status."); - break; - } - - flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_eeprom, data_read_hex, sizeof(data_read_hex)); - } - - flipper_i32hex_file_close(flipper_hex_eeprom); - instance->progress_eeprom = 1.0f; - - return ret; -} - -bool avr_isp_worker_rw_verification( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - furi_assert(file_path); - furi_assert(file_name); - - FURI_LOG_D(TAG, "Verification chip"); - - instance->progress_flash = 0.0f; - instance->progress_eeprom = 0.0f; - FuriString* file_path_name = furi_string_alloc(); - - bool ret = false; - - if(avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - do { - furi_string_printf( - file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_FLASH_FILE); - if(!avr_isp_worker_rw_verification_flash( - instance, furi_string_get_cstr(file_path_name))) - break; - - if(avr_isp_chip_arr[instance->chip_arr_ind].eepromsize > 0) { - furi_string_printf( - file_path_name, "%s/%s_%s", file_path, file_name, NAME_PATERN_EEPROM_FILE); - - if(!avr_isp_worker_rw_verification_eeprom( - instance, furi_string_get_cstr(file_path_name))) - break; - } - ret = true; - } while(false); - avr_isp_end_pmode(instance->avr_isp); - furi_string_free(file_path_name); - } - return ret; -} - -void avr_isp_worker_rw_verification_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtVerification); -} - -static void avr_isp_worker_rw_write_flash(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_check(instance->avr_isp); - - instance->progress_flash = 0.0; - - FURI_LOG_D(TAG, "Write Flash %s", file_path); - - uint8_t data[288] = {0}; - - FlipperI32HexFile* flipper_hex_flash = flipper_i32hex_file_open_read(file_path); - - uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].flashoffset; - bool send_extended_addr = ((avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2) > 0x10000); - uint8_t extended_addr = 0; - - FlipperI32HexFileRet flipper_hex_ret = - flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_flash, data, sizeof(data)); - - while((flipper_hex_ret.status == FlipperI32HexFileStatusData) || - (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) { - switch(flipper_hex_ret.status) { - case FlipperI32HexFileStatusData: - - if(send_extended_addr) { - if(extended_addr <= ((addr >> 16) & 0xFF)) { - avr_isp_write_extended_addr(instance->avr_isp, extended_addr); - extended_addr = ((addr >> 16) & 0xFF) + 1; - } - } - - if(!avr_isp_write_page( - instance->avr_isp, - STK_SET_FLASH_TYPE, - avr_isp_chip_arr[instance->chip_arr_ind].flashsize, - (uint16_t)addr, - avr_isp_chip_arr[instance->chip_arr_ind].pagesize, - data, - flipper_hex_ret.data_size)) { - break; - } - addr += flipper_hex_ret.data_size / 2; - instance->progress_flash = - (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].flashsize / 2.0f); - break; - - case FlipperI32HexFileStatusUdateAddr: - addr = (data[0] << 24 | data[1] << 16) / 2; - break; - - default: - furi_crash(TAG " Incorrect status."); - break; - } - - flipper_hex_ret = - flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_flash, data, sizeof(data)); - } - - flipper_i32hex_file_close(flipper_hex_flash); - instance->progress_flash = 1.0f; -} - -static void avr_isp_worker_rw_write_eeprom(AvrIspWorkerRW* instance, const char* file_path) { - furi_assert(instance); - furi_check(instance->avr_isp); - - instance->progress_eeprom = 0.0; - uint8_t data[288] = {0}; - - FURI_LOG_D(TAG, "Write EEPROM %s", file_path); - - FlipperI32HexFile* flipper_hex_eeprom_read = flipper_i32hex_file_open_read(file_path); - - uint32_t addr = avr_isp_chip_arr[instance->chip_arr_ind].eepromoffset; - FlipperI32HexFileRet flipper_hex_ret = - flipper_i32hex_file_i32hex_to_bin_get_data(flipper_hex_eeprom_read, data, sizeof(data)); - - while((flipper_hex_ret.status == FlipperI32HexFileStatusData) || - (flipper_hex_ret.status == FlipperI32HexFileStatusUdateAddr)) { - switch(flipper_hex_ret.status) { - case FlipperI32HexFileStatusData: - if(!avr_isp_write_page( - instance->avr_isp, - STK_SET_EEPROM_TYPE, - avr_isp_chip_arr[instance->chip_arr_ind].eepromsize, - (uint16_t)addr, - avr_isp_chip_arr[instance->chip_arr_ind].eeprompagesize, - data, - flipper_hex_ret.data_size)) { - break; - } - addr += flipper_hex_ret.data_size; - instance->progress_eeprom = - (float)(addr) / ((float)avr_isp_chip_arr[instance->chip_arr_ind].eepromsize); - break; - - case FlipperI32HexFileStatusUdateAddr: - addr = data[0] << 24 | data[1] << 16; - break; - - default: - furi_crash(TAG " Incorrect status."); - break; - } - - flipper_hex_ret = flipper_i32hex_file_i32hex_to_bin_get_data( - flipper_hex_eeprom_read, data, sizeof(data)); - } - - flipper_i32hex_file_close(flipper_hex_eeprom_read); - instance->progress_eeprom = 1.0f; -} - -bool avr_isp_worker_rw_write_dump( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - furi_assert(file_path); - furi_assert(file_name); - - FURI_LOG_D(TAG, "Write dump chip"); - - instance->progress_flash = 0.0f; - instance->progress_eeprom = 0.0f; - bool ret = false; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - FuriString* file_path_name = furi_string_alloc(); - - FuriString* temp_str_1 = furi_string_alloc(); - FuriString* temp_str_2 = furi_string_alloc(); - uint32_t temp_data32; - - if(!avr_isp_worker_rw_detect_chip(instance)) { - FURI_LOG_E(TAG, "No detect AVR chip"); - } else { - //upload file with description - do { - furi_string_printf( - file_path_name, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); - if(!flipper_format_file_open_existing( - flipper_format, furi_string_get_cstr(file_path_name))) { - FURI_LOG_E(TAG, "Error open file %s", furi_string_get_cstr(file_path_name)); - break; - } - - if(!flipper_format_read_header(flipper_format, temp_str_1, &temp_data32)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - break; - } - - if((!strcmp(furi_string_get_cstr(temp_str_1), AVR_ISP_APP_FILE_TYPE)) && - temp_data32 == AVR_ISP_APP_FILE_VERSION) { - } else { - FURI_LOG_E(TAG, "Type or version mismatch"); - break; - } - - AvrIspSignature sig_read = {0}; - - if(!flipper_format_read_hex( - flipper_format, "Signature", (uint8_t*)&sig_read, sizeof(AvrIspSignature))) { - FURI_LOG_E(TAG, "Missing Signature"); - break; - } - - if(memcmp( - (uint8_t*)&instance->signature, (uint8_t*)&sig_read, sizeof(AvrIspSignature)) != - 0) { - FURI_LOG_E( - TAG, - "Wrong chip. Connected (%02X %02X %02X), read from file (%02X %02X %02X)", - instance->signature.vendor, - instance->signature.part_family, - instance->signature.part_number, - sig_read.vendor, - sig_read.part_family, - sig_read.part_number); - break; - } - - if(!flipper_format_read_string(flipper_format, "Dump_flash", temp_str_1)) { - FURI_LOG_E(TAG, "Missing Dump_flash"); - break; - } - - //may not be - flipper_format_read_string(flipper_format, "Dump_eeprom", temp_str_2); - ret = true; - } while(false); - } - flipper_format_free(flipper_format); - furi_record_close(RECORD_STORAGE); - - if(ret) { - do { - //checking .hex files for errors - - furi_string_printf( - file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_1)); - - FURI_LOG_D(TAG, "Check flash file"); - FlipperI32HexFile* flipper_hex_flash_read = - flipper_i32hex_file_open_read(furi_string_get_cstr(file_path_name)); - if(flipper_i32hex_file_check(flipper_hex_flash_read)) { - FURI_LOG_D(TAG, "Check flash file: OK"); - } else { - FURI_LOG_E(TAG, "Check flash file: Error"); - ret = false; - } - flipper_i32hex_file_close(flipper_hex_flash_read); - - if(furi_string_size(temp_str_2) > 4) { - furi_string_printf( - file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_2)); - - FURI_LOG_D(TAG, "Check eeprom file"); - FlipperI32HexFile* flipper_hex_eeprom_read = - flipper_i32hex_file_open_read(furi_string_get_cstr(file_path_name)); - if(flipper_i32hex_file_check(flipper_hex_eeprom_read)) { - FURI_LOG_D(TAG, "Check eeprom file: OK"); - } else { - FURI_LOG_E(TAG, "Check eeprom file: Error"); - ret = false; - } - flipper_i32hex_file_close(flipper_hex_eeprom_read); - } - - if(!ret) break; - ret = false; - - //erase chip - FURI_LOG_D(TAG, "Erase chip"); - if(!avr_isp_erase_chip(instance->avr_isp)) { - FURI_LOG_E(TAG, "Erase chip: Error"); - break; - } - - if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); - break; - } - - //write flash - furi_string_printf( - file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_1)); - avr_isp_worker_rw_write_flash(instance, furi_string_get_cstr(file_path_name)); - - //write eeprom - if(furi_string_size(temp_str_2) > 4) { - furi_string_printf( - file_path_name, "%s/%s", file_path, furi_string_get_cstr(temp_str_2)); - avr_isp_worker_rw_write_eeprom(instance, furi_string_get_cstr(file_path_name)); - } - ret = true; - avr_isp_end_pmode(instance->avr_isp); - } while(false); - } - - furi_string_free(file_path_name); - furi_string_free(temp_str_1); - furi_string_free(temp_str_2); - - return ret; -} - -void avr_isp_worker_rw_write_dump_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtWriting); -} - -bool avr_isp_worker_rw_write_fuse( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - furi_assert(file_path); - furi_assert(file_name); - - FURI_LOG_D(TAG, "Write fuse chip"); - - bool ret = false; - uint8_t lfuse; - uint8_t hfuse; - uint8_t efuse; - uint8_t lock; - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - FuriString* temp_str = furi_string_alloc(); - - uint32_t temp_data32; - - if(!avr_isp_worker_rw_detect_chip(instance)) { - FURI_LOG_E(TAG, "No detect AVR chip"); - } else { - //upload file with description - do { - furi_string_printf(temp_str, "%s/%s%s", file_path, file_name, AVR_ISP_APP_EXTENSION); - if(!flipper_format_file_open_existing(flipper_format, furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Error open file %s", furi_string_get_cstr(temp_str)); - break; - } - - if(!flipper_format_read_header(flipper_format, temp_str, &temp_data32)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - break; - } - - if((!strcmp(furi_string_get_cstr(temp_str), AVR_ISP_APP_FILE_TYPE)) && - temp_data32 == AVR_ISP_APP_FILE_VERSION) { - } else { - FURI_LOG_E(TAG, "Type or version mismatch"); - break; - } - - AvrIspSignature sig_read = {0}; - - if(!flipper_format_read_hex( - flipper_format, "Signature", (uint8_t*)&sig_read, sizeof(AvrIspSignature))) { - FURI_LOG_E(TAG, "Missing Signature"); - break; - } - - if(memcmp( - (uint8_t*)&instance->signature, (uint8_t*)&sig_read, sizeof(AvrIspSignature)) != - 0) { - FURI_LOG_E( - TAG, - "Wrong chip. Connected (%02X %02X %02X), read from file (%02X %02X %02X)", - instance->signature.vendor, - instance->signature.part_family, - instance->signature.part_number, - sig_read.vendor, - sig_read.part_family, - sig_read.part_number); - break; - } - - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { - if(!flipper_format_read_hex(flipper_format, "Lfuse", &lfuse, 1)) { - FURI_LOG_E(TAG, "Missing Lfuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { - if(!flipper_format_read_hex(flipper_format, "Hfuse", &hfuse, 1)) { - FURI_LOG_E(TAG, "Missing Hfuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { - if(!flipper_format_read_hex(flipper_format, "Efuse", &efuse, 1)) { - FURI_LOG_E(TAG, "Missing Efuse"); - break; - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { - if(!flipper_format_read_hex(flipper_format, "Lock", &lock, 1)) { - FURI_LOG_E(TAG, "Missing Lock"); - break; - } - } - - if(!avr_isp_auto_set_spi_speed_start_pmode(instance->avr_isp)) { - FURI_LOG_E(TAG, "Well, I managed to enter the mod program"); - break; - } - - ret = true; - - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 0) { - if(instance->lfuse != lfuse) { - if(!avr_isp_write_fuse_low(instance->avr_isp, lfuse)) { - FURI_LOG_E(TAG, "Write Lfuse: error"); - ret = false; - } - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 1) { - if(instance->hfuse != hfuse) { - if(!avr_isp_write_fuse_high(instance->avr_isp, hfuse)) { - FURI_LOG_E(TAG, "Write Hfuse: error"); - ret = false; - } - } - } - if(avr_isp_chip_arr[instance->chip_arr_ind].nfuses > 2) { - if(instance->efuse != efuse) { - if(!avr_isp_write_fuse_extended(instance->avr_isp, efuse)) { - FURI_LOG_E(TAG, "Write Efuse: error"); - ret = false; - } - } - } - - if(avr_isp_chip_arr[instance->chip_arr_ind].nlocks == 1) { - FURI_LOG_D(TAG, "Write lock byte"); - if(instance->lock != lock) { - if(!avr_isp_write_lock_byte(instance->avr_isp, lock)) { - FURI_LOG_E(TAG, "Write Lock byte: error"); - ret = false; - } - } - } - avr_isp_end_pmode(instance->avr_isp); - } while(false); - } - - flipper_format_free(flipper_format); - furi_record_close(RECORD_STORAGE); - furi_string_free(temp_str); - return ret; -} - -void avr_isp_worker_rw_write_fuse_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; - furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerRWEvtWritingFuse); -} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h b/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h deleted file mode 100644 index 2c52a8700..000000000 --- a/applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -#include - -typedef struct AvrIspWorkerRW AvrIspWorkerRW; - -typedef void (*AvrIspWorkerRWCallback)( - void* context, - const char* name, - bool detect_chip, - uint32_t flash_size); - -typedef enum { - AvrIspWorkerRWStatusILDE = 0, - AvrIspWorkerRWStatusEndReading = 1, - AvrIspWorkerRWStatusEndVerification = 2, - AvrIspWorkerRWStatusEndWriting = 3, - AvrIspWorkerRWStatusEndWritingFuse = 4, - - AvrIspWorkerRWStatusErrorReading = (-1), - AvrIspWorkerRWStatusErrorVerification = (-2), - AvrIspWorkerRWStatusErrorWriting = (-3), - AvrIspWorkerRWStatusErrorWritingFuse = (-4), - - AvrIspWorkerRWStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization. -} AvrIspWorkerRWStatus; - -typedef void (*AvrIspWorkerRWStatusCallback)(void* context, AvrIspWorkerRWStatus status); - -AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context); - -void avr_isp_worker_rw_free(AvrIspWorkerRW* instance); - -void avr_isp_worker_rw_start(AvrIspWorkerRW* instance); - -void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance); - -bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance); - -void avr_isp_worker_rw_set_callback( - AvrIspWorkerRW* instance, - AvrIspWorkerRWCallback callback, - void* context); - -void avr_isp_worker_rw_set_callback_status( - AvrIspWorkerRW* instance, - AvrIspWorkerRWStatusCallback callback_status, - void* context_status); - -bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance); - -float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance); - -float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance); - -bool avr_isp_worker_rw_read_dump( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -void avr_isp_worker_rw_read_dump_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -bool avr_isp_worker_rw_verification( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -void avr_isp_worker_rw_verification_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -bool avr_isp_worker_rw_check_hex( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -bool avr_isp_worker_rw_write_dump( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -void avr_isp_worker_rw_write_dump_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -bool avr_isp_worker_rw_write_fuse( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); - -void avr_isp_worker_rw_write_fuse_start( - AvrIspWorkerRW* instance, - const char* file_path, - const char* file_name); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c deleted file mode 100644 index a8c20a0bd..000000000 --- a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c +++ /dev/null @@ -1,321 +0,0 @@ -#include "flipper_i32hex_file.h" -#include -#include -#include -#include -#include - -//https://en.wikipedia.org/wiki/Intel_HEX - -#define TAG "FlipperI32HexFile" - -#define COUNT_BYTE_PAYLOAD 32 //how much payload will be used - -#define I32HEX_TYPE_DATA 0x00 -#define I32HEX_TYPE_END_OF_FILE 0x01 -#define I32HEX_TYPE_EXT_LINEAR_ADDR 0x04 -#define I32HEX_TYPE_START_LINEAR_ADDR 0x05 - -struct FlipperI32HexFile { - uint32_t addr; - uint32_t addr_last; - Storage* storage; - Stream* stream; - FuriString* str_data; - FlipperI32HexFileStatus file_open; -}; - -FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr) { - furi_assert(name); - - FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); - instance->addr = start_addr; - instance->addr_last = 0; - instance->storage = furi_record_open(RECORD_STORAGE); - instance->stream = file_stream_alloc(instance->storage); - - if(file_stream_open(instance->stream, name, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - instance->file_open = FlipperI32HexFileStatusOpenFileWrite; - FURI_LOG_D(TAG, "Open write file %s", name); - } else { - FURI_LOG_E(TAG, "Failed to open file %s", name); - instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; - } - instance->str_data = furi_string_alloc(instance->storage); - - return instance; -} - -FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name) { - furi_assert(name); - - FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); - instance->addr = 0; - instance->addr_last = 0; - instance->storage = furi_record_open(RECORD_STORAGE); - instance->stream = file_stream_alloc(instance->storage); - - if(file_stream_open(instance->stream, name, FSAM_READ, FSOM_OPEN_EXISTING)) { - instance->file_open = FlipperI32HexFileStatusOpenFileRead; - FURI_LOG_D(TAG, "Open read file %s", name); - } else { - FURI_LOG_E(TAG, "Failed to open file %s", name); - instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; - } - instance->str_data = furi_string_alloc(instance->storage); - - return instance; -} - -void flipper_i32hex_file_close(FlipperI32HexFile* instance) { - furi_assert(instance); - - furi_string_free(instance->str_data); - file_stream_close(instance->stream); - stream_free(instance->stream); - furi_record_close(RECORD_STORAGE); -} - -FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( - FlipperI32HexFile* instance, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - furi_assert(data); - - FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; - if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { - ret.status = FlipperI32HexFileStatusErrorFileWrite; - } - uint8_t count_byte = 0; - uint32_t ind = 0; - uint8_t crc = 0; - - furi_string_reset(instance->str_data); - - if((instance->addr_last & 0xFF0000) < (instance->addr & 0xFF0000)) { - crc = 0x02 + 0x04 + ((instance->addr >> 24) & 0xFF) + ((instance->addr >> 16) & 0xFF); - crc = 0x01 + ~crc; - //I32HEX_TYPE_EXT_LINEAR_ADDR - furi_string_cat_printf( - instance->str_data, ":02000004%04lX%02X\r\n", (instance->addr >> 16), crc); - instance->addr_last = instance->addr; - } - - while(ind < data_size) { - if((ind + COUNT_BYTE_PAYLOAD) > data_size) { - count_byte = data_size - ind; - } else { - count_byte = COUNT_BYTE_PAYLOAD; - } - //I32HEX_TYPE_DATA - furi_string_cat_printf( - instance->str_data, ":%02X%04lX00", count_byte, (instance->addr & 0xFFFF)); - crc = count_byte + ((instance->addr >> 8) & 0xFF) + (instance->addr & 0xFF); - - for(uint32_t i = 0; i < count_byte; i++) { - furi_string_cat_printf(instance->str_data, "%02X", *data); - crc += *data++; - } - crc = 0x01 + ~crc; - furi_string_cat_printf(instance->str_data, "%02X\r\n", crc); - - ind += count_byte; - instance->addr += count_byte; - } - if(instance->file_open) stream_write_string(instance->stream, instance->str_data); - return ret; -} - -FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance) { - furi_assert(instance); - - FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; - if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { - ret.status = FlipperI32HexFileStatusErrorFileWrite; - } - furi_string_reset(instance->str_data); - //I32HEX_TYPE_END_OF_FILE - furi_string_cat_printf(instance->str_data, ":00000001FF\r\n"); - if(instance->file_open) stream_write_string(instance->stream, instance->str_data); - return ret; -} - -void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr) { - furi_assert(instance); - - instance->addr = addr; -} - -const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance) { - furi_assert(instance); - - return furi_string_get_cstr(instance->str_data); -} - -static FlipperI32HexFileRet flipper_i32hex_file_parse_line( - FlipperI32HexFile* instance, - const char* str, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - furi_assert(data); - - char* str1; - uint32_t data_wrire_ind = 0; - uint32_t data_len = 0; - FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusErrorData, .data_size = 0}; - - //Search for start of data I32HEX - str1 = strstr(str, ":"); - do { - if(str1 == NULL) { - ret.status = FlipperI32HexFileStatusErrorData; - break; - } - str1++; - if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { - ret.status = FlipperI32HexFileStatusErrorData; - break; - } - str1++; - if(++data_wrire_ind > data_size) { - ret.status = FlipperI32HexFileStatusErrorOverflow; - break; - } - data_len = 5 + data[0]; // +5 bytes per header and crc - while(data_len > data_wrire_ind) { - str1++; - if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { - ret.status = FlipperI32HexFileStatusErrorData; - break; - } - str1++; - if(++data_wrire_ind > data_size) { - ret.status = FlipperI32HexFileStatusErrorOverflow; - break; - } - } - ret.status = FlipperI32HexFileStatusOK; - ret.data_size = data_wrire_ind; - - } while(0); - return ret; -} - -static bool flipper_i32hex_file_check_data(uint8_t* data, uint32_t data_size) { - furi_assert(data); - - uint8_t crc = 0; - uint32_t data_read_ind = 0; - if(data[0] > data_size) return false; - while(data_read_ind < data_size - 1) { - crc += data[data_read_ind++]; - } - return data[data_size - 1] == ((1 + ~crc) & 0xFF); -} - -static FlipperI32HexFileRet flipper_i32hex_file_parse( - FlipperI32HexFile* instance, - const char* str, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - furi_assert(data); - - FlipperI32HexFileRet ret = flipper_i32hex_file_parse_line(instance, str, data, data_size); - - if((ret.status == FlipperI32HexFileStatusOK) && (ret.data_size > 4)) { - switch(data[3]) { - case I32HEX_TYPE_DATA: - if(flipper_i32hex_file_check_data(data, ret.data_size)) { - ret.data_size -= 5; - memcpy(data, data + 4, ret.data_size); - ret.status = FlipperI32HexFileStatusData; - } else { - ret.status = FlipperI32HexFileStatusErrorCrc; - ret.data_size = 0; - } - break; - case I32HEX_TYPE_END_OF_FILE: - if(flipper_i32hex_file_check_data(data, ret.data_size)) { - ret.status = FlipperI32HexFileStatusEofFile; - ret.data_size = 0; - } else { - ret.status = FlipperI32HexFileStatusErrorCrc; - ret.data_size = 0; - } - break; - case I32HEX_TYPE_EXT_LINEAR_ADDR: - if(flipper_i32hex_file_check_data(data, ret.data_size)) { - data[0] = data[4]; - data[1] = data[5]; - data[3] = 0; - data[4] = 0; - ret.status = FlipperI32HexFileStatusUdateAddr; - ret.data_size = 4; - } else { - ret.status = FlipperI32HexFileStatusErrorCrc; - ret.data_size = 0; - } - break; - case I32HEX_TYPE_START_LINEAR_ADDR: - ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; - ret.data_size = 0; - break; - default: - ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; - ret.data_size = 0; - break; - } - } else { - ret.status = FlipperI32HexFileStatusErrorData; - ret.data_size = 0; - } - return ret; -} - -bool flipper_i32hex_file_check(FlipperI32HexFile* instance) { - furi_assert(instance); - - uint32_t data_size = 280; - uint8_t data[280] = {0}; - bool ret = true; - - if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { - FURI_LOG_E(TAG, "File is not open"); - ret = false; - } else { - stream_rewind(instance->stream); - - while(stream_read_line(instance->stream, instance->str_data)) { - FlipperI32HexFileRet parse_ret = flipper_i32hex_file_parse( - instance, furi_string_get_cstr(instance->str_data), data, data_size); - - if(parse_ret.status < 0) { - ret = false; - } - } - stream_rewind(instance->stream); - } - return ret; -} - -FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( - FlipperI32HexFile* instance, - uint8_t* data, - uint32_t data_size) { - furi_assert(instance); - furi_assert(data); - - FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; - if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { - ret.status = FlipperI32HexFileStatusErrorFileRead; - } else { - stream_read_line(instance->stream, instance->str_data); - ret = flipper_i32hex_file_parse( - instance, furi_string_get_cstr(instance->str_data), data, data_size); - } - - return ret; -} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h b/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h deleted file mode 100644 index 765b94beb..000000000 --- a/applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include - -typedef struct FlipperI32HexFile FlipperI32HexFile; - -typedef enum { - FlipperI32HexFileStatusOK = 0, - FlipperI32HexFileStatusData = 2, - FlipperI32HexFileStatusUdateAddr = 3, - FlipperI32HexFileStatusEofFile = 4, - FlipperI32HexFileStatusOpenFileWrite = 5, - FlipperI32HexFileStatusOpenFileRead = 6, - - // Errors - FlipperI32HexFileStatusErrorCrc = (-1), - FlipperI32HexFileStatusErrorOverflow = (-2), - FlipperI32HexFileStatusErrorData = (-3), - FlipperI32HexFileStatusErrorUnsupportedCommand = (-4), - FlipperI32HexFileStatusErrorNoOpenFile = (-5), - FlipperI32HexFileStatusErrorFileWrite = (-6), - FlipperI32HexFileStatusErrorFileRead = (-7), - - FlipperI32HexFileStatusReserved = - 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization. -} FlipperI32HexFileStatus; - -typedef struct { - FlipperI32HexFileStatus status; - uint32_t data_size; -} FlipperI32HexFileRet; - -FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr); - -FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name); - -void flipper_i32hex_file_close(FlipperI32HexFile* instance); - -FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( - FlipperI32HexFile* instance, - uint8_t* data, - uint32_t data_size); - -FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance); - -const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance); - -void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr); - -bool flipper_i32hex_file_check(FlipperI32HexFile* instance); - -FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( - FlipperI32HexFile* instance, - uint8_t* data, - uint32_t data_size); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png b/applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png deleted file mode 100644 index 533787fe3569f70196d3f71e8373102dfd3967a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3614 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 Vpqb^9w@m;7?5&;gRaoD1{{|C}E&c!i diff --git a/applications/external/avr_isp_programmer/images/avr_wiring.png b/applications/external/avr_isp_programmer/images/avr_wiring.png deleted file mode 100644 index 957012405127ce4504ef8cd4e805b1fc31963026..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4513 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 diff --git a/applications/external/avr_isp_programmer/images/chif_not_found_83x37.png b/applications/external/avr_isp_programmer/images/chif_not_found_83x37.png deleted file mode 100644 index b03bf3567ade9b2e4ce2d29328032ebd40c89401..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3742 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 diff --git a/applications/external/avr_isp_programmer/images/chip_error_70x22.png b/applications/external/avr_isp_programmer/images/chip_error_70x22.png deleted file mode 100644 index 16f81178c0ec857591db3fe40830a5955db54e73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3688 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 diff --git a/applications/external/avr_isp_programmer/images/chip_long_70x22.png b/applications/external/avr_isp_programmer/images/chip_long_70x22.png deleted file mode 100644 index 3edfff82de952d6f49a012154a23104e3fd935fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3656 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 diff --git a/applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png b/applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QjS=r55&&8K)tZJ>! zX9!|urpns_+3bLGhpWpJa7G1iR=7U<4q#?(qy>Qh$3$rnPo_4eDBe*|l7 zt*?E0IVl%{I3HrfzVWH??Kkt>Bi(nnZ@7%i#u{xs%!_xbh~O&bT^Ien|%u6t7Zn-j(gUnSv0WUO%}G04p`rhWCnG zY)p@^iEhU3vhKD~_HlseZgR&P04`wVAh`BQ-BvCDz-EUimFr7>YdEZ2&vB$-|40Mx zmb1nUv|Mu|S_sYK#ysNVe4->2tr*c+E~VrQeXl2_R&VVQkw6oGG}=8E(54CgByeRl zDAtB>v+K8U9U@2%MS)yy;bmjE#L~hyq#KOc58jpozljpImNAQ0H-_8X!h!9KrB<|k z_8}vk3}3{bZUYdZTM@NJ@WhY`Ywh=ZPchX6ni4k*@ALM!(c$T_qS+ZeK2IdHqcw8o zdWt;+hhlXwt+4vfhdEW7FT)@$P3Xs`l(`dJJ08oF@D;a6l%FkOtGT)6+WnZpelWzK zo?C;Rfd&(f>Ko(D@s=Nr3&2O@)D8@BYjU&Qux?b4NhmOTBLCvRkLJTJ2zVskSXp-9 zVC*5NP*4=6SyS%dO$_I(}mMxRqYvwdUm z@kfY+wMLN?#WN0b9wv!14nImY&l7)lTf7wq(}XXi&ZP;aQSI7?>s5sq+ z!4BIuIUJhIo2)Pot+O9roT_aB^SX*x`YTI&@fSy22~lsBf805E)laD=bz7?Dwsuir z4ickks%l?pvzq9x%Q=XXr$vvl-pRyW!YfO0g#N-LdJT>!bIMKar%|?;pP5%@P~)%}BB0-Ds^FwxM2hX&pE+kcXgiwElP_wajan;%6nW)J=G0&r zuPFITsaY>CFtg05`C|cfb3czzm3(p>!+c$bwO*@xQ?;a^t;1if zG3T4~Fu8;zLdwLA`08G*2mOYB7z##vwm416O_5v3Ef3^5)T*ilt@n_EG{Ld*@6;wSZnp8}m%X3(&s-=XVLptQ* z?arOAG%U?5Jw8xVT9bbuzuGdvvN&si)Kvbp>P=PQGx747j~v5gRphE`1d@vw>DlYD zrlo|sgljLZ{jsgh$sai=P%L#$D%kglk1*;iYAn6$?vn1c*WZ%op(K2_Q1?gGsj5RA zCz?GoZ8P2(k;F*VzG16Tw{Mz-c0f{eAQ_S^qiuE5rt~%M^Amx6Ynd698I6kt!;h9U zmOPgtNAA5ESBr8$NebGZ0cv;JAzvkt2!YSzW@am;nuUANu9-CiJ{c^pJyyBVS% z;<#^fBk-#9s~BC>F!6iE;G%wXcD25Uer#xI=uAVYv`5>Yai!AhbE#eNU7iBrXM#Tu z^l%bp3AdYq`4qwPk9AkV{%a znlIE|=(a%I9p3iiGw~*u&5j@;N@W_9%P+^b7FQ!DGbeecg2YmxZRcqLIbDt4!t+H7 zAqSOF$$I8dmZuW`r7xsZAR2vqH%`ERdbbRs&6P1#?_khn~!FovP9GUz+{9rstz7@CqB*_T_kOhP(}JensxW?oZ<&1&I%II-u+eQ&30sRan{Ms#kZC1!*QB- zm+$Q^9&9`~ai=Ob!pvSp3O`#{atT?Xh0V8(#3zNt&DCz*?tSj_vtue*jsnR=DYGd86#l`XC;a1QpDeC@HyDPdbSe(l zgjHdxAH33fUQ5h>)75!e7xxhN4fhkLvD7#El<;AL(z_%XRQp}+&;DV@+VyRnH!p|n zKz0`W?)}6~lg-L?-LjiS^Bc*VMPG3nk%&<-0 zbaZiiVf9w0ci_ud;Fi(wF~PfPS`GoGtGG9wL-V2U5=blE(V0n^*McEGMx2N5R|Ua?u5HlLtuj{xo@^N|O`lWhC_G<5l(K<(XSoco+TC5;ue{5Q8M+ASwLe?oA zByv*MXM27tAJgsDEuST}bAP9!OiUCSywSh#p{qBwHz#E!CE*qMYVP)z`UUYv!!3<1 zM_<12SA}2rc6M{Ific36T7EDtXf=Hmd|h$ZUH$OID6hDdM=@P0$o0suBePaK|(w=hS!Qppg)o(;sG zOk<$|Kug!3MsW2a(!nl7k|#x5X1V5-4A|36TgG190%k$O5IsDN1AU0LftEPeKrdIM zn~bgwSj!*9A|Mm#1h7B(GQ}6=uPyTzFN!7asi899zf9;}+A{wM3U6@+jG_7v!I}`b ziYp8T18X87L^lG$Mb(|)stiWJ5O64*b!)1?HBksv6dVcu`;uWf^l@`X*eMIcmI7An306gt6Qh2kswivdgYb@lP2(LJdY z@E#+9|u4GM?A_OkkAXkqccP08ectbOS=#Q(qt3hN`e%SS;1`3Ykcu|H8Wc7klcr*u8-u(^#IdL?2H-qMM-)l??tXYn12jV^RMt z-``lb-^ZfyTP&0n40Nxz|EJf#RICBo6aN`r*5;q_CsJ55@535yKkHgm)`!7y#vEtB zT6cGMa|iE@vZQ@<8%x_=VCEUj6aYYeCRlx(|InYQ3j$4GuJG99-9E$yK6D9-^!kkir((8ExON>}J~Ocpw}BbH6#MyjJ@7P6)WtmSMmDP-fQf-6whZkY`fn1t B&F}yK diff --git a/applications/external/avr_isp_programmer/images/link_waiting_77x56.png b/applications/external/avr_isp_programmer/images/link_waiting_77x56.png deleted file mode 100644 index d7d32aed59a19e24a7e1f2216c806872610a0af5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3883 zcmaJ@cUV*DvpxvYn@AB5Hx%h4gla-hp$7zl1h54X0%AyjXebK;N)s1Eqzhu97o`}b zNtGf30)o<|iO2#gMNrxe`&;*S|G3`soHE}$^S(3lesj)qVo%$c@o`IV0|3BhVUDtA zJ~7Pe6elb5o^UGiF96_1dm)jhEs#hM)sN!gb(shN0V7!sB&@@NsKMsMI>IU@?5-8X zUW5~5kBAEsPLx-c<`T4wk$x~NV%Ky8jb@YV$cbT%j}N;gVyDV`llue5tn|b9>yKh? zzTTb+e&jt=xB01i@7a69`I5D)%3h8}PTmxAO*`!{-a^EQBOkA~x3*2qf{nwu<*0xl zXC*<}e^-_T*b3FxSCMJtcnPos4DfIQjhM_v_2bd|0$&j6XIa8-ur$&VPg!w>2?NGK z@rXRY*pwwKD^1=3$YBF6cDcLF0H@V}iwf614FF+TTj{|pfa_gp5tf`p0CbcXg91GD zfRf%bH_-r29T4`gYJ~wG)Btr0Cl7Pr>2sj5N06ri;N%6=?P4O80JdP@Vu!430B|E6 z5H?+P(*LSbCOEImR4TnfzgiB44tM2L^W|`I0-sRqu@F-c*1;dbXBdN<1JlJd!nFiG zuDt<(oJ0|3w`;orJ^W=oJv#9W{tIef8rb(`+}vjN=6Z{%#sDxy3+>xeg;Yv}>9L2A z_a2^HX7fDHlXGP=&Z9!W_!*G1FygdEJ`p$R4TrLZj2} zca&b8?B6F$PpWRS8cu2hPcIp=1ShH$oO5UWW~CsAqcu)%0>El5 zrRkj_Cu^AJ^{HO^{)*AAS?Xy!a4t5J4$h-^>5&)~x0^WGcuukO$Svt6b2gzkIZ$Veu$_!mqP98I{w5aW zXfCyC;CBcXeb%%lQLh8gh}em$GlSj@udp+C$NLOfU7#y*!}KA~TLKN5ksz9r`PQ#W z!r+$9gZa0`o&qBYhRAmH#?Qw%G+QsLgWFmV<)>7+lH9w>WlDI9+a#WzDPgUR-Ei+M zr?Ux#qZ_$&*ysol{)CA+&KhU)!Mp%;Tu$rA2$wDw>kYeR1(~D*t19`LBi~z(xoJS7 zaptPBLqZ8hA%ej%$W~oBp;)AbLiO!K7Uhqz{X+ew{XX`x3#x^gTILe6Nu47E?+Oms zT~&}uN91hQY|E_XtmLfpsw;Pvo3ZcXEr)4E``4E&#peX)wC31}X&NSuk237X3m#yP zXeYQJN*^%npV&ng9M!s#0qedlYGIXI`Y?Gw!c)w1)9cA+TFsI1#^YTTBTyKvdDT-$v<2XhVryqNgW}PQK5GUS_Ro8_srp>1dq*EMm$_(Y-MG{|g zCtD`VCrc_ru!Ti=MH59lj%$ux*o4CK4k2Zxj+zcLglRz&W4oO43o~_XARc$|$^cbqZ@%KFE8*I$^5xybzh70ZP1}{K zjWZ}Jd;mjgT538~+OOU9Fyfd=^WC~fv*DUo%uihly*VMgqBN}}nWtr44JDrSE=oyF z!4;bq+ZCHF*6WllCXn`uQKnLm<1@UGk6o4KrRGdnKtuS9O%Nh2V z>O7@9J!?Jd_U<>`54(rbwKEN%?=|K#=QH1DPCmcr65yiBC}6xGT2#!sU<(y zV9vQXN0)Pzrlnb>Cx>cFYx9rfSKB1n6lV{STAqGobTSH`i$9(Fz&={WATvVnBsVeA z^H*gp%SrV~AvGa?>>6;~dZ?x_!Wjky7zisJ2ezcqGGvc|QtnNKo5^9UI4JSRDmxZ`P5}iulKYgA{ zFWSVfh#7t}^t(S}IHRvSp)uin;f-$N^N#0Twk?$G3z3t^YqI-<{h<9mAV2IR3yC#0 z+$7xf(Dqi)@6rwNM(|PMw~FB^~mEN3B>q+eK;*UHX z`g!Or2mTX2t|gRLAu>ABDat6G8iSMQgQjZJ`^J#|lc*o46x2i}32F;_qGqYBY*+-o zq(7otqg7+n2KI1%GlG)iJIk~g67CoIc%`+1$mImoKM-6>0@`R3X5B-3B4Zu9t)o))UsXqQ;JeQrSkjm4UbguO`fS*+W3YZg`{>X zj@DjhAgdoW=)b5V=6CjV>ltAmW7n}iusX~ARPwCYuNd6 z)RDyzGw3l$+_u=R+%zhSEn3)0*(RSWwITa1wX^oK?sCZTGu~If8BU=j)C8mk=4N8K#*I z8QZRIt~IuA4Eu(@Oa$$ijs7NZPfOo9&~gpi={2$tF_1)B?Y)(ioD~uZ{yuhb^dTd7 z-o0n?k^p6;MvykukKT`)*Q?X(IlKCTwpuYdchu>HQ^phc1@af#7yZ4Y0o(T4d$k#5 z)n~n{mxJn`1$%5RNM`HyjIY-Reihvx8q9_njMuLPQ8r&~ZcK`fhx#e(_H@+_(-oFW z>ul>TtQ#+x3?s**2aR0!#y+f!UAxps&spmmGuvd3yxzN)xRD@$Je-i8&=tiOwU~X% z5C)qz^4ne5$w&4QdgZgl_8#tam5GT$LbnDN-}m&T^*u;kO-*Vb|DL=1rEyXG$!J@1 z+liN*0h-YB>u0u?n&@M6sg*~Q0=BcigRUv=dwwt9aCn=)og|)=w9m$xwzjjPeK&&n zUnx#Q<7f^P4;mfsM+8g=6gMKsf{Z5-?TL6opl>Hp9{^Yty|6eM4r2{>r;x$;gBWlC znaV^1fWA=x74Pm%q=DRsBrhKWnU&fG8ITvjK*mWMqmH2>iJo5OL4HJsARDZEkheRG zAY)_*(hq<$3CKhm9uz>n?Bfp)Fp&A17tXW~+z=Vi-yt+_1DXF6g~OZ%At`=DkS-Xi z=B}=;4$_5zi3Gfco2CceT@|FEt^tKWnWwHAR2QzH35UW!{~R*Rgnk4MxIN1BpLEQX zfs7}OMukHlbUGbO*924iNDwFt27{<;Kr}Sem=S9Jfj%^RfSQlL>`w+1(cj(Ai%RpN z_<#-=@otnWGy@rCvH$6UO#PSE$NwLtn3_QX@KgvCtbWkd&p-_3{|_aT|Bd#i*%SX; z@Bc~cj}4>}A@)Rn$`wC%=H7Y89;Bkek$yxxjpB!;P%i%z^0X&~M)CKgP(d1+U?@lt zgLn7xIq)d`4Z&dG7C!zoypKE40%ah>BmsMQ5twSCbkW*qZKOI=XDAwl(9$(UYeO}l zXs9U~iq`yzMN!x+ z{=pJ{U5nN)u@Gi4kb}MbUwi%2#T=jm^WWiRF8&>Vq7QTC{g}hea>zo1`C_o2w#K6O z_xG8mWAi{L0I=v-piHm4_!@n4Ng?1=)yPg7kcR`b10W6l{AWbI9sWut$neGM3y@Fi6uo^^Vs - -//https://github.com/avrdudes/avrdude/blob/master/src/avrintel.c - -const AvrIspChipArr avr_isp_chip_arr[] = { // Value of -1 typically means unknown - //{mcu_name, mcuid, family, {sig, na, ture}, flstart, flsize, pgsiz, nb, bootsz, eestart, eesize, ep, rambeg, ramsiz, nf, nl, ni}, // Source - {"ATtiny4", 0, F_AVR8L, {0x1E, 0x8F, 0x0A}, 0, 0x00200, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny5", 1, F_AVR8L, {0x1E, 0x8F, 0x09}, 0, 0x00200, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny9", 2, F_AVR8L, {0x1E, 0x90, 0x08}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny10", 3, F_AVR8L, {0x1E, 0x90, 0x03}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny20", 4, F_AVR8L, {0x1E, 0x91, 0x0F}, 0, 0x00800, 0x020, 0, 0, 0, 0, 0, 0x0040, 0x0080, 1, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny40", 5, F_AVR8L, {0x1E, 0x92, 0x0E}, 0, 0x01000, 0x040, 0, 0, 0, 0, 0, 0x0040, 0x0100, 1, 1, 18}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATtiny102", 6, F_AVR8L, {0x1E, 0x90, 0x0C}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 16}, // atdf, avrdude, boot size (manual) - {"ATtiny104", 7, F_AVR8L, {0x1E, 0x90, 0x0B}, 0, 0x00400, 0x010, 0, 0, 0, 0, 0, 0x0040, 0x0020, 1, 1, 16}, // atdf, avrdude, boot size (manual) - - {"ATtiny11", 8, F_AVR8, {0x1E, 0x90, 0x04}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 1, 0x0060, 0x0020, 1, 1, 5}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny12", 9, F_AVR8, {0x1E, 0x90, 0x05}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 2, 0x0060, 0x0020, 1, 1, 6}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny13", 10, F_AVR8, {0x1E, 0x90, 0x07}, 0, 0x00400, 0x020, 0, 0, 0, 0x0040, 4, 0x0060, 0x0040, 2, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny13A", 11, F_AVR8, {0x1E, 0x90, 0x07}, 0, 0x00400, 0x020, 0, 0, 0, 0x0040, 4, 0x0060, 0x0040, 2, 1, 10}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny15", 12, F_AVR8, {0x1E, 0x90, 0x06}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 2, 0x0060, 0x0020, 1, 1, 9}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny22", 13, F_AVR8, {0x1E, 0x91, 0x06}, 0, 0x00800, -1, 0, 0, -1, -1, -1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, boot size (manual) - {"ATtiny24", 14, F_AVR8, {0x1E, 0x91, 0x0B}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny24A", 15, F_AVR8, {0x1E, 0x91, 0x0B}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny25", 16, F_AVR8, {0x1E, 0x91, 0x08}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny26", 17, F_AVR8, {0x1E, 0x91, 0x09}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 2, 1, 12}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny28", 18, F_AVR8, {0x1E, 0x91, 0x07}, 0, 0x00800, 0x002, 0, 0, 0, 0, 0, 0x0060, 0x0020, 1, 1, 6}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny43U", 19, F_AVR8, {0x1E, 0x92, 0x0C}, 0, 0x01000, 0x040, 0, 0, 0, 0x0040, 4, 0x0060, 0x0100, 3, 1, 16}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny44", 20, F_AVR8, {0x1E, 0x92, 0x07}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny44A", 21, F_AVR8, {0x1E, 0x92, 0x07}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny45", 22, F_AVR8, {0x1E, 0x92, 0x06}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny48", 23, F_AVR8, {0x1E, 0x92, 0x09}, 0, 0x01000, 0x040, 0, 0, 0, 0x0040, 4, 0x0100, 0x0100, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny84", 24, F_AVR8, {0x1E, 0x93, 0x0C}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny84A", 25, F_AVR8, {0x1E, 0x93, 0x0C}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny85", 26, F_AVR8, {0x1E, 0x93, 0x0B}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 15}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny87", 27, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny88", 28, F_AVR8, {0x1E, 0x93, 0x11}, 0, 0x02000, 0x040, 0, 0, 0, 0x0040, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny167", 29, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny261", 30, F_AVR8, {0x1E, 0x91, 0x0C}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny261A", 31, F_AVR8, {0x1E, 0x91, 0x0C}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny441", 32, F_AVR8, {0x1E, 0x92, 0x15}, 0, 0x01000, 0x010, 0, 0, 0, 0x0100, 4, 0x0100, 0x0100, 3, 1, 30}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny461", 33, F_AVR8, {0x1E, 0x92, 0x08}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny461A", 34, F_AVR8, {0x1E, 0x92, 0x08}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny828", 35, F_AVR8, {0x1E, 0x93, 0x14}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny828R", 36, F_AVR8, {0x1E, 0x93, 0x14}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // avrdude, from ATtiny828 - {"ATtiny841", 37, F_AVR8, {0x1E, 0x93, 0x15}, 0, 0x02000, 0x010, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 30}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny861", 38, F_AVR8, {0x1E, 0x93, 0x0D}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny861A", 39, F_AVR8, {0x1E, 0x93, 0x0D}, 0, 0x02000, 0x040, 0, 0, 0, 0x0200, 4, 0x0060, 0x0200, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1634", 40, F_AVR8, {0x1E, 0x94, 0x12}, 0, 0x04000, 0x020, 0, 0, 0, 0x0100, 4, 0x0100, 0x0400, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1634R", 41, F_AVR8, {0x1E, 0x94, 0x12}, 0, 0x04000, 0x020, 0, 0, 0, 0x0100, 4, 0x0100, 0x0400, 3, 1, 28}, // avrdude, from ATtiny1634 - {"ATtiny2313", 42, F_AVR8, {0x1E, 0x91, 0x0A}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny2313A", 43, F_AVR8, {0x1E, 0x91, 0x0A}, 0, 0x00800, 0x020, 0, 0, 0, 0x0080, 4, 0x0060, 0x0080, 3, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny4313", 44, F_AVR8, {0x1E, 0x92, 0x0D}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0060, 0x0100, 3, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8", 45, F_AVR8, {0x1E, 0x93, 0x07}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8A", 46, F_AVR8, {0x1E, 0x93, 0x07}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 19}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8HVA", 47, F_AVR8, {0x1E, 0x93, 0x10}, 0, 0x02000, 0x080, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 1, 1, 21}, // atdf, avr-gcc 12.2.0 - {"ATmega8U2", 48, F_AVR8, {0x1E, 0x93, 0x89}, 0, 0x02000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega16", 49, F_AVR8, {0x1E, 0x94, 0x03}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega16A", 50, F_AVR8, {0x1E, 0x94, 0x03}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0400, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega16HVA", 51, F_AVR8, {0x1E, 0x94, 0x0C}, 0, 0x04000, 0x080, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 1, 1, 21}, // atdf, avr-gcc 12.2.0 - {"ATmega16HVB", 52, F_AVR8, {0x1E, 0x94, 0x0D}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 2, 1, 29}, // atdf, avr-gcc 12.2.0 - {"ATmega16HVBrevB", 53, F_AVR8, {0x1E, 0x94, 0x0D}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 2, 1, 29}, // atdf, avr-gcc 12.2.0 - {"ATmega16M1", 54, F_AVR8, {0x1E, 0x94, 0x84}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0 - {"ATmega16HVA2", 55, F_AVR8, {0x1E, 0x94, 0x0E}, 0, 0x04000, 0x080, -1, -1, -1, -1, -1, 0x0100, 0x0400, 2, 1, 22}, // avr-gcc 12.2.0 - {"ATmega16U2", 56, F_AVR8, {0x1E, 0x94, 0x89}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega16U4", 57, F_AVR8, {0x1E, 0x94, 0x88}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0500, 3, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32", 58, F_AVR8, {0x1E, 0x95, 0x02}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0060, 0x0800, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32A", 59, F_AVR8, {0x1E, 0x95, 0x02}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0060, 0x0800, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32HVB", 60, F_AVR8, {0x1E, 0x95, 0x10}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 2, 1, 29}, // atdf, avr-gcc 12.2.0 - {"ATmega32HVBrevB", 61, F_AVR8, {0x1E, 0x95, 0x10}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 2, 1, 29}, // atdf, avr-gcc 12.2.0 - {"ATmega32C1", 62, F_AVR8, {0x1E, 0x95, 0x86}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0 - {"ATmega32M1", 63, F_AVR8, {0x1E, 0x95, 0x84}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32U2", 64, F_AVR8, {0x1E, 0x95, 0x8A}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0400, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32U4", 65, F_AVR8, {0x1E, 0x95, 0x87}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0a00, 3, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega32U6", 66, F_AVR8, {0x1E, 0x95, 0x88}, 0, 0x08000, 0x080, 4, 0x0200, -1, -1, -1, 0x0100, 0x0a00, 3, 1, 38}, // avr-gcc 12.2.0, boot size (manual) - {"ATmega48", 67, F_AVR8, {0x1E, 0x92, 0x05}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega48A", 68, F_AVR8, {0x1E, 0x92, 0x05}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega48P", 69, F_AVR8, {0x1E, 0x92, 0x0A}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega48PA", 70, F_AVR8, {0x1E, 0x92, 0x0A}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega48PB", 71, F_AVR8, {0x1E, 0x92, 0x10}, 0, 0x01000, 0x040, 0, 0, 0, 0x0100, 4, 0x0100, 0x0200, 3, 1, 27}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega64", 72, F_AVR8, {0x1E, 0x96, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega64A", 73, F_AVR8, {0x1E, 0x96, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega64HVE", 74, F_AVR8, {0x1E, 0x96, 0x10}, 0, 0x10000, 0x080, 4, 0x0400, -1, -1, -1, 0x0100, 0x1000, 2, 1, 25}, // avr-gcc 12.2.0, boot size (manual) - {"ATmega64C1", 75, F_AVR8, {0x1E, 0x96, 0x86}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0 - {"ATmega64M1", 76, F_AVR8, {0x1E, 0x96, 0x84}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega64HVE2", 77, F_AVR8, {0x1E, 0x96, 0x10}, 0, 0x10000, 0x080, 4, 0x0400, 0, 0x0400, 4, 0x0100, 0x1000, 2, 1, 25}, // atdf, avr-gcc 12.2.0 - {"ATmega64RFR2", 78, F_AVR8, {0x1E, 0xA6, 0x02}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0200, 0x2000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88", 79, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88A", 80, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88P", 81, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88PA", 82, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega88PB", 83, F_AVR8, {0x1E, 0x93, 0x16}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 27}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega103", 84, F_AVR8, {0x1E, 0x97, 0x01}, 0, 0x20000, 0x100, 0, 0, 0, 0x1000, 1, 0x0060, 0x0fa0, 1, 1, 24}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega128", 85, F_AVR8, {0x1E, 0x97, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega128A", 86, F_AVR8, {0x1E, 0x97, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega128RFA1", 87, F_AVR8, {0x1E, 0xA7, 0x01}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 72}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega128RFR2", 88, F_AVR8, {0x1E, 0xA7, 0x02}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega161", 89, F_AVR8, {0x1E, 0x94, 0x01}, 0, 0x04000, 0x080, 1, 0x0400, 0, 0x0200, 1, 0x0060, 0x0400, 1, 1, 21}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega162", 90, F_AVR8, {0x1E, 0x94, 0x04}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega163", 91, F_AVR8, {0x1E, 0x94, 0x02}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 1, 0x0060, 0x0400, 2, 1, 18}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega164A", 92, F_AVR8, {0x1E, 0x94, 0x0F}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega164P", 93, F_AVR8, {0x1E, 0x94, 0x0A}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega164PA", 94, F_AVR8, {0x1E, 0x94, 0x0A}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega165", 95, F_AVR8, {0x1E, 0x94, 0x10}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega165A", 96, F_AVR8, {0x1E, 0x94, 0x10}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega165P", 97, F_AVR8, {0x1E, 0x94, 0x07}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega165PA", 98, F_AVR8, {0x1E, 0x94, 0x07}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168", 99, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168A", 100, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168P", 101, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168PA", 102, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega168PB", 103, F_AVR8, {0x1E, 0x94, 0x15}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 27}, // atdf, avr-gcc 7.3.0, avrdude - {"ATmega169", 104, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"ATmega169A", 105, F_AVR8, {0x1E, 0x94, 0x11}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega169P", 106, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega169PA", 107, F_AVR8, {0x1E, 0x94, 0x05}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega256RFR2", 108, F_AVR8, {0x1E, 0xA8, 0x02}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x2000, 8, 0x0200, 0x8000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega323", 109, F_AVR8, {0x1E, 0x95, 0x01}, 0, 0x08000, 0x080, 4, 0x0200, -1, -1, -1, 0x0060, 0x0800, 2, 1, 21}, // avr-gcc 12.2.0, boot size (manual) - {"ATmega324A", 110, F_AVR8, {0x1E, 0x95, 0x15}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega324P", 111, F_AVR8, {0x1E, 0x95, 0x08}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega324PA", 112, F_AVR8, {0x1E, 0x95, 0x11}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega324PB", 113, F_AVR8, {0x1E, 0x95, 0x17}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 51}, // atdf, avrdude - {"ATmega325", 114, F_AVR8, {0x1E, 0x95, 0x05}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega325A", 115, F_AVR8, {0x1E, 0x95, 0x05}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega325P", 116, F_AVR8, {0x1E, 0x95, 0x0D}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega325PA", 117, F_AVR8, {0x1E, 0x95, 0x0D}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega328", 118, F_AVR8, {0x1E, 0x95, 0x14}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega328P", 119, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega328PB", 120, F_AVR8, {0x1E, 0x95, 0x16}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 45}, // atdf, avr-gcc 7.3.0, avrdude - {"ATmega329", 121, F_AVR8, {0x1E, 0x95, 0x03}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega329A", 122, F_AVR8, {0x1E, 0x95, 0x03}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega329P", 123, F_AVR8, {0x1E, 0x95, 0x0B}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega329PA", 124, F_AVR8, {0x1E, 0x95, 0x0B}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega406", 125, F_AVR8, {0x1E, 0x95, 0x07}, 0, 0x0a000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0800, 2, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega640", 126, F_AVR8, {0x1E, 0x96, 0x08}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644", 127, F_AVR8, {0x1E, 0x96, 0x09}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 28}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644A", 128, F_AVR8, {0x1E, 0x96, 0x09}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644P", 129, F_AVR8, {0x1E, 0x96, 0x0A}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644PA", 130, F_AVR8, {0x1E, 0x96, 0x0A}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega644RFR2", 131, F_AVR8, {0x1E, 0xA6, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0200, 0x2000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega645", 132, F_AVR8, {0x1E, 0x96, 0x05}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega645A", 133, F_AVR8, {0x1E, 0x96, 0x05}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega645P", 134, F_AVR8, {0x1E, 0x96, 0x0D}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 22}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega649", 135, F_AVR8, {0x1E, 0x96, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega649A", 136, F_AVR8, {0x1E, 0x96, 0x03}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega649P", 137, F_AVR8, {0x1E, 0x96, 0x0B}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 23}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1280", 138, F_AVR8, {0x1E, 0x97, 0x03}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1281", 139, F_AVR8, {0x1E, 0x97, 0x04}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1284", 140, F_AVR8, {0x1E, 0x97, 0x06}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x4000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1284P", 141, F_AVR8, {0x1E, 0x97, 0x05}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x4000, 3, 1, 35}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1284RFR2", 142, F_AVR8, {0x1E, 0xA7, 0x03}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x4000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega2560", 143, F_AVR8, {0x1E, 0x98, 0x01}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega2561", 144, F_AVR8, {0x1E, 0x98, 0x02}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0200, 0x2000, 3, 1, 57}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega2564RFR2", 145, F_AVR8, {0x1E, 0xA8, 0x03}, 0, 0x40000, 0x100, 4, 0x0400, 0, 0x2000, 8, 0x0200, 0x8000, 3, 1, 77}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3250", 146, F_AVR8, {0x1E, 0x95, 0x06}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3250A", 147, F_AVR8, {0x1E, 0x95, 0x06}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3250P", 148, F_AVR8, {0x1E, 0x95, 0x0E}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3250PA", 149, F_AVR8, {0x1E, 0x95, 0x0E}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3290", 150, F_AVR8, {0x1E, 0x95, 0x04}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3290A", 151, F_AVR8, {0x1E, 0x95, 0x04}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3290P", 152, F_AVR8, {0x1E, 0x95, 0x0C}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3290PA", 153, F_AVR8, {0x1E, 0x95, 0x0C}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6450", 154, F_AVR8, {0x1E, 0x96, 0x06}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6450A", 155, F_AVR8, {0x1E, 0x96, 0x06}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6450P", 156, F_AVR8, {0x1E, 0x96, 0x0E}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6490", 157, F_AVR8, {0x1E, 0x96, 0x04}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6490A", 158, F_AVR8, {0x1E, 0x96, 0x04}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega6490P", 159, F_AVR8, {0x1E, 0x96, 0x0C}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 25}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8515", 160, F_AVR8, {0x1E, 0x93, 0x06}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0200, 2, 1, 17}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega8535", 161, F_AVR8, {0x1E, 0x93, 0x08}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0060, 0x0200, 2, 1, 21}, // atdf, avr-gcc 12.2.0, avrdude - {"AT43USB320", 162, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0200, -1, -1, 0}, // avr-gcc 12.2.0 - {"AT43USB355", 163, F_AVR8, {0xff, -1, -1}, 0, 0x06000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0400, -1, -1, 0}, // avr-gcc 12.2.0 - {"AT76C711", 164, F_AVR8, {0xff, -1, -1}, 0, 0x04000, -1, -1, -1, -1, -1, -1, 0x0060, 0x07a0, -1, -1, 0}, // avr-gcc 12.2.0 - {"AT86RF401", 165, F_AVR8, {0x1E, 0x91, 0x81}, 0, 0x00800, -1, -1, -1, -1, -1, -1, 0x0060, 0x0080, 0, 1, 3}, // avr-gcc 12.2.0 - {"AT90PWM1", 166, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0 - {"AT90PWM2", 167, F_AVR8, {0x1E, 0x93, 0x81}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90PWM2B", 168, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM3", 169, F_AVR8, {0x1E, 0x93, 0x81}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM3B", 170, F_AVR8, {0x1E, 0x93, 0x83}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90CAN32", 171, F_AVR8, {0x1E, 0x95, 0x81}, 0, 0x08000, 0x100, 4, 0x0400, 0, 0x0400, 8, 0x0100, 0x0800, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90CAN64", 172, F_AVR8, {0x1E, 0x96, 0x81}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM81", 173, F_AVR8, {0x1E, 0x93, 0x88}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0100, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"AT90USB82", 174, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90SCR100", 175, F_AVR8, {0x1E, 0x96, 0xC1}, 0, 0x10000, 0x100, 4, 0x0200, -1, -1, -1, 0x0100, 0x1000, 3, 1, 38}, // avr-gcc 12.2.0, boot size (manual) - {"AT90CAN128", 176, F_AVR8, {0x1E, 0x97, 0x81}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x1000, 3, 1, 37}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM161", 177, F_AVR8, {0x1E, 0x94, 0x8B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"AT90USB162", 178, F_AVR8, {0x1E, 0x94, 0x82}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 29}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM216", 179, F_AVR8, {0x1E, 0x94, 0x83}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90PWM316", 180, F_AVR8, {0x1E, 0x94, 0x83}, 0, 0x04000, 0x080, 4, 0x0200, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 32}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90USB646", 181, F_AVR8, {0x1E, 0x96, 0x82}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90USB647", 182, F_AVR8, {0x1E, 0x96, 0x82}, 0, 0x10000, 0x100, 4, 0x0400, 0, 0x0800, 8, 0x0100, 0x1000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90S1200", 183, F_AVR8, {0x1E, 0x90, 0x01}, 0, 0x00400, 0x001, 0, 0, 0, 0x0040, 1, 0x0060, 0x0020, 1, 1, 4}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90USB1286", 184, F_AVR8, {0x1E, 0x97, 0x82}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x2000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90USB1287", 185, F_AVR8, {0x1E, 0x97, 0x82}, 0, 0x20000, 0x100, 4, 0x0400, 0, 0x1000, 8, 0x0100, 0x2000, 3, 1, 38}, // atdf, avr-gcc 12.2.0, avrdude - {"AT90S2313", 186, F_AVR8, {0x1E, 0x91, 0x01}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, 1, 1, 11}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S2323", 187, F_AVR8, {0x1E, 0x91, 0x02}, 0, 0x00800, -1, 0, 0, -1, -1, -1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, boot size (manual) - {"AT90S2333", 188, F_AVR8, {0x1E, 0x91, 0x05}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, -1, -1, 14}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S2343", 189, F_AVR8, {0x1E, 0x91, 0x03}, 0, 0x00800, 0x001, 0, 0, 0, 0x0080, 1, 0x0060, 0x0080, 1, 1, 3}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S4414", 190, F_AVR8, {0x1E, 0x92, 0x01}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0100, 1, 1, 13}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S4433", 191, F_AVR8, {0x1E, 0x92, 0x03}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0080, 1, 1, 14}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S4434", 192, F_AVR8, {0x1E, 0x92, 0x02}, 0, 0x01000, 0x001, 0, 0, 0, 0x0100, 1, 0x0060, 0x0100, 1, 1, 17}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90S8515", 193, F_AVR8, {0x1E, 0x93, 0x01}, 0, 0x02000, 0x001, 0, 0, 0, 0x0200, 1, 0x0060, 0x0200, 1, 1, 13}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT90C8534", 194, F_AVR8, {0xff, -1, -1}, 0, 0x02000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0100, -1, -1, 0}, // avr-gcc 12.2.0 - {"AT90S8535", 195, F_AVR8, {0x1E, 0x93, 0x03}, 0, 0x02000, 0x001, 0, 0, 0, 0x0200, 1, 0x0060, 0x0200, 1, 1, 17}, // avr-gcc 12.2.0, avrdude, boot size (manual) - {"AT94K", 196, F_AVR8, {0xff, -1, -1}, 0, 0x08000, -1, -1, -1, -1, -1, -1, 0x0060, 0x0fa0, -1, -1, 0}, // avr-gcc 12.2.0 - {"ATA5272", 197, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 37}, // atdf, avr-gcc 12.2.0 - {"ATA5505", 198, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"ATA5700M322", 199, F_AVR8, {0x1E, 0x95, 0x67}, 0x08000, 0x08000, 0x040, 0, 0, 0, 0x0880, 16, 0x0200, 0x0400, 1, 1, 51}, // atdf - {"ATA5702M322", 200, F_AVR8, {0x1E, 0x95, 0x69}, 0x08000, 0x08000, 0x040, 0, 0, 0, 0x0880, 16, 0x0200, 0x0400, 1, 1, 51}, // atdf, avr-gcc 12.2.0 - {"ATA5781", 201, F_AVR8, {0x1E, 0x95, 0x64}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA5782", 202, F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 12.2.0 - {"ATA5783", 203, F_AVR8, {0x1E, 0x95, 0x66}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA5787", 204, F_AVR8, {0x1E, 0x94, 0x6C}, 0x08000, 0x05200, 0x040, 0, 0, 0, 0x0400, 16, 0x0200, 0x0800, 1, 1, 44}, // atdf - {"ATA5790", 205, F_AVR8, {0x1E, 0x94, 0x61}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 30}, // atdf, avr-gcc 12.2.0 - {"ATA5790N", 206, F_AVR8, {0x1E, 0x94, 0x62}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 31}, // atdf, avr-gcc 12.2.0 - {"ATA5791", 207, F_AVR8, {0x1E, 0x94, 0x62}, 0, 0x04000, 0x080, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 31}, // atdf, avr-gcc 7.3.0 - {"ATA5795", 208, F_AVR8, {0x1E, 0x93, 0x61}, 0, 0x02000, 0x040, 1, 0x0800, 0, 0x0800, 16, 0x0100, 0x0200, 1, 1, 23}, // atdf, avr-gcc 12.2.0 - {"ATA5831", 209, F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 12.2.0 - {"ATA5832", 210, F_AVR8, {0x1E, 0x95, 0x62}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA5833", 211, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA5835", 212, F_AVR8, {0x1E, 0x94, 0x6B}, 0x08000, 0x05200, 0x040, 0, 0, 0, 0x0400, 16, 0x0200, 0x0800, 1, 1, 44}, // atdf - {"ATA6285", 213, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0140, 4, 0x0100, 0x0200, 2, 1, 27}, // atdf, avr-gcc 12.2.0 - {"ATA6286", 214, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0140, 4, 0x0100, 0x0200, 2, 1, 27}, // atdf, avr-gcc 12.2.0 - {"ATA6289", 215, F_AVR8, {0x1E, 0x93, 0x82}, 0, 0x02000, 0x040, 4, 0x0100, -1, -1, -1, 0x0100, 0x0200, 2, 1, 27}, // avr-gcc 12.2.0, boot size (manual) - {"ATA6612C", 216, F_AVR8, {0x1E, 0x93, 0x0A}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0 - {"ATA6613C", 217, F_AVR8, {0x1E, 0x94, 0x06}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // atdf, avr-gcc 12.2.0 - {"ATA6614Q", 218, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // atdf, avr-gcc 12.2.0 - {"ATA6616C", 219, F_AVR8, {0x1E, 0x93, 0x87}, 0, 0x02000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"ATA6617C", 220, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"ATA8210", 221, F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 7.3.0 - {"ATA8215", 222, F_AVR8, {0x1E, 0x95, 0x64}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA8510", 223, F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040, 1, 0x5000, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf, avr-gcc 7.3.0 - {"ATA8515", 224, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42}, // atdf - {"ATA664251", 225, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20}, // atdf, avr-gcc 12.2.0 - {"M3000", 226, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x1000, 0x1000, -1, -1, 0}, // avr-gcc 12.2.0 - {"LGT8F88P", 227, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // avrdude, from ATmega88 - {"LGT8F168P", 228, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26}, // avrdude, from ATmega168P - {"LGT8F328P", 229, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26}, // avrdude, from ATmega328P - - {"ATxmega8E5", 230, F_XMEGA, {0x1E, 0x93, 0x41}, 0, 0x02800, 0x080, 1, 0x0800, 0, 0x0200, 32, 0x2000, 0x0400, 7, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16A4", 231, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 94}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16A4U", 232, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16C4", 233, F_XMEGA, {0x1E, 0x94, 0x43}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16D4", 234, F_XMEGA, {0x1E, 0x94, 0x42}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega16E5", 235, F_XMEGA, {0x1E, 0x94, 0x45}, 0, 0x05000, 0x080, 1, 0x1000, 0, 0x0200, 32, 0x2000, 0x0800, 7, 1, 43}, // atdf, avr-gcc 7.3.0, avrdude - {"ATxmega32C3", 236, F_XMEGA, {0x1E, 0x95, 0x49}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0 - {"ATxmega32D3", 237, F_XMEGA, {0x1E, 0x95, 0x4A}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 114}, // atdf, avr-gcc 12.2.0 - {"ATxmega32A4", 238, F_XMEGA, {0x1E, 0x95, 0x41}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 94}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega32A4U", 239, F_XMEGA, {0x1E, 0x95, 0x41}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega32C4", 240, F_XMEGA, {0x1E, 0x95, 0x44}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega32D4", 241, F_XMEGA, {0x1E, 0x95, 0x42}, 0, 0x09000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega32E5", 242, F_XMEGA, {0x1E, 0x95, 0x4C}, 0, 0x09000, 0x080, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x1000, 7, 1, 43}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A1", 243, F_XMEGA, {0x1E, 0x96, 0x4E}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 125}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A1U", 244, F_XMEGA, {0x1E, 0x96, 0x4E}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64B1", 245, F_XMEGA, {0x1E, 0x96, 0x52}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 81}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A3", 246, F_XMEGA, {0x1E, 0x96, 0x42}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A3U", 247, F_XMEGA, {0x1E, 0x96, 0x42}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64B3", 248, F_XMEGA, {0x1E, 0x96, 0x51}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 54}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64C3", 249, F_XMEGA, {0x1E, 0x96, 0x49}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64D3", 250, F_XMEGA, {0x1E, 0x96, 0x4A}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64A4", 251, F_XMEGA, {0x1E, 0x96, 0x46}, 0, 0x11000, 0x100, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega64A4U", 252, F_XMEGA, {0x1E, 0x96, 0x46}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega64D4", 253, F_XMEGA, {0x1E, 0x96, 0x47}, 0, 0x11000, 0x100, 1, 0x1000, 0, 0x0800, 32, 0x2000, 0x1000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A1", 254, F_XMEGA, {0x1E, 0x97, 0x4C}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 125}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A1revD", 255, F_XMEGA, {0x1E, 0x97, 0x41}, 0, 0x22000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega128A1U", 256, F_XMEGA, {0x1E, 0x97, 0x4C}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128B1", 257, F_XMEGA, {0x1E, 0x97, 0x4D}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 81}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A3", 258, F_XMEGA, {0x1E, 0x97, 0x42}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A3U", 259, F_XMEGA, {0x1E, 0x97, 0x42}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128B3", 260, F_XMEGA, {0x1E, 0x97, 0x4B}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 54}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128C3", 261, F_XMEGA, {0x1E, 0x97, 0x52}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128D3", 262, F_XMEGA, {0x1E, 0x97, 0x48}, 0, 0x22000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128A4", 263, F_XMEGA, {0x1E, 0x97, 0x46}, 0, 0x22000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega128A4U", 264, F_XMEGA, {0x1E, 0x97, 0x46}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega128D4", 265, F_XMEGA, {0x1E, 0x97, 0x47}, 0, 0x22000, 0x100, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x2000, 6, 1, 91}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega192A1", 266, F_XMEGA, {0x1E, 0x97, 0x4E}, 0, 0x32000, 0x200, -1, -1, 0, 0x0800, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega192A3", 267, F_XMEGA, {0x1E, 0x97, 0x44}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega192A3U", 268, F_XMEGA, {0x1E, 0x97, 0x44}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega192C3", 269, F_XMEGA, {0x1E, 0x97, 0x51}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega192D3", 270, F_XMEGA, {0x1E, 0x97, 0x49}, 0, 0x32000, 0x200, 1, 0x2000, 0, 0x0800, 32, 0x2000, 0x4000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256A1", 271, F_XMEGA, {0x1E, 0x98, 0x46}, 0, 0x42000, 0x200, -1, -1, 0, 0x1000, 32, -1, -1, -1, -1, 0}, // avrdude - {"ATxmega256A3", 272, F_XMEGA, {0x1E, 0x98, 0x42}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256A3B", 273, F_XMEGA, {0x1E, 0x98, 0x43}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 122}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256A3BU", 274, F_XMEGA, {0x1E, 0x98, 0x43}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256A3U", 275, F_XMEGA, {0x1E, 0x98, 0x42}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256C3", 276, F_XMEGA, {0x1E, 0x98, 0x46}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega256D3", 277, F_XMEGA, {0x1E, 0x98, 0x44}, 0, 0x42000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x4000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega384C3", 278, F_XMEGA, {0x1E, 0x98, 0x45}, 0, 0x62000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x8000, 6, 1, 127}, // atdf, avr-gcc 12.2.0, avrdude - {"ATxmega384D3", 279, F_XMEGA, {0x1E, 0x98, 0x47}, 0, 0x62000, 0x200, 1, 0x2000, 0, 0x1000, 32, 0x2000, 0x8000, 6, 1, 114}, // atdf, avr-gcc 12.2.0, avrdude - - {"ATtiny202", 280, F_AVR8X, {0x1E, 0x91, 0x23}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny204", 281, F_AVR8X, {0x1E, 0x91, 0x22}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny212", 282, F_AVR8X, {0x1E, 0x91, 0x21}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny214", 283, F_AVR8X, {0x1E, 0x91, 0x20}, 0, 0x00800, 0x040, 1, 0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny402", 284, F_AVR8X, {0x1E, 0x92, 0x27}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny404", 285, F_AVR8X, {0x1E, 0x92, 0x26}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny406", 286, F_AVR8X, {0x1E, 0x92, 0x25}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny412", 287, F_AVR8X, {0x1E, 0x92, 0x23}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny414", 288, F_AVR8X, {0x1E, 0x92, 0x22}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny416", 289, F_AVR8X, {0x1E, 0x92, 0x21}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny416auto", 290, F_AVR8X, {0x1E, 0x92, 0x28}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf - {"ATtiny417", 291, F_AVR8X, {0x1E, 0x92, 0x20}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny424", 292, F_AVR8X, {0x1E, 0x92, 0x2C}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude - {"ATtiny426", 293, F_AVR8X, {0x1E, 0x92, 0x2B}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude - {"ATtiny427", 294, F_AVR8X, {0x1E, 0x92, 0x2A}, 0, 0x01000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 30}, // atdf, avrdude - {"ATtiny804", 295, F_AVR8X, {0x1E, 0x93, 0x25}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny806", 296, F_AVR8X, {0x1E, 0x93, 0x24}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny807", 297, F_AVR8X, {0x1E, 0x93, 0x23}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny814", 298, F_AVR8X, {0x1E, 0x93, 0x22}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny816", 299, F_AVR8X, {0x1E, 0x93, 0x21}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny817", 300, F_AVR8X, {0x1E, 0x93, 0x20}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10, 1, 26}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny824", 301, F_AVR8X, {0x1E, 0x93, 0x29}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude - {"ATtiny826", 302, F_AVR8X, {0x1E, 0x93, 0x28}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude - {"ATtiny827", 303, F_AVR8X, {0x1E, 0x93, 0x27}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10, 1, 30}, // atdf, avrdude - {"ATtiny1604", 304, F_AVR8X, {0x1E, 0x94, 0x25}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1606", 305, F_AVR8X, {0x1E, 0x94, 0x24}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1607", 306, F_AVR8X, {0x1E, 0x94, 0x23}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1614", 307, F_AVR8X, {0x1E, 0x94, 0x22}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1616", 308, F_AVR8X, {0x1E, 0x94, 0x21}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1617", 309, F_AVR8X, {0x1E, 0x94, 0x20}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny1624", 310, F_AVR8X, {0x1E, 0x94, 0x2A}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude - {"ATtiny1626", 311, F_AVR8X, {0x1E, 0x94, 0x29}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude - {"ATtiny1627", 312, F_AVR8X, {0x1E, 0x94, 0x28}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 30}, // atdf, avrdude - {"ATtiny3214", 313, F_AVR8X, {0x1E, 0x95, 0x20}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // avr-gcc 12.2.0 - {"ATtiny3216", 314, F_AVR8X, {0x1E, 0x95, 0x21}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny3217", 315, F_AVR8X, {0x1E, 0x95, 0x22}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10, 1, 31}, // atdf, avr-gcc 12.2.0, avrdude - {"ATtiny3224", 316, F_AVR8X, {0x1E, 0x95, 0x28}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude - {"ATtiny3226", 317, F_AVR8X, {0x1E, 0x95, 0x27}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude - {"ATtiny3227", 318, F_AVR8X, {0x1E, 0x95, 0x26}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10, 1, 30}, // atdf, avrdude - {"ATmega808", 319, F_AVR8X, {0x1E, 0x93, 0x26}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega809", 320, F_AVR8X, {0x1E, 0x93, 0x2A}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1608", 321, F_AVR8X, {0x1E, 0x94, 0x27}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega1609", 322, F_AVR8X, {0x1E, 0x94, 0x26}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3208", 323, F_AVR8X, {0x1E, 0x95, 0x30}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega3209", 324, F_AVR8X, {0x1E, 0x95, 0x31}, 0, 0x08000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega4808", 325, F_AVR8X, {0x1E, 0x96, 0x50}, 0, 0x0c000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10, 1, 36}, // atdf, avr-gcc 12.2.0, avrdude - {"ATmega4809", 326, F_AVR8X, {0x1E, 0x96, 0x51}, 0, 0x0c000, 0x080, 1, 0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10, 1, 40}, // atdf, avr-gcc 12.2.0, avrdude - {"AVR8EA28", 327, F_AVR8X, {0x1E, 0x93, 0x2C}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR8EA32", 328, F_AVR8X, {0x1E, 0x93, 0x2B}, 0, 0x02000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR16DD14", 329, F_AVR8X, {0x1E, 0x94, 0x34}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude - {"AVR16DD20", 330, F_AVR8X, {0x1E, 0x94, 0x33}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude - {"AVR16DD28", 331, F_AVR8X, {0x1E, 0x94, 0x32}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude - {"AVR16EA28", 332, F_AVR8X, {0x1E, 0x94, 0x37}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR16DD32", 333, F_AVR8X, {0x1E, 0x94, 0x31}, 0, 0x04000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7800, 0x0800, 16, 4, 36}, // atdf, avrdude - {"AVR16EA32", 334, F_AVR8X, {0x1E, 0x94, 0x36}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR16EA48", 335, F_AVR8X, {0x1E, 0x94, 0x35}, 0, 0x04000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR32DD14", 336, F_AVR8X, {0x1E, 0x95, 0x3B}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude - {"AVR32DD20", 337, F_AVR8X, {0x1E, 0x95, 0x3A}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude - {"AVR32DA28", 338, F_AVR8X, {0x1E, 0x95, 0x34}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 41}, // atdf, avrdude - {"AVR32DB28", 339, F_AVR8X, {0x1E, 0x95, 0x37}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 42}, // atdf, avrdude - {"AVR32DD28", 340, F_AVR8X, {0x1E, 0x95, 0x39}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude - {"AVR32EA28", 341, F_AVR8X, {0x1E, 0x95, 0x3E}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR32DA32", 342, F_AVR8X, {0x1E, 0x95, 0x33}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 44}, // atdf, avrdude - {"AVR32DB32", 343, F_AVR8X, {0x1E, 0x95, 0x36}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 44}, // atdf, avrdude - {"AVR32DD32", 344, F_AVR8X, {0x1E, 0x95, 0x38}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x7000, 0x1000, 16, 4, 36}, // atdf, avrdude - {"AVR32EA32", 345, F_AVR8X, {0x1E, 0x95, 0x3D}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR32DA48", 346, F_AVR8X, {0x1E, 0x95, 0x32}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 58}, // atdf, avrdude - {"AVR32DB48", 347, F_AVR8X, {0x1E, 0x95, 0x35}, 0, 0x08000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x7000, 0x1000, 16, 4, 61}, // atdf, avrdude - {"AVR32EA48", 348, F_AVR8X, {0x1E, 0x95, 0x3C}, 0, 0x08000, 0x040, 1, 0, 0x01400, 0x0200, 8, -1, -1, -1, -1, 0}, // avrdude - {"AVR64DD14", 349, F_AVR8X, {0x1E, 0x96, 0x1D}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude - {"AVR64DD20", 350, F_AVR8X, {0x1E, 0x96, 0x1C}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude - {"AVR64DA28", 351, F_AVR8X, {0x1E, 0x96, 0x15}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 41}, // atdf, avrdude - {"AVR64DB28", 352, F_AVR8X, {0x1E, 0x96, 0x19}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 42}, // atdf, avrdude - {"AVR64DD28", 353, F_AVR8X, {0x1E, 0x96, 0x1B}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude - {"AVR64EA28", 354, F_AVR8X, {0x1E, 0x96, 0x20}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 37}, // atdf, avrdude - {"AVR64DA32", 355, F_AVR8X, {0x1E, 0x96, 0x14}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 44}, // atdf, avrdude - {"AVR64DB32", 356, F_AVR8X, {0x1E, 0x96, 0x18}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 44}, // atdf, avrdude - {"AVR64DD32", 357, F_AVR8X, {0x1E, 0x96, 0x1A}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0100, 1, 0x6000, 0x2000, 16, 4, 36}, // atdf, avrdude - {"AVR64EA32", 358, F_AVR8X, {0x1E, 0x96, 0x1F}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 37}, // atdf, avrdude - {"AVR64DA48", 359, F_AVR8X, {0x1E, 0x96, 0x13}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 58}, // atdf, avrdude - {"AVR64DB48", 360, F_AVR8X, {0x1E, 0x96, 0x17}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 61}, // atdf, avrdude - {"AVR64EA48", 361, F_AVR8X, {0x1E, 0x96, 0x1E}, 0, 0x10000, 0x080, 1, 0, 0x01400, 0x0200, 8, 0x6800, 0x1800, 16, 4, 45}, // atdf, avrdude - {"AVR64DA64", 362, F_AVR8X, {0x1E, 0x96, 0x12}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 64}, // atdf, avrdude - {"AVR64DB64", 363, F_AVR8X, {0x1E, 0x96, 0x16}, 0, 0x10000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x6000, 0x2000, 16, 4, 65}, // atdf, avrdude - {"AVR128DA28", 364, F_AVR8X, {0x1E, 0x97, 0x0A}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 41}, // atdf, avrdude - {"AVR128DB28", 365, F_AVR8X, {0x1E, 0x97, 0x0E}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 42}, // atdf, avrdude - {"AVR128DA32", 366, F_AVR8X, {0x1E, 0x97, 0x09}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 44}, // atdf, avrdude - {"AVR128DB32", 367, F_AVR8X, {0x1E, 0x97, 0x0D}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 44}, // atdf, avrdude - {"AVR128DA48", 368, F_AVR8X, {0x1E, 0x97, 0x08}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 58}, // atdf, avrdude - {"AVR128DB48", 369, F_AVR8X, {0x1E, 0x97, 0x0C}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 61}, // atdf, avrdude - {"AVR128DA64", 370, F_AVR8X, {0x1E, 0x97, 0x07}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 64}, // atdf, avrdude - {"AVR128DB64", 371, F_AVR8X, {0x1E, 0x97, 0x0B}, 0, 0x20000, 0x200, 1, 0, 0x01400, 0x0200, 1, 0x4000, 0x4000, 16, 4, 65}, // atdf, avrdude -}; - -const size_t avr_isp_chip_arr_size = COUNT_OF(avr_isp_chip_arr); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h deleted file mode 100644 index 66f16a7b9..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -#define F_AVR8L 1 // TPI programming, ATtiny(4|5|9|10|20|40|102|104) -#define F_AVR8 2 // ISP programming with SPI, "classic" AVRs -#define F_XMEGA 4 // PDI programming, ATxmega family -#define F_AVR8X 8 // UPDI programming, newer 8-bit MCUs - -struct AvrIspChipArr { // Value of -1 typically means unknown - const char* name; // Name of part - uint16_t mcuid; // ID of MCU in 0..2039 - uint8_t avrarch; // F_AVR8L, F_AVR8, F_XMEGA or F_AVR8X - uint8_t sigs[3]; // Signature bytes - int32_t flashoffset; // Flash offset - int32_t flashsize; // Flash size - int16_t pagesize; // Flash page size - int8_t nboots; // Number of supported boot sectors - int16_t bootsize; // Size of (smallest) boot sector - int32_t eepromoffset; // EEPROM offset - int32_t eepromsize; // EEPROM size - int32_t eeprompagesize; // EEPROM page size - int32_t sramstart; // SRAM offset - int32_t sramsize; // SRAM size - int8_t nfuses; // Number of fuse bytes - int8_t nlocks; // Number of lock bytes - uint8_t ninterrupts; // Number of vectors in interrupt vector table -}; - -typedef struct AvrIspChipArr AvrIspChipArr; - -extern const AvrIspChipArr avr_isp_chip_arr[]; -extern const size_t avr_isp_chip_arr_size; \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c deleted file mode 100644 index 0f46872dd..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c +++ /dev/null @@ -1,639 +0,0 @@ -#include "avr_isp_prog.h" -#include "avr_isp_prog_cmd.h" - -#include - -#define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 -#define TAG "AvrIspProg" - -struct AvrIspProgSignature { - uint8_t vendor; - uint8_t part_family; - uint8_t part_number; -}; - -typedef struct AvrIspProgSignature AvrIspProgSignature; - -struct AvrIspProgCfgDevice { - uint8_t devicecode; - uint8_t revision; - uint8_t progtype; - uint8_t parmode; - uint8_t polling; - uint8_t selftimed; - uint8_t lockbytes; - uint8_t fusebytes; - uint8_t flashpoll; - uint16_t eeprompoll; - uint16_t pagesize; - uint16_t eepromsize; - uint32_t flashsize; -}; - -typedef struct AvrIspProgCfgDevice AvrIspProgCfgDevice; - -struct AvrIspProg { - AvrIspSpiSw* spi; - AvrIspProgCfgDevice* cfg; - FuriStreamBuffer* stream_rx; - FuriStreamBuffer* stream_tx; - - uint16_t error; - uint16_t addr; - bool pmode; - bool exit; - bool rst_active_high; - uint8_t buff[AVR_ISP_PROG_TX_RX_BUF_SIZE]; - - AvrIspProgCallback callback; - void* context; -}; - -static void avr_isp_prog_end_pmode(AvrIspProg* instance); - -AvrIspProg* avr_isp_prog_init(void) { - AvrIspProg* instance = malloc(sizeof(AvrIspProg)); - instance->cfg = malloc(sizeof(AvrIspProgCfgDevice)); - instance->stream_rx = - furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); - instance->stream_tx = - furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); - instance->rst_active_high = false; - instance->exit = false; - return instance; -} - -void avr_isp_prog_free(AvrIspProg* instance) { - furi_assert(instance); - if(instance->spi) avr_isp_prog_end_pmode(instance); - furi_stream_buffer_free(instance->stream_tx); - furi_stream_buffer_free(instance->stream_rx); - free(instance->cfg); - free(instance); -} - -size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) { - return furi_stream_buffer_spaces_available(instance->stream_rx); -} - -bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len) { - furi_assert(instance); - furi_assert(data); - furi_assert(len != 0); - size_t ret = furi_stream_buffer_send(instance->stream_rx, data, sizeof(uint8_t) * len, 0); - return ret == sizeof(uint8_t) * len; -} - -size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len) { - furi_assert(instance); - return furi_stream_buffer_receive(instance->stream_tx, data, sizeof(int8_t) * max_len, 0); -} - -void avr_isp_prog_exit(AvrIspProg* instance) { - furi_assert(instance); - instance->exit = true; -} - -void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context) { - furi_assert(instance); - furi_assert(context); - instance->callback = callback; - instance->context = context; -} - -static void avr_isp_prog_tx_ch(AvrIspProg* instance, uint8_t data) { - furi_assert(instance); - furi_stream_buffer_send(instance->stream_tx, &data, sizeof(uint8_t), FuriWaitForever); -} - -static uint8_t avr_isp_prog_getch(AvrIspProg* instance) { - furi_assert(instance); - uint8_t data[1] = {0}; - while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) { - if(instance->exit) break; - }; - return data[0]; -} - -static void avr_isp_prog_fill(AvrIspProg* instance, size_t len) { - furi_assert(instance); - for(size_t x = 0; x < len; x++) { - instance->buff[x] = avr_isp_prog_getch(instance); - } -} - -static void avr_isp_prog_reset_target(AvrIspProg* instance, bool reset) { - furi_assert(instance); - avr_isp_spi_sw_res_set(instance->spi, (reset == instance->rst_active_high) ? true : false); -} - -static uint8_t avr_isp_prog_spi_transaction( - AvrIspProg* instance, - uint8_t cmd, - uint8_t addr_hi, - uint8_t addr_lo, - uint8_t data) { - furi_assert(instance); - - avr_isp_spi_sw_txrx(instance->spi, cmd); - avr_isp_spi_sw_txrx(instance->spi, addr_hi); - avr_isp_spi_sw_txrx(instance->spi, addr_lo); - return avr_isp_spi_sw_txrx(instance->spi, data); -} - -static void avr_isp_prog_empty_reply(AvrIspProg* instance) { - furi_assert(instance); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - avr_isp_prog_tx_ch(instance, STK_OK); - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } -} - -static void avr_isp_prog_breply(AvrIspProg* instance, uint8_t data) { - furi_assert(instance); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - avr_isp_prog_tx_ch(instance, data); - avr_isp_prog_tx_ch(instance, STK_OK); - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } -} - -static void avr_isp_prog_get_version(AvrIspProg* instance, uint8_t data) { - furi_assert(instance); - switch(data) { - case STK_HW_VER: - avr_isp_prog_breply(instance, AVR_ISP_HWVER); - break; - case STK_SW_MAJOR: - avr_isp_prog_breply(instance, AVR_ISP_SWMAJ); - break; - case STK_SW_MINOR: - avr_isp_prog_breply(instance, AVR_ISP_SWMIN); - break; - case AVP_ISP_CONNECT_TYPE: - avr_isp_prog_breply(instance, AVP_ISP_SERIAL_CONNECT_TYPE); - break; - default: - avr_isp_prog_breply(instance, AVR_ISP_RESP_0); - } -} - -static void avr_isp_prog_set_cfg(AvrIspProg* instance) { - furi_assert(instance); - // call this after reading cfg packet into buff[] - instance->cfg->devicecode = instance->buff[0]; - instance->cfg->revision = instance->buff[1]; - instance->cfg->progtype = instance->buff[2]; - instance->cfg->parmode = instance->buff[3]; - instance->cfg->polling = instance->buff[4]; - instance->cfg->selftimed = instance->buff[5]; - instance->cfg->lockbytes = instance->buff[6]; - instance->cfg->fusebytes = instance->buff[7]; - instance->cfg->flashpoll = instance->buff[8]; - // ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as �flashpoll� - instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11]; - instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13]; - instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15]; - instance->cfg->flashsize = instance->buff[16] << 24 | instance->buff[17] << 16 | - instance->buff[18] << 8 | instance->buff[19]; - - // avr devices have active low reset, at89sx are active high - instance->rst_active_high = (instance->cfg->devicecode >= 0xe0); -} -static bool - avr_isp_prog_set_pmode(AvrIspProg* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { - furi_assert(instance); - uint8_t res = 0; - avr_isp_spi_sw_txrx(instance->spi, a); - avr_isp_spi_sw_txrx(instance->spi, b); - res = avr_isp_spi_sw_txrx(instance->spi, c); - avr_isp_spi_sw_txrx(instance->spi, d); - return res == 0x53; -} - -static void avr_isp_prog_end_pmode(AvrIspProg* instance) { - furi_assert(instance); - if(instance->pmode) { - avr_isp_prog_reset_target(instance, false); - // We're about to take the target out of reset - // so configure SPI pins as input - - if(instance->spi) avr_isp_spi_sw_free(instance->spi); - instance->spi = NULL; - } - - instance->pmode = false; -} - -static bool avr_isp_prog_start_pmode(AvrIspProg* instance, AvrIspSpiSwSpeed spi_speed) { - furi_assert(instance); - // Reset target before driving PIN_SCK or PIN_MOSI - - // SPI.begin() will configure SS as output, - // so SPI master mode is selected. - // We have defined RESET as pin 10, - // which for many arduino's is not the SS pin. - // So we have to configure RESET as output here, - // (reset_target() first sets the correct level) - if(instance->spi) avr_isp_spi_sw_free(instance->spi); - instance->spi = avr_isp_spi_sw_init(spi_speed); - - avr_isp_prog_reset_target(instance, true); - // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm": - - // Pulse RESET after PIN_SCK is low: - avr_isp_spi_sw_sck_set(instance->spi, false); - - // discharge PIN_SCK, value arbitrally chosen - furi_delay_ms(20); - avr_isp_prog_reset_target(instance, false); - - // Pulse must be minimum 2 target CPU speed cycles - // so 100 usec is ok for CPU speeds above 20KHz - furi_delay_ms(1); - - avr_isp_prog_reset_target(instance, true); - - // Send the enable programming command: - // datasheet: must be > 20 msec - furi_delay_ms(50); - if(avr_isp_prog_set_pmode(instance, AVR_ISP_SET_PMODE)) { - instance->pmode = true; - return true; - } - return false; -} - -static AvrIspProgSignature avr_isp_prog_check_signature(AvrIspProg* instance) { - furi_assert(instance); - AvrIspProgSignature signature; - signature.vendor = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR); - signature.part_family = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); - signature.part_number = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); - return signature; -} - -static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { - AvrIspSpiSwSpeed spi_speed[] = { - AvrIspSpiSwSpeed1Mhz, - AvrIspSpiSwSpeed400Khz, - AvrIspSpiSwSpeed250Khz, - AvrIspSpiSwSpeed125Khz, - AvrIspSpiSwSpeed60Khz, - AvrIspSpiSwSpeed40Khz, - AvrIspSpiSwSpeed20Khz, - AvrIspSpiSwSpeed10Khz, - AvrIspSpiSwSpeed5Khz, - AvrIspSpiSwSpeed1Khz, - }; - for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { - if(avr_isp_prog_start_pmode(instance, spi_speed[i])) { - AvrIspProgSignature sig = avr_isp_prog_check_signature(instance); - AvrIspProgSignature sig_examination = avr_isp_prog_check_signature(instance); //-V656 - uint8_t y = 0; - while(y < 8) { - if(memcmp( - (uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspProgSignature)) != - 0) - break; - sig_examination = avr_isp_prog_check_signature(instance); - y++; - } - if(y == 8) { - if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { - if(i < (COUNT_OF(spi_speed) - 1)) { - avr_isp_prog_end_pmode(instance); - i++; - return avr_isp_prog_start_pmode(instance, spi_speed[i]); - } - } - return true; - } - } - } - - if(instance->spi) { - avr_isp_spi_sw_free(instance->spi); - instance->spi = NULL; - } - - return false; -} - -static void avr_isp_prog_universal(AvrIspProg* instance) { - furi_assert(instance); - uint8_t data; - - avr_isp_prog_fill(instance, 4); - data = avr_isp_prog_spi_transaction( - instance, instance->buff[0], instance->buff[1], instance->buff[2], instance->buff[3]); - avr_isp_prog_breply(instance, data); -} - -static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t data) { - furi_assert(instance); - avr_isp_prog_spi_transaction(instance, AVR_ISP_COMMIT(addr)); - /* polling flash */ - if(data == 0xFF) { - furi_delay_ms(5); - } else { - /* polling flash */ - uint32_t starttime = furi_get_tick(); - while((furi_get_tick() - starttime) < 30) { - if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { - break; - }; - } - } -} - -static uint16_t avr_isp_prog_current_page(AvrIspProg* instance) { - furi_assert(instance); - uint16_t page = 0; - switch(instance->cfg->pagesize) { - case 32: - page = instance->addr & 0xFFFFFFF0; - break; - case 64: - page = instance->addr & 0xFFFFFFE0; - break; - case 128: - page = instance->addr & 0xFFFFFFC0; - break; - case 256: - page = instance->addr & 0xFFFFFF80; - break; - - default: - page = instance->addr; - break; - } - - return page; -} - -static uint8_t avr_isp_prog_write_flash_pages(AvrIspProg* instance, size_t length) { - furi_assert(instance); - size_t x = 0; - uint16_t page = avr_isp_prog_current_page(instance); - while(x < length) { - if(page != avr_isp_prog_current_page(instance)) { - --x; - avr_isp_prog_commit(instance, page, instance->buff[x++]); - page = avr_isp_prog_current_page(instance); - } - avr_isp_prog_spi_transaction( - instance, AVR_ISP_WRITE_FLASH_LO(instance->addr, instance->buff[x++])); - - avr_isp_prog_spi_transaction( - instance, AVR_ISP_WRITE_FLASH_HI(instance->addr, instance->buff[x++])); - instance->addr++; - } - - avr_isp_prog_commit(instance, page, instance->buff[--x]); - return STK_OK; -} - -static void avr_isp_prog_write_flash(AvrIspProg* instance, size_t length) { - furi_assert(instance); - avr_isp_prog_fill(instance, length); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - avr_isp_prog_tx_ch(instance, avr_isp_prog_write_flash_pages(instance, length)); - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } -} - -// write (length) bytes, (start) is a byte address -static uint8_t - avr_isp_prog_write_eeprom_chunk(AvrIspProg* instance, uint16_t start, uint16_t length) { - furi_assert(instance); - // this writes byte-by-byte, - // page writing may be faster (4 bytes at a time) - avr_isp_prog_fill(instance, length); - for(uint16_t x = 0; x < length; x++) { - uint16_t addr = start + x; - avr_isp_prog_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, instance->buff[x])); - furi_delay_ms(10); - } - return STK_OK; -} - -static uint8_t avr_isp_prog_write_eeprom(AvrIspProg* instance, size_t length) { - furi_assert(instance); - // here is a word address, get the byte address - uint16_t start = instance->addr * 2; - uint16_t remaining = length; - if(length > instance->cfg->eepromsize) { - instance->error++; - return STK_FAILED; - } - while(remaining > AVR_ISP_EECHUNK) { - avr_isp_prog_write_eeprom_chunk(instance, start, AVR_ISP_EECHUNK); - start += AVR_ISP_EECHUNK; - remaining -= AVR_ISP_EECHUNK; - } - avr_isp_prog_write_eeprom_chunk(instance, start, remaining); - return STK_OK; -} - -static void avr_isp_prog_program_page(AvrIspProg* instance) { - furi_assert(instance); - uint8_t result = STK_FAILED; - uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); - uint8_t memtype = avr_isp_prog_getch(instance); - // flash memory @addr, (length) bytes - if(memtype == STK_SET_FLASH_TYPE) { - avr_isp_prog_write_flash(instance, length); - return; - } - if(memtype == STK_SET_EEPROM_TYPE) { - result = avr_isp_prog_write_eeprom(instance, length); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - avr_isp_prog_tx_ch(instance, result); - - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } - return; - } - avr_isp_prog_tx_ch(instance, STK_FAILED); - return; -} - -static uint8_t avr_isp_prog_flash_read_page(AvrIspProg* instance, uint16_t length) { - furi_assert(instance); - for(uint16_t x = 0; x < length; x += 2) { - avr_isp_prog_tx_ch( - instance, - avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(instance->addr))); - avr_isp_prog_tx_ch( - instance, - avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(instance->addr))); - instance->addr++; - } - return STK_OK; -} - -static uint8_t avr_isp_prog_eeprom_read_page(AvrIspProg* instance, uint16_t length) { - furi_assert(instance); - // here again we have a word address - uint16_t start = instance->addr * 2; - for(uint16_t x = 0; x < length; x++) { - uint16_t addr = start + x; - avr_isp_prog_tx_ch( - instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr))); - } - return STK_OK; -} - -static void avr_isp_prog_read_page(AvrIspProg* instance) { - furi_assert(instance); - uint8_t result = STK_FAILED; - uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); - uint8_t memtype = avr_isp_prog_getch(instance); - if(avr_isp_prog_getch(instance) != CRC_EOP) { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - return; - } - avr_isp_prog_tx_ch(instance, STK_INSYNC); - if(memtype == STK_SET_FLASH_TYPE) result = avr_isp_prog_flash_read_page(instance, length); - if(memtype == STK_SET_EEPROM_TYPE) result = avr_isp_prog_eeprom_read_page(instance, length); - avr_isp_prog_tx_ch(instance, result); -} - -static void avr_isp_prog_read_signature(AvrIspProg* instance) { - furi_assert(instance); - if(avr_isp_prog_getch(instance) != CRC_EOP) { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - return; - } - avr_isp_prog_tx_ch(instance, STK_INSYNC); - - avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR)); - avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY)); - avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER)); - - avr_isp_prog_tx_ch(instance, STK_OK); -} - -void avr_isp_prog_avrisp(AvrIspProg* instance) { - furi_assert(instance); - uint8_t ch = avr_isp_prog_getch(instance); - - switch(ch) { - case STK_GET_SYNC: - FURI_LOG_D(TAG, "cmd STK_GET_SYNC"); - instance->error = 0; - avr_isp_prog_empty_reply(instance); - break; - case STK_GET_SIGN_ON: - FURI_LOG_D(TAG, "cmd STK_GET_SIGN_ON"); - if(avr_isp_prog_getch(instance) == CRC_EOP) { - avr_isp_prog_tx_ch(instance, STK_INSYNC); - - avr_isp_prog_tx_ch(instance, 'A'); - avr_isp_prog_tx_ch(instance, 'V'); - avr_isp_prog_tx_ch(instance, 'R'); - avr_isp_prog_tx_ch(instance, ' '); - avr_isp_prog_tx_ch(instance, 'I'); - avr_isp_prog_tx_ch(instance, 'S'); - avr_isp_prog_tx_ch(instance, 'P'); - - avr_isp_prog_tx_ch(instance, STK_OK); - } else { - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } - break; - case STK_GET_PARAMETER: - FURI_LOG_D(TAG, "cmd STK_GET_PARAMETER"); - avr_isp_prog_get_version(instance, avr_isp_prog_getch(instance)); - break; - case STK_SET_DEVICE: - FURI_LOG_D(TAG, "cmd STK_SET_DEVICE"); - avr_isp_prog_fill(instance, 20); - avr_isp_prog_set_cfg(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_SET_DEVICE_EXT: // ignore for now - FURI_LOG_D(TAG, "cmd STK_SET_DEVICE_EXT"); - avr_isp_prog_fill(instance, 5); - avr_isp_prog_empty_reply(instance); - break; - case STK_ENTER_PROGMODE: - FURI_LOG_D(TAG, "cmd STK_ENTER_PROGMODE"); - if(!instance->pmode) avr_isp_prog_auto_set_spi_speed_start_pmode(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_LOAD_ADDRESS: - FURI_LOG_D(TAG, "cmd STK_LOAD_ADDRESS"); - instance->addr = avr_isp_prog_getch(instance) | avr_isp_prog_getch(instance) << 8; - avr_isp_prog_empty_reply(instance); - break; - case STK_PROG_FLASH: // ignore for now - FURI_LOG_D(TAG, "cmd STK_PROG_FLASH"); - avr_isp_prog_getch(instance); - avr_isp_prog_getch(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_PROG_DATA: // ignore for now - FURI_LOG_D(TAG, "cmd STK_PROG_DATA"); - avr_isp_prog_getch(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_PROG_PAGE: - FURI_LOG_D(TAG, "cmd STK_PROG_PAGE"); - avr_isp_prog_program_page(instance); - break; - case STK_READ_PAGE: - FURI_LOG_D(TAG, "cmd STK_READ_PAGE"); - avr_isp_prog_read_page(instance); - break; - case STK_UNIVERSAL: - FURI_LOG_D(TAG, "cmd STK_UNIVERSAL"); - avr_isp_prog_universal(instance); - break; - case STK_LEAVE_PROGMODE: - FURI_LOG_D(TAG, "cmd STK_LEAVE_PROGMODE"); - instance->error = 0; - if(instance->pmode) avr_isp_prog_end_pmode(instance); - avr_isp_prog_empty_reply(instance); - break; - case STK_READ_SIGN: - FURI_LOG_D(TAG, "cmd STK_READ_SIGN"); - avr_isp_prog_read_signature(instance); - break; - // expecting a command, not CRC_EOP - // this is how we can get back in sync - case CRC_EOP: - FURI_LOG_D(TAG, "cmd CRC_EOP"); - instance->error++; - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - break; - // anything else we will return STK_UNKNOWN - default: - FURI_LOG_D(TAG, "cmd STK_ERROR_CMD"); - instance->error++; - if(avr_isp_prog_getch(instance) == CRC_EOP) - avr_isp_prog_tx_ch(instance, STK_UNKNOWN); - else - avr_isp_prog_tx_ch(instance, STK_NOSYNC); - } - - if(instance->callback) { - instance->callback(instance->context); - } -} diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h deleted file mode 100644 index 2c15ab066..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "avr_isp_spi_sw.h" -#include - -typedef struct AvrIspProg AvrIspProg; -typedef void (*AvrIspProgCallback)(void* context); - -AvrIspProg* avr_isp_prog_init(void); -void avr_isp_prog_free(AvrIspProg* instance); -size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) ; -bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len); -size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len); -void avr_isp_prog_avrisp(AvrIspProg* instance); -void avr_isp_prog_exit(AvrIspProg* instance); -void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context); diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h deleted file mode 100644 index f8b07203e..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once - -// http://ww1.microchip.com/downloads/en/appnotes/atmel-0943-in-system-programming_applicationnote_avr910.pdf -// AVR ISP Definitions -#define AVR_ISP_HWVER 0X02 -#define AVR_ISP_SWMAJ 0X01 -#define AVR_ISP_SWMIN 0X12 -#define AVP_ISP_SERIAL_CONNECT_TYPE 0X53 -#define AVP_ISP_CONNECT_TYPE 0x93 -#define AVR_ISP_RESP_0 0X00 - -#define AVR_ISP_SET_PMODE 0xAC, 0x53, 0x00, 0x00 -#define AVR_ISP_READ_VENDOR 0x30, 0x00, 0x00, 0x00 -#define AVR_ISP_READ_PART_FAMILY 0x30, 0x00, 0x01, 0x00 -#define AVR_ISP_READ_PART_NUMBER 0x30, 0x00, 0x02, 0x00 -#define AVR_ISP_ERASE_CHIP \ - 0xAC, 0x80, 0x00, 0x00 //Erase Chip, Wait N ms, Release RESET to end the erase. -//The only way to end a Chip Erase cycle is by temporarily releasing the Reset line - -#define AVR_ISP_EXTENDED_ADDR(data) 0x4D, 0x00, data, 0x00 -#define AVR_ISP_WRITE_FLASH_LO(add, data) 0x40, (add >> 8) & 0xFF, add & 0xFF, data -#define AVR_ISP_WRITE_FLASH_HI(add, data) 0x48, (add >> 8) & 0xFF, add & 0xFF, data -#define AVR_ISP_READ_FLASH_LO(add) 0x20, (add >> 8) & 0xFF, add & 0xFF, 0x00 -#define AVR_ISP_READ_FLASH_HI(add) 0x28, (add >> 8) & 0xFF, add & 0xFF, 0x00 - -#define AVR_ISP_WRITE_EEPROM(add, data) \ - 0xC0, (add >> 8) & 0xFF, add & 0xFF, data //Send cmd, Wait N ms -#define AVR_ISP_READ_EEPROM(add) 0xA0, (add >> 8) & 0xFF, add & 0xFF, 0xFF - -#define AVR_ISP_COMMIT(add) \ - 0x4C, (add >> 8) & 0xFF, add & 0xFF, 0x00 //Send cmd, polling read last addr page - -#define AVR_ISP_OSCCAL(add) 0x38, 0x00, add, 0x00 - -#define AVR_ISP_WRITE_LOCK_BYTE(data) 0xAC, 0xE0, 0x00, data //Send cmd, Wait N ms -#define AVR_ISP_READ_LOCK_BYTE 0x58, 0x00, 0x00, 0x00 -#define AVR_ISP_WRITE_FUSE_LOW(data) 0xAC, 0xA0, 0x00, data //Send cmd, Wait N ms -#define AVR_ISP_READ_FUSE_LOW 0x50, 0x00, 0x00, 0x00 -#define AVR_ISP_WRITE_FUSE_HIGH(data) 0xAC, 0xA8, 0x00, data //Send cmd, Wait N ms -#define AVR_ISP_READ_FUSE_HIGH 0x58, 0x08, 0x00, 0x00 -#define AVR_ISP_WRITE_FUSE_EXTENDED(data) 0xAC, 0xA4, 0x00, data //Send cmd, Wait N ms (~write) -#define AVR_ISP_READ_FUSE_EXTENDED 0x50, 0x08, 0x00, 0x00 - -#define AVR_ISP_EECHUNK 0x20 - -// https://www.microchip.com/content/dam/mchp/documents/OTH/ApplicationNotes/ApplicationNotes/doc2525.pdf -// STK Definitions -#define STK_OK 0x10 -#define STK_FAILED 0x11 -#define STK_UNKNOWN 0x12 -#define STK_INSYNC 0x14 -#define STK_NOSYNC 0x15 -#define CRC_EOP 0x20 - -#define STK_GET_SYNC 0x30 -#define STK_GET_SIGN_ON 0x31 -#define STK_SET_PARAMETER 0x40 -#define STK_GET_PARAMETER 0x41 -#define STK_SET_DEVICE 0x42 -#define STK_SET_DEVICE_EXT 0x45 -#define STK_ENTER_PROGMODE 0x50 -#define STK_LEAVE_PROGMODE 0x51 -#define STK_CHIP_ERASE 0x52 -#define STK_CHECK_AUTOINC 0x53 -#define STK_LOAD_ADDRESS 0x55 -#define STK_UNIVERSAL 0x56 -#define STK_UNIVERSAL_MULTI 0x57 -#define STK_PROG_FLASH 0x60 -#define STK_PROG_DATA 0x61 -#define STK_PROG_FUSE 0x62 -#define STK_PROG_FUSE_EXT 0x65 -#define STK_PROG_LOCK 0x63 -#define STK_PROG_PAGE 0x64 -#define STK_READ_FLASH 0x70 -#define STK_READ_DATA 0x71 -#define STK_READ_FUSE 0x72 -#define STK_READ_LOCK 0x73 -#define STK_READ_PAGE 0x74 -#define STK_READ_SIGN 0x75 -#define STK_READ_OSCCAL 0x76 -#define STK_READ_FUSE_EXT 0x77 -#define STK_READ_OSCCAL_EXT 0x78 -#define STK_HW_VER 0x80 -#define STK_SW_MAJOR 0x81 -#define STK_SW_MINOR 0x82 -#define STK_LEDS 0x83 -#define STK_VTARGET 0x84 -#define STK_VADJUST 0x85 -#define STK_OSC_PSCALE 0x86 -#define STK_OSC_CMATCH 0x87 -#define STK_SCK_DURATION 0x89 -#define STK_BUFSIZEL 0x90 -#define STK_BUFSIZEH 0x91 -#define STK_STK500_TOPCARD_DETECT 0x98 - -#define STK_SET_EEPROM_TYPE 0X45 -#define STK_SET_FLASH_TYPE 0X46 diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c deleted file mode 100644 index c6d9d54c8..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "avr_isp_spi_sw.h" - -#include - -#define AVR_ISP_SPI_SW_MISO &gpio_ext_pa6 -#define AVR_ISP_SPI_SW_MOSI &gpio_ext_pa7 -#define AVR_ISP_SPI_SW_SCK &gpio_ext_pb3 -#define AVR_ISP_RESET &gpio_ext_pb2 - -struct AvrIspSpiSw { - AvrIspSpiSwSpeed speed_wait_time; - const GpioPin* miso; - const GpioPin* mosi; - const GpioPin* sck; - const GpioPin* res; -}; - -AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed) { - AvrIspSpiSw* instance = malloc(sizeof(AvrIspSpiSw)); - instance->speed_wait_time = speed; - instance->miso = AVR_ISP_SPI_SW_MISO; - instance->mosi = AVR_ISP_SPI_SW_MOSI; - instance->sck = AVR_ISP_SPI_SW_SCK; - instance->res = AVR_ISP_RESET; - - furi_hal_gpio_init(instance->miso, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(instance->mosi, false); - furi_hal_gpio_init(instance->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(instance->sck, false); - furi_hal_gpio_init(instance->sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(instance->res, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - - return instance; -} - -void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { - furi_assert(instance); - furi_hal_gpio_init(instance->res, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(instance->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(instance->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(instance->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - free(instance); -} - -uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data) { - furi_assert(instance); - for(uint8_t i = 0; i < 8; ++i) { - furi_hal_gpio_write(instance->mosi, (data & 0x80) ? true : false); - - furi_hal_gpio_write(instance->sck, true); - if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) - furi_delay_us(instance->speed_wait_time - 1); - - data = (data << 1) | furi_hal_gpio_read(instance->miso); //-V792 - - furi_hal_gpio_write(instance->sck, false); - if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) - furi_delay_us(instance->speed_wait_time - 1); - } - return data; -} - -void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state) { - furi_assert(instance); - furi_hal_gpio_write(instance->res, state); -} - -void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state) { - furi_assert(instance); - furi_hal_gpio_write(instance->sck, state); -} \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h b/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h deleted file mode 100644 index 44de5ff79..000000000 --- a/applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -typedef enum { - AvrIspSpiSwSpeed1Mhz = 0, - AvrIspSpiSwSpeed400Khz = 1, - AvrIspSpiSwSpeed250Khz = 2, - AvrIspSpiSwSpeed125Khz = 4, - AvrIspSpiSwSpeed60Khz = 8, - AvrIspSpiSwSpeed40Khz = 12, - AvrIspSpiSwSpeed20Khz = 24, - AvrIspSpiSwSpeed10Khz = 48, - AvrIspSpiSwSpeed5Khz = 96, - AvrIspSpiSwSpeed1Khz = 480, -} AvrIspSpiSwSpeed; - -typedef struct AvrIspSpiSw AvrIspSpiSw; - -AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed); -void avr_isp_spi_sw_free(AvrIspSpiSw* instance); -uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data); -void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state); -void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state); \ No newline at end of file diff --git a/applications/external/avr_isp_programmer/lib/driver/clock.png b/applications/external/avr_isp_programmer/lib/driver/clock.png deleted file mode 100644 index 93a59fe681d26795d0db342a0c55ba42fc2c7529..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3649 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 LTALIbyM_M?u*zuP diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c deleted file mode 100644 index 4af125aee..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "../avr_isp_app_i.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const avr_isp_scene_on_enter_handlers[])(void*) = { -#include "avr_isp_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const avr_isp_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "avr_isp_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const avr_isp_scene_on_exit_handlers[])(void* context) = { -#include "avr_isp_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers avr_isp_scene_handlers = { - .on_enter_handlers = avr_isp_scene_on_enter_handlers, - .on_event_handlers = avr_isp_scene_on_event_handlers, - .on_exit_handlers = avr_isp_scene_on_exit_handlers, - .scene_num = AvrIspSceneNum, -}; diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h b/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h deleted file mode 100644 index 658ee7432..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) AvrIspScene##id, -typedef enum { -#include "avr_isp_scene_config.h" - AvrIspSceneNum, -} AvrIspScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers avr_isp_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "avr_isp_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "avr_isp_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "avr_isp_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c deleted file mode 100644 index e5f530fec..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c +++ /dev/null @@ -1,99 +0,0 @@ -#include "../avr_isp_app_i.h" -#include "../helpers/avr_isp_types.h" - -void avr_isp_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(app->view_dispatcher, result); - } -} - -void avr_isp_scene_about_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - FuriString* temp_str = furi_string_alloc(); - furi_string_printf(temp_str, "\e#%s\n", "Information"); - - furi_string_cat_printf(temp_str, "Version: %s\n", AVR_ISP_VERSION_APP); - furi_string_cat_printf(temp_str, "Developed by: %s\n", AVR_ISP_DEVELOPED); - furi_string_cat_printf(temp_str, "Github: %s\n\n", AVR_ISP_GITHUB); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); - furi_string_cat_printf( - temp_str, - "This application is an AVR in-system programmer based on stk500mk1. It is compatible with AVR-based" - " microcontrollers including Arduino. You can also use it to repair the chip if you accidentally" - " corrupt the bootloader.\n\n"); - - furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); - furi_string_cat_printf(temp_str, "- Create a dump of your chip on an SD card\n"); - furi_string_cat_printf(temp_str, "- Flash your chip firmware from the SD card\n"); - furi_string_cat_printf(temp_str, "- Act as a wired USB ISP using avrdude software\n\n"); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Supported chip series:"); - furi_string_cat_printf( - temp_str, - "Example command for avrdude flashing: avrdude.exe -p m328p -c stk500v1 -P COMxx -U flash:r:" - "X:\\sketch_sample.hex" - ":i\n"); - furi_string_cat_printf( - temp_str, - "Where: " - "-p m328p" - " brand of your chip, " - "-P COMxx" - " com port number in the system when " - "ISP Programmer" - " is enabled\n\n"); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Info"); - furi_string_cat_printf( - temp_str, - "ATtinyXXXX\nATmegaXXXX\nAT43Uxxx\nAT76C711\nAT86RF401\nAT90xxxxx\nAT94K\n" - "ATAxxxxx\nATA664251\nM3000\nLGT8F88P\nLGT8F168P\nLGT8F328P\n"); - - furi_string_cat_printf( - temp_str, "For a more detailed list of supported chips, see AVRDude help\n"); - - widget_add_text_box_element( - app->widget, - 0, - 0, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! \e!\n", - false); - widget_add_text_box_element( - app->widget, - 0, - 2, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! ISP Programmer \e!\n", - false); - widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); - furi_string_free(temp_str); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); -} - -bool avr_isp_scene_about_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void avr_isp_scene_about_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - // Clear views - widget_reset(app->widget); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c deleted file mode 100644 index 79c239390..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_chip_detect_callback(AvrIspCustomEvent event, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void avr_isp_scene_chip_detect_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - switch(app->error) { - case AvrIspErrorReading: - case AvrIspErrorWriting: - case AvrIspErrorWritingFuse: - avr_isp_chip_detect_set_state( - app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorOccured); - break; - case AvrIspErrorVerification: - avr_isp_chip_detect_set_state( - app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorVerification); - break; - - default: - avr_isp_chip_detect_set_state( - app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateNoDetect); - break; - } - app->error = AvrIspErrorNoError; - avr_isp_chip_detect_view_set_callback( - app->avr_isp_chip_detect_view, avr_isp_scene_chip_detect_callback, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewChipDetect); -} - -bool avr_isp_scene_chip_detect_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case AvrIspCustomEventSceneChipDetectOk: - - if(scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == - AvrIspViewProgrammer) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneProgrammer); - } else if( - scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == - AvrIspViewReader) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneInputName); - } else if( - scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == - AvrIspViewWriter) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneLoad); - } - - consumed = true; - break; - default: - break; - } - } else if(event.type == SceneManagerEventTypeTick) { - } - return consumed; -} - -void avr_isp_scene_chip_detect_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h deleted file mode 100644 index 6f22511db..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h +++ /dev/null @@ -1,10 +0,0 @@ -ADD_SCENE(avr_isp, start, Start) -ADD_SCENE(avr_isp, about, About) -ADD_SCENE(avr_isp, programmer, Programmer) -ADD_SCENE(avr_isp, reader, Reader) -ADD_SCENE(avr_isp, input_name, InputName) -ADD_SCENE(avr_isp, load, Load) -ADD_SCENE(avr_isp, writer, Writer) -ADD_SCENE(avr_isp, wiring, Wiring) -ADD_SCENE(avr_isp, chip_detect, ChipDetect) -ADD_SCENE(avr_isp, success, Success) diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c deleted file mode 100644 index 3394f4362..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c +++ /dev/null @@ -1,89 +0,0 @@ -#include "../avr_isp_app_i.h" -#include - -#define MAX_TEXT_INPUT_LEN 22 - -void avr_isp_scene_input_name_text_callback(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneInputName); -} - -void avr_isp_scene_input_name_get_timefilename(FuriString* name) { - FuriHalRtcDateTime datetime = {0}; - furi_hal_rtc_get_datetime(&datetime); - furi_string_printf( - name, - "AVR_dump-%.4d%.2d%.2d-%.2d%.2d%.2d", - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second); -} - -void avr_isp_scene_input_name_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - // Setup view - TextInput* text_input = app->text_input; - bool dev_name_empty = false; - - FuriString* file_name = furi_string_alloc(); - - avr_isp_scene_input_name_get_timefilename(file_name); - furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); - //highlighting the entire filename by default - dev_name_empty = true; - - strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME); - text_input_set_header_text(text_input, "Name dump"); - text_input_set_result_callback( - text_input, - avr_isp_scene_input_name_text_callback, - app, - app->file_name_tmp, - MAX_TEXT_INPUT_LEN, // buffer size - dev_name_empty); - - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(STORAGE_APP_DATA_PATH_PREFIX, AVR_ISP_APP_EXTENSION, ""); - text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); - - furi_string_free(file_name); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewTextInput); -} - -bool avr_isp_scene_input_name_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - if(event.type == SceneManagerEventTypeBack) { - scene_manager_previous_scene(app->scene_manager); - return true; - } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == AvrIspCustomEventSceneInputName) { - if(strcmp(app->file_name_tmp, "") != 0) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneReader); - } else { - } - } - } - return false; -} - -void avr_isp_scene_input_name_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - // Clear validator - void* validator_context = text_input_get_validator_callback_context(app->text_input); - text_input_set_validator(app->text_input, NULL, NULL); - validator_is_file_free(validator_context); - // Clear view - text_input_reset(app->text_input); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c deleted file mode 100644 index e8890e373..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_load_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - if(avr_isp_load_from_file(app)) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneWriter); - } else { - scene_manager_previous_scene(app->scene_manager); - } -} - -bool avr_isp_scene_load_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void avr_isp_scene_load_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c deleted file mode 100644 index 0915e1e8a..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_programmer_callback(AvrIspCustomEvent event, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void avr_isp_scene_programmer_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - avr_isp_programmer_view_set_callback( - app->avr_isp_programmer_view, avr_isp_scene_programmer_callback, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewProgrammer); -} - -bool avr_isp_scene_programmer_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void avr_isp_scene_programmer_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c deleted file mode 100644 index 8dcb47597..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_reader_callback(AvrIspCustomEvent event, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void avr_isp_scene_reader_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - avr_isp_reader_set_file_path( - app->avr_isp_reader_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); - avr_isp_reader_view_set_callback(app->avr_isp_reader_view, avr_isp_scene_reader_callback, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewReader); -} - -bool avr_isp_scene_reader_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - UNUSED(app); - bool consumed = false; - if(event.type == SceneManagerEventTypeBack) { - //do not handle exit on "Back" - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case AvrIspCustomEventSceneReadingOk: - scene_manager_next_scene(app->scene_manager, AvrIspSceneSuccess); - consumed = true; - break; - case AvrIspCustomEventSceneExit: - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorVerification: - app->error = AvrIspErrorVerification; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorReading: - app->error = AvrIspErrorReading; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - default: - break; - } - } else if(event.type == SceneManagerEventTypeTick) { - avr_isp_reader_update_progress(app->avr_isp_reader_view); - } - return consumed; -} - -void avr_isp_scene_reader_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c deleted file mode 100644 index b00bfefce..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_start_submenu_callback(void* context, uint32_t index) { - furi_assert(context); - AvrIspApp* app = context; - - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void avr_isp_scene_start_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - Submenu* submenu = app->submenu; - submenu_add_item( - submenu, "Dump AVR", SubmenuIndexAvrIspReader, avr_isp_scene_start_submenu_callback, app); - submenu_add_item( - submenu, "Flash AVR", SubmenuIndexAvrIspWriter, avr_isp_scene_start_submenu_callback, app); - submenu_add_item( - submenu, - "ISP Programmer", - SubmenuIndexAvrIspProgrammer, - avr_isp_scene_start_submenu_callback, - app); - submenu_add_item( - submenu, "Wiring", SubmenuIndexAvrIsWiring, avr_isp_scene_start_submenu_callback, app); - submenu_add_item( - submenu, "About", SubmenuIndexAvrIspAbout, avr_isp_scene_start_submenu_callback, app); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(app->scene_manager, AvrIspSceneStart)); - - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewSubmenu); -} - -bool avr_isp_scene_start_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexAvrIspAbout) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneAbout); - consumed = true; - } else if(event.event == SubmenuIndexAvrIspProgrammer) { - scene_manager_set_scene_state( - app->scene_manager, AvrIspSceneChipDetect, AvrIspViewProgrammer); - scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - } else if(event.event == SubmenuIndexAvrIspReader) { - scene_manager_set_scene_state( - app->scene_manager, AvrIspSceneChipDetect, AvrIspViewReader); - scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - } else if(event.event == SubmenuIndexAvrIspWriter) { - scene_manager_set_scene_state( - app->scene_manager, AvrIspSceneChipDetect, AvrIspViewWriter); - scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - } else if(event.event == SubmenuIndexAvrIsWiring) { - scene_manager_next_scene(app->scene_manager, AvrIspSceneWiring); - consumed = true; - } - scene_manager_set_scene_state(app->scene_manager, AvrIspSceneStart, event.event); - } - - return consumed; -} - -void avr_isp_scene_start_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - submenu_reset(app->submenu); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c deleted file mode 100644 index a88ed28aa..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_success_popup_callback(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneSuccess); -} - -void avr_isp_scene_success_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - Popup* popup = app->popup; - popup_set_icon(popup, 32, 5, &I_dolphin_nice_96x59); - popup_set_header(popup, "Success!", 8, 22, AlignLeft, AlignBottom); - popup_set_timeout(popup, 1500); - popup_set_context(popup, app); - popup_set_callback(popup, avr_isp_scene_success_popup_callback); - popup_enable_timeout(popup); - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewPopup); -} - -bool avr_isp_scene_success_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == AvrIspCustomEventSceneSuccess) { - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneStart); - return true; - } - } - return false; -} - -void avr_isp_scene_success_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - Popup* popup = app->popup; - popup_reset(popup); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c deleted file mode 100644 index 787ed5673..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_wiring_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - widget_add_icon_element(app->widget, 0, 0, &I_avr_wiring); - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); -} - -bool avr_isp_scene_wiring_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} -void avr_isp_scene_wiring_on_exit(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c b/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c deleted file mode 100644 index 39c944fd5..000000000 --- a/applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "../avr_isp_app_i.h" - -void avr_isp_scene_writer_callback(AvrIspCustomEvent event, void* context) { - furi_assert(context); - - AvrIspApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void avr_isp_scene_writer_on_enter(void* context) { - furi_assert(context); - - AvrIspApp* app = context; - avr_isp_writer_set_file_path( - app->avr_isp_writer_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); - avr_isp_writer_view_set_callback(app->avr_isp_writer_view, avr_isp_scene_writer_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWriter); -} - -bool avr_isp_scene_writer_on_event(void* context, SceneManagerEvent event) { - furi_assert(context); - - AvrIspApp* app = context; - bool consumed = false; - if(event.type == SceneManagerEventTypeBack) { - //do not handle exit on "Back" - consumed = true; - } else if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case AvrIspCustomEventSceneExitStartMenu: - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneStart); - consumed = true; - break; - case AvrIspCustomEventSceneExit: - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorVerification: - app->error = AvrIspErrorVerification; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorWriting: - app->error = AvrIspErrorWriting; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - case AvrIspCustomEventSceneErrorWritingFuse: - app->error = AvrIspErrorWritingFuse; - scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, AvrIspSceneChipDetect); - consumed = true; - break; - default: - break; - } - } else if(event.type == SceneManagerEventTypeTick) { - avr_isp_writer_update_progress(app->avr_isp_writer_view); - } - return consumed; -} - -void avr_isp_scene_writer_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c deleted file mode 100644 index fdcf71c36..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c +++ /dev/null @@ -1,213 +0,0 @@ -#include "avr_isp_view_chip_detect.h" -#include -#include - -#include "../helpers/avr_isp_worker_rw.h" - -struct AvrIspChipDetectView { - View* view; - AvrIspWorkerRW* avr_isp_worker_rw; - AvrIspChipDetectViewCallback callback; - void* context; -}; - -typedef struct { - uint16_t idx; - const char* name_chip; - uint32_t flash_size; - AvrIspChipDetectViewState state; -} AvrIspChipDetectViewModel; - -void avr_isp_chip_detect_view_set_callback( - AvrIspChipDetectView* instance, - AvrIspChipDetectViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state) { - furi_assert(instance); - - with_view_model( - instance->view, AvrIspChipDetectViewModel * model, { model->state = state; }, true); -} - -void avr_isp_chip_detect_view_draw(Canvas* canvas, AvrIspChipDetectViewModel* model) { - canvas_clear(canvas); - - char str_buf[64] = {0}; - canvas_set_font(canvas, FontPrimary); - - switch(model->state) { - case AvrIspChipDetectViewStateDetected: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip detected!"); - canvas_draw_icon(canvas, 29, 14, &I_chip_long_70x22); - canvas_set_font(canvas, FontSecondary); - snprintf(str_buf, sizeof(str_buf), "%ld Kb", model->flash_size / 1024); - canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignCenter, str_buf); - canvas_draw_str_aligned(canvas, 64, 45, AlignCenter, AlignCenter, model->name_chip); - elements_button_right(canvas, "Next"); - break; - case AvrIspChipDetectViewStateErrorOccured: - canvas_draw_str_aligned( - canvas, 64, 5, AlignCenter, AlignCenter, "Error occured, try again!"); - canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignCenter, "Check the wiring and retry"); - break; - case AvrIspChipDetectViewStateErrorVerification: - canvas_draw_str_aligned( - canvas, 64, 5, AlignCenter, AlignCenter, "Data verification failed"); - canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 64, 45, AlignCenter, AlignCenter, "Try to restart the process"); - break; - - default: - //AvrIspChipDetectViewStateNoDetect - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip not found!"); - canvas_draw_icon(canvas, 29, 12, &I_chif_not_found_83x37); - - break; - } - canvas_set_font(canvas, FontSecondary); - elements_button_left(canvas, "Retry"); -} - -bool avr_isp_chip_detect_view_input(InputEvent* event, void* context) { - furi_assert(context); - - AvrIspChipDetectView* instance = context; - - if(event->type == InputTypeShort) { - if(event->key == InputKeyBack) { - return false; - } else if(event->key == InputKeyRight) { - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { - if(model->state == AvrIspChipDetectViewStateDetected) { - if(instance->callback) - instance->callback( - AvrIspCustomEventSceneChipDetectOk, instance->context); - } - }, - false); - - } else if(event->key == InputKeyLeft) { - bool detect_chip = false; - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { - if(model->state != AvrIspChipDetectViewStateDetecting) { - model->state = AvrIspChipDetectViewStateDetecting; - detect_chip = true; - } - }, - false); - if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); - } - } else { - return false; - } - - return true; -} - -static void avr_isp_chip_detect_detect_chip_callback( - void* context, - const char* name, - bool detect_chip, - uint32_t flash_size) { - furi_assert(context); - - AvrIspChipDetectView* instance = context; - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { - model->name_chip = name; - model->flash_size = flash_size; - if(detect_chip) { - model->state = AvrIspChipDetectViewStateDetected; - } else { - model->state = AvrIspChipDetectViewStateNoDetect; - } - }, - true); -} -void avr_isp_chip_detect_view_enter(void* context) { - furi_assert(context); - - AvrIspChipDetectView* instance = context; - bool detect_chip = false; - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { - if(model->state == AvrIspChipDetectViewStateNoDetect || - model->state == AvrIspChipDetectViewStateDetected) { - detect_chip = true; - } - }, - false); - - //Start avr_isp_worker_rw - instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); - - avr_isp_worker_rw_set_callback( - instance->avr_isp_worker_rw, avr_isp_chip_detect_detect_chip_callback, instance); - - if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); -} - -void avr_isp_chip_detect_view_exit(void* context) { - furi_assert(context); - - AvrIspChipDetectView* instance = context; - - avr_isp_worker_rw_set_callback(instance->avr_isp_worker_rw, NULL, NULL); - avr_isp_worker_rw_free(instance->avr_isp_worker_rw); -} - -AvrIspChipDetectView* avr_isp_chip_detect_view_alloc() { - AvrIspChipDetectView* instance = malloc(sizeof(AvrIspChipDetectView)); - - // View allocation and configuration - instance->view = view_alloc(); - - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspChipDetectViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_chip_detect_view_draw); - view_set_input_callback(instance->view, avr_isp_chip_detect_view_input); - view_set_enter_callback(instance->view, avr_isp_chip_detect_view_enter); - view_set_exit_callback(instance->view, avr_isp_chip_detect_view_exit); - - with_view_model( - instance->view, - AvrIspChipDetectViewModel * model, - { model->state = AvrIspChipDetectViewStateNoDetect; }, - false); - return instance; -} - -void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h b/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h deleted file mode 100644 index 37f2ae233..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include "../helpers/avr_isp_types.h" -#include "../helpers/avr_isp_event.h" - -typedef struct AvrIspChipDetectView AvrIspChipDetectView; - -typedef void (*AvrIspChipDetectViewCallback)(AvrIspCustomEvent event, void* context); - -typedef enum { - AvrIspChipDetectViewStateNoDetect, - AvrIspChipDetectViewStateDetecting, - AvrIspChipDetectViewStateDetected, - AvrIspChipDetectViewStateErrorOccured, - AvrIspChipDetectViewStateErrorVerification, -} AvrIspChipDetectViewState; - -void avr_isp_chip_detect_view_set_callback( - AvrIspChipDetectView* instance, - AvrIspChipDetectViewCallback callback, - void* context); - -void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state); - -AvrIspChipDetectView* avr_isp_chip_detect_view_alloc(); - -void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance); - -View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance); - -void avr_isp_chip_detect_view_exit(void* context); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c deleted file mode 100644 index 34e18770b..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c +++ /dev/null @@ -1,134 +0,0 @@ -#include "avr_isp_view_programmer.h" -#include - -#include "../helpers/avr_isp_worker.h" -#include - -struct AvrIspProgrammerView { - View* view; - AvrIspWorker* worker; - AvrIspProgrammerViewCallback callback; - void* context; -}; - -typedef struct { - AvrIspProgrammerViewStatus status; -} AvrIspProgrammerViewModel; - -void avr_isp_programmer_view_set_callback( - AvrIspProgrammerView* instance, - AvrIspProgrammerViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_programmer_view_draw(Canvas* canvas, AvrIspProgrammerViewModel* model) { - canvas_clear(canvas); - - if(model->status == AvrIspProgrammerViewStatusUSBConnect) { - canvas_set_font(canvas, FontPrimary); - canvas_draw_icon(canvas, 0, 0, &I_isp_active_128x53); - elements_multiline_text(canvas, 45, 10, "ISP mode active"); - } else { - canvas_set_font(canvas, FontSecondary); - canvas_draw_icon(canvas, 51, 6, &I_link_waiting_77x56); - elements_multiline_text(canvas, 0, 25, "Waiting for\nsoftware\nconnection"); - } -} - -bool avr_isp_programmer_view_input(InputEvent* event, void* context) { - furi_assert(context); - UNUSED(context); - - if(event->key == InputKeyBack || event->type != InputTypeShort) { - return false; - } - - return true; -} - -static void avr_isp_programmer_usb_connect_callback(void* context, bool status_connect) { - furi_assert(context); - AvrIspProgrammerView* instance = context; - - with_view_model( - instance->view, - AvrIspProgrammerViewModel * model, - { - if(status_connect) { - model->status = AvrIspProgrammerViewStatusUSBConnect; - } else { - model->status = AvrIspProgrammerViewStatusNoUSBConnect; - } - }, - true); -} - -void avr_isp_programmer_view_enter(void* context) { - furi_assert(context); - - AvrIspProgrammerView* instance = context; - with_view_model( - instance->view, - AvrIspProgrammerViewModel * model, - { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, - true); - - //Start worker - instance->worker = avr_isp_worker_alloc(instance->context); - - avr_isp_worker_set_callback( - instance->worker, avr_isp_programmer_usb_connect_callback, instance); - - avr_isp_worker_start(instance->worker); -} - -void avr_isp_programmer_view_exit(void* context) { - furi_assert(context); - - AvrIspProgrammerView* instance = context; - //Stop worker - if(avr_isp_worker_is_running(instance->worker)) { - avr_isp_worker_stop(instance->worker); - } - avr_isp_worker_set_callback(instance->worker, NULL, NULL); - avr_isp_worker_free(instance->worker); -} - -AvrIspProgrammerView* avr_isp_programmer_view_alloc() { - AvrIspProgrammerView* instance = malloc(sizeof(AvrIspProgrammerView)); - - // View allocation and configuration - instance->view = view_alloc(); - - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspProgrammerViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_programmer_view_draw); - view_set_input_callback(instance->view, avr_isp_programmer_view_input); - view_set_enter_callback(instance->view, avr_isp_programmer_view_enter); - view_set_exit_callback(instance->view, avr_isp_programmer_view_exit); - - with_view_model( - instance->view, - AvrIspProgrammerViewModel * model, - { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, - false); - return instance; -} - -void avr_isp_programmer_view_free(AvrIspProgrammerView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h b/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h deleted file mode 100644 index 9f005b026..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include "../helpers/avr_isp_types.h" -#include "../helpers/avr_isp_event.h" - -typedef struct AvrIspProgrammerView AvrIspProgrammerView; - -typedef void (*AvrIspProgrammerViewCallback)(AvrIspCustomEvent event, void* context); - -typedef enum { - AvrIspProgrammerViewStatusNoUSBConnect, - AvrIspProgrammerViewStatusUSBConnect, -} AvrIspProgrammerViewStatus; - -void avr_isp_programmer_view_set_callback( - AvrIspProgrammerView* instance, - AvrIspProgrammerViewCallback callback, - void* context); - -AvrIspProgrammerView* avr_isp_programmer_view_alloc(); - -void avr_isp_programmer_view_free(AvrIspProgrammerView* instance); - -View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance); - -void avr_isp_programmer_view_exit(void* context); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c deleted file mode 100644 index 92d15bd7f..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.c +++ /dev/null @@ -1,215 +0,0 @@ -#include "avr_isp_view_reader.h" -#include - -#include "../helpers/avr_isp_worker_rw.h" - -struct AvrIspReaderView { - View* view; - AvrIspWorkerRW* avr_isp_worker_rw; - const char* file_path; - const char* file_name; - AvrIspReaderViewCallback callback; - void* context; -}; - -typedef struct { - AvrIspReaderViewStatus status; - float progress_flash; - float progress_eeprom; -} AvrIspReaderViewModel; - -void avr_isp_reader_update_progress(AvrIspReaderView* instance) { - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - model->progress_flash = - avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); - model->progress_eeprom = - avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); - }, - true); -} - -void avr_isp_reader_view_set_callback( - AvrIspReaderView* instance, - AvrIspReaderViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_reader_set_file_path( - AvrIspReaderView* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; -} - -void avr_isp_reader_view_draw(Canvas* canvas, AvrIspReaderViewModel* model) { - canvas_clear(canvas); - char str_buf[64] = {0}; - - canvas_set_font(canvas, FontPrimary); - switch(model->status) { - case AvrIspReaderViewStatusIDLE: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to dump"); - canvas_set_font(canvas, FontSecondary); - elements_button_center(canvas, "Start"); - break; - case AvrIspReaderViewStatusReading: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Reading dump"); - break; - case AvrIspReaderViewStatusVerification: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifyng dump"); - break; - - default: - break; - } - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 0, 27, "Flash"); - snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100)); - elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_buf); - canvas_draw_str(canvas, 0, 43, "EEPROM"); - snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_buf); -} - -bool avr_isp_reader_view_input(InputEvent* event, void* context) { - furi_assert(context); - AvrIspReaderView* instance = context; - - bool ret = true; - if(event->key == InputKeyBack && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - if(model->status == AvrIspReaderViewStatusIDLE) { - if(instance->callback) - instance->callback(AvrIspCustomEventSceneExit, instance->context); - ret = false; - } - }, - false); - } else if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - if(model->status == AvrIspReaderViewStatusIDLE) { - model->status = AvrIspReaderViewStatusReading; - avr_isp_worker_rw_read_dump_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - } - }, - false); - } - return ret; -} - -static void avr_isp_reader_callback_status(void* context, AvrIspWorkerRWStatus status) { - furi_assert(context); - AvrIspReaderView* instance = context; - - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - switch(status) { - case AvrIspWorkerRWStatusEndReading: - model->status = AvrIspReaderViewStatusVerification; - avr_isp_worker_rw_verification_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - model->status = AvrIspReaderViewStatusVerification; - break; - case AvrIspWorkerRWStatusEndVerification: - if(instance->callback) - instance->callback(AvrIspCustomEventSceneReadingOk, instance->context); - break; - case AvrIspWorkerRWStatusErrorVerification: - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); - break; - - default: - //AvrIspWorkerRWStatusErrorReading; - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorReading, instance->context); - break; - } - }, - true); -} - -void avr_isp_reader_view_enter(void* context) { - furi_assert(context); - AvrIspReaderView* instance = context; - - with_view_model( - instance->view, - AvrIspReaderViewModel * model, - { - model->status = AvrIspReaderViewStatusIDLE; - model->progress_flash = 0.0f; - model->progress_eeprom = 0.0f; - }, - true); - - //Start avr_isp_worker_rw - instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); - - avr_isp_worker_rw_set_callback_status( - instance->avr_isp_worker_rw, avr_isp_reader_callback_status, instance); - - avr_isp_worker_rw_start(instance->avr_isp_worker_rw); -} - -void avr_isp_reader_view_exit(void* context) { - furi_assert(context); - - AvrIspReaderView* instance = context; - //Stop avr_isp_worker_rw - if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { - avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); - } - - avr_isp_worker_rw_free(instance->avr_isp_worker_rw); -} - -AvrIspReaderView* avr_isp_reader_view_alloc() { - AvrIspReaderView* instance = malloc(sizeof(AvrIspReaderView)); - - // View allocation and configuration - instance->view = view_alloc(); - - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspReaderViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_reader_view_draw); - view_set_input_callback(instance->view, avr_isp_reader_view_input); - view_set_enter_callback(instance->view, avr_isp_reader_view_enter); - view_set_exit_callback(instance->view, avr_isp_reader_view_exit); - - return instance; -} - -void avr_isp_reader_view_free(AvrIspReaderView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* avr_isp_reader_view_get_view(AvrIspReaderView* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h b/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h deleted file mode 100644 index 44a439948..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_reader.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include "../helpers/avr_isp_types.h" -#include "../helpers/avr_isp_event.h" - -typedef struct AvrIspReaderView AvrIspReaderView; - -typedef void (*AvrIspReaderViewCallback)(AvrIspCustomEvent event, void* context); - -typedef enum { - AvrIspReaderViewStatusIDLE, - AvrIspReaderViewStatusReading, - AvrIspReaderViewStatusVerification, -} AvrIspReaderViewStatus; - -void avr_isp_reader_update_progress(AvrIspReaderView* instance); - -void avr_isp_reader_set_file_path( - AvrIspReaderView* instance, - const char* file_path, - const char* file_name); - -void avr_isp_reader_view_set_callback( - AvrIspReaderView* instance, - AvrIspReaderViewCallback callback, - void* context); - -AvrIspReaderView* avr_isp_reader_view_alloc(); - -void avr_isp_reader_view_free(AvrIspReaderView* instance); - -View* avr_isp_reader_view_get_view(AvrIspReaderView* instance); - -void avr_isp_reader_view_exit(void* context); diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c deleted file mode 100644 index a06b78535..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.c +++ /dev/null @@ -1,268 +0,0 @@ -#include "avr_isp_view_writer.h" -#include - -#include "../helpers/avr_isp_worker_rw.h" -#include - -struct AvrIspWriterView { - View* view; - AvrIspWorkerRW* avr_isp_worker_rw; - const char* file_path; - const char* file_name; - AvrIspWriterViewCallback callback; - void* context; -}; - -typedef struct { - AvrIspWriterViewStatus status; - float progress_flash; - float progress_eeprom; -} AvrIspWriterViewModel; - -void avr_isp_writer_update_progress(AvrIspWriterView* instance) { - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - model->progress_flash = - avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); - model->progress_eeprom = - avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); - }, - true); -} - -void avr_isp_writer_view_set_callback( - AvrIspWriterView* instance, - AvrIspWriterViewCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - - instance->callback = callback; - instance->context = context; -} - -void avr_isp_writer_set_file_path( - AvrIspWriterView* instance, - const char* file_path, - const char* file_name) { - furi_assert(instance); - - instance->file_path = file_path; - instance->file_name = file_name; -} - -void avr_isp_writer_view_draw(Canvas* canvas, AvrIspWriterViewModel* model) { - canvas_clear(canvas); - char str_flash[32] = {0}; - char str_eeprom[32] = {0}; - - canvas_set_font(canvas, FontPrimary); - - switch(model->status) { - case AvrIspWriterViewStatusIDLE: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to write"); - canvas_set_font(canvas, FontSecondary); - elements_button_center(canvas, "Start"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - break; - case AvrIspWriterViewStatusWriting: - if(float_is_equal(model->progress_flash, 0.f)) { - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying firmware"); - snprintf(str_flash, sizeof(str_flash), "***"); - snprintf(str_eeprom, sizeof(str_eeprom), "***"); - } else { - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing dump"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf( - str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - } - break; - case AvrIspWriterViewStatusVerification: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying dump"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - break; - case AvrIspWriterViewStatusWritingFuse: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing fuse"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - break; - case AvrIspWriterViewStatusWritingFuseOk: - canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Done!"); - snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); - snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - canvas_set_font(canvas, FontSecondary); - elements_button_center(canvas, "Reflash"); - elements_button_right(canvas, "Exit"); - break; - - default: - break; - } - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 0, 27, "Flash"); - // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100)); - elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_flash); - canvas_draw_str(canvas, 0, 43, "EEPROM"); - // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100)); - elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_eeprom); -} - -bool avr_isp_writer_view_input(InputEvent* event, void* context) { - furi_assert(context); - AvrIspWriterView* instance = context; - - bool ret = true; - if(event->key == InputKeyBack && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - if((model->status == AvrIspWriterViewStatusIDLE) || - (model->status == AvrIspWriterViewStatusWritingFuseOk)) { - if(instance->callback) - instance->callback(AvrIspCustomEventSceneExit, instance->context); - ret = false; - } - }, - false); - } else if(event->key == InputKeyOk && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - if((model->status == AvrIspWriterViewStatusIDLE) || - (model->status == AvrIspWriterViewStatusWritingFuseOk)) { - model->status = AvrIspWriterViewStatusWriting; - - avr_isp_worker_rw_write_dump_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - } - }, - false); - } else if(event->key == InputKeyRight && event->type == InputTypeShort) { - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - if((model->status == AvrIspWriterViewStatusIDLE) || - (model->status == AvrIspWriterViewStatusWritingFuseOk)) { - if(instance->callback) - instance->callback(AvrIspCustomEventSceneExitStartMenu, instance->context); - ret = false; - } - }, - false); - } - return ret; -} - -static void avr_isp_writer_callback_status(void* context, AvrIspWorkerRWStatus status) { - furi_assert(context); - - AvrIspWriterView* instance = context; - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - switch(status) { - case AvrIspWorkerRWStatusEndWriting: - model->status = AvrIspWriterViewStatusVerification; - avr_isp_worker_rw_verification_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - model->status = AvrIspWriterViewStatusVerification; - break; - case AvrIspWorkerRWStatusErrorVerification: - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); - break; - case AvrIspWorkerRWStatusEndVerification: - avr_isp_worker_rw_write_fuse_start( - instance->avr_isp_worker_rw, instance->file_path, instance->file_name); - model->status = AvrIspWriterViewStatusWritingFuse; - break; - case AvrIspWorkerRWStatusErrorWritingFuse: - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorWritingFuse, instance->context); - break; - case AvrIspWorkerRWStatusEndWritingFuse: - model->status = AvrIspWriterViewStatusWritingFuseOk; - break; - - default: - //AvrIspWorkerRWStatusErrorWriting; - if(instance->callback) - instance->callback(AvrIspCustomEventSceneErrorWriting, instance->context); - break; - } - }, - true); -} - -void avr_isp_writer_view_enter(void* context) { - furi_assert(context); - - AvrIspWriterView* instance = context; - with_view_model( - instance->view, - AvrIspWriterViewModel * model, - { - model->status = AvrIspWriterViewStatusIDLE; - model->progress_flash = 0.0f; - model->progress_eeprom = 0.0f; - }, - true); - - //Start avr_isp_worker_rw - instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); - - avr_isp_worker_rw_set_callback_status( - instance->avr_isp_worker_rw, avr_isp_writer_callback_status, instance); - - avr_isp_worker_rw_start(instance->avr_isp_worker_rw); -} - -void avr_isp_writer_view_exit(void* context) { - furi_assert(context); - AvrIspWriterView* instance = context; - - //Stop avr_isp_worker_rw - if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { - avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); - } - - avr_isp_worker_rw_free(instance->avr_isp_worker_rw); -} - -AvrIspWriterView* avr_isp_writer_view_alloc() { - AvrIspWriterView* instance = malloc(sizeof(AvrIspWriterView)); - - // View allocation and configuration - instance->view = view_alloc(); - - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspWriterViewModel)); - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_writer_view_draw); - view_set_input_callback(instance->view, avr_isp_writer_view_input); - view_set_enter_callback(instance->view, avr_isp_writer_view_enter); - view_set_exit_callback(instance->view, avr_isp_writer_view_exit); - - return instance; -} - -void avr_isp_writer_view_free(AvrIspWriterView* instance) { - furi_assert(instance); - - view_free(instance->view); - free(instance); -} - -View* avr_isp_writer_view_get_view(AvrIspWriterView* instance) { - furi_assert(instance); - - return instance->view; -} diff --git a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h b/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h deleted file mode 100644 index 1ff728387..000000000 --- a/applications/external/avr_isp_programmer/views/avr_isp_view_writer.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include "../helpers/avr_isp_types.h" -#include "../helpers/avr_isp_event.h" - -typedef struct AvrIspWriterView AvrIspWriterView; - -typedef void (*AvrIspWriterViewCallback)(AvrIspCustomEvent event, void* context); - -typedef enum { - AvrIspWriterViewStatusIDLE, - AvrIspWriterViewStatusWriting, - AvrIspWriterViewStatusVerification, - AvrIspWriterViewStatusWritingFuse, - AvrIspWriterViewStatusWritingFuseOk, -} AvrIspWriterViewStatus; - -void avr_isp_writer_update_progress(AvrIspWriterView* instance); - -void avr_isp_writer_set_file_path( - AvrIspWriterView* instance, - const char* file_path, - const char* file_name); - -void avr_isp_writer_view_set_callback( - AvrIspWriterView* instance, - AvrIspWriterViewCallback callback, - void* context); - -AvrIspWriterView* avr_isp_writer_view_alloc(); - -void avr_isp_writer_view_free(AvrIspWriterView* instance); - -View* avr_isp_writer_view_get_view(AvrIspWriterView* instance); - -void avr_isp_writer_view_exit(void* context); diff --git a/applications/external/bad_bt/application.fam b/applications/external/bad_bt/application.fam deleted file mode 100644 index 981c0c0c0..000000000 --- a/applications/external/bad_bt/application.fam +++ /dev/null @@ -1,16 +0,0 @@ -App( - appid="bad_bt", - name="Bad BT", - apptype=FlipperAppType.EXTERNAL, - entry_point="bad_bt_app", - requires=[ - "gui", - "dialogs", - ], - stack_size=2 * 1024, - order=70, - fap_libs=["assets"], - fap_category="Bluetooth", - fap_icon="images/badbt_10px.png", - fap_icon_assets="images", -) diff --git a/applications/external/bad_bt/bad_bt_app.c b/applications/external/bad_bt/bad_bt_app.c deleted file mode 100644 index 6ac7d4fa4..000000000 --- a/applications/external/bad_bt/bad_bt_app.c +++ /dev/null @@ -1,333 +0,0 @@ -#include "bad_bt_app.h" -#include -#include -#include -#include -#include - -#include -#include - -#define BAD_BT_SETTINGS_FILE_NAME ".badbt.settings" -#define BAD_BT_APP_PATH_BOUND_KEYS_FOLDER EXT_PATH("badbt") -#define BAD_BT_APP_PATH_BOUND_KEYS_FILE BAD_BT_APP_PATH_BOUND_KEYS_FOLDER "/.badbt.keys" - -#define BAD_BT_SETTINGS_PATH BAD_BT_APP_BASE_CONFIG_FOLDER "/" BAD_BT_SETTINGS_FILE_NAME - -static bool bad_bt_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - BadBtApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool bad_bt_app_back_event_callback(void* context) { - furi_assert(context); - BadBtApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void bad_bt_app_tick_event_callback(void* context) { - furi_assert(context); - BadBtApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -static void bad_bt_load_settings(BadBtApp* app) { - furi_string_reset(app->keyboard_layout); - strcpy(app->config.bt_name, ""); - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_BT_MAC_ADDRESS_LEN); - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_existing(file, BAD_BT_SETTINGS_PATH)) { - FuriString* tmp_str = furi_string_alloc(); - if(!flipper_format_read_string(file, "Keyboard_Layout", app->keyboard_layout)) { - furi_string_reset(app->keyboard_layout); - } - if(!flipper_format_read_bool(file, "BT_Remember", &(app->bt_remember), 1)) { - app->bt_remember = false; - } - if(flipper_format_read_string(file, "Bt_Name", tmp_str) && !furi_string_empty(tmp_str)) { - strcpy(app->config.bt_name, furi_string_get_cstr(tmp_str)); - } else { - strcpy(app->config.bt_name, ""); - } - if(!flipper_format_read_hex( - file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN)) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_BT_MAC_ADDRESS_LEN); - } - furi_string_free(tmp_str); - flipper_format_file_close(file); - } - flipper_format_free(file); - - if(!furi_string_empty(app->keyboard_layout)) { - FileInfo layout_file_info; - FS_Error file_check_err = storage_common_stat( - storage, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); - if(file_check_err != FSE_OK) { - furi_string_reset(app->keyboard_layout); - return; - } - if(layout_file_info.size != 256) { - furi_string_reset(app->keyboard_layout); - } - } - - furi_record_close(RECORD_STORAGE); -} - -static void bad_bt_save_settings(BadBtApp* app) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_always(file, BAD_BT_SETTINGS_PATH)) { - flipper_format_write_string(file, "Keyboard_Layout", app->keyboard_layout); - flipper_format_write_bool(file, "BT_Remember", &(app->bt_remember), 1); - flipper_format_write_string_cstr(file, "Bt_Name", app->config.bt_name); - flipper_format_write_hex( - file, "Bt_Mac", (uint8_t*)&app->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN); - flipper_format_file_close(file); - } - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); -} - -void bad_bt_reload_worker(BadBtApp* app) { - bad_bt_script_close(app->bad_bt_script); - app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app); - bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout); -} - -void bad_kb_config_refresh_menu(BadBtApp* app) { - scene_manager_next_scene(app->scene_manager, BadBtSceneConfig); - scene_manager_previous_scene(app->scene_manager); -} - -int32_t bad_bt_config_switch_mode(BadBtApp* app) { - bad_bt_reload_worker(app); - furi_hal_bt_start_advertising(); - bad_kb_config_refresh_menu(app); - return 0; -} - -void bad_bt_config_switch_remember_mode(BadBtApp* app) { - if(app->bt_remember) { - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - bt_set_profile_mac_address(app->bt, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS); - bt_enable_peer_key_update(app->bt); - } else { - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - bt_set_profile_mac_address(app->bt, app->config.bt_mac); - bt_disable_peer_key_update(app->bt); - } - bad_bt_reload_worker(app); -} - -int32_t bad_bt_connection_init(BadBtApp* app) { - // Set original name and mac address in prev config - strcpy( - app->prev_config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - - memcpy(app->prev_config.bt_mac, furi_hal_version_get_ble_mac(), BAD_BT_MAC_ADDRESS_LEN); - - bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(app->bt); - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - bt_keys_storage_set_storage_path(app->bt, BAD_BT_APP_PATH_BOUND_KEYS_FILE); - if(strcmp(app->config.bt_name, "") != 0) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->config.bt_name); - } - if(app->bt_remember) { - furi_hal_bt_set_profile_mac_addr( - FuriHalBtProfileHidKeyboard, (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS); - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - } else { - if(memcmp( - app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) != - 0) { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->config.bt_mac); - } - furi_hal_bt_set_profile_pairing_method(FuriHalBtProfileHidKeyboard, GapPairingNone); - } - bt_set_profile(app->bt, BtProfileHidKeyboard); - if(strcmp(app->config.bt_name, "") == 0) { - strcpy(app->config.bt_name, furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard)); - } - if(memcmp(app->config.bt_mac, (uint8_t*)&BAD_BT_EMPTY_MAC_ADDRESS, BAD_BT_MAC_ADDRESS_LEN) == - 0) { - memcpy( - app->config.bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_BT_MAC_ADDRESS_LEN); - } - - furi_hal_bt_start_advertising(); - if(app->bt_remember) { - bt_enable_peer_key_update(app->bt); - } else { - bt_disable_peer_key_update(app->bt); - } - - return 0; -} - -void bad_bt_connection_deinit(BadBtApp* app) { - bt_disconnect(app->bt); - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - bt_keys_storage_set_default_path(app->bt); - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->prev_config.bt_name); - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->prev_config.bt_mac); - furi_hal_bt_set_profile_pairing_method( - FuriHalBtProfileHidKeyboard, GapPairingPinCodeVerifyYesNo); - bt_set_profile(app->bt, BtProfileSerial); - bt_enable_peer_key_update(app->bt); -} - -BadBtApp* bad_bt_app_alloc(char* arg) { - BadBtApp* app = malloc(sizeof(BadBtApp)); - - app->bad_bt_script = NULL; - - app->file_path = furi_string_alloc(); - app->keyboard_layout = furi_string_alloc(); - if(arg && strlen(arg)) { - furi_string_set(app->file_path, arg); - } - - Storage* storage = furi_record_open(RECORD_STORAGE); - storage_simply_mkdir(storage, BAD_BT_APP_BASE_CONFIG_FOLDER); - furi_record_close(RECORD_STORAGE); - - bad_bt_load_settings(app); - - app->gui = furi_record_open(RECORD_GUI); - app->notifications = furi_record_open(RECORD_NOTIFICATION); - app->dialogs = furi_record_open(RECORD_DIALOGS); - - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - - app->scene_manager = scene_manager_alloc(&bad_bt_scene_handlers, app); - - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, bad_bt_app_tick_event_callback, 500); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, bad_bt_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, bad_bt_app_back_event_callback); - - Bt* bt = furi_record_open(RECORD_BT); - app->bt = bt; - app->bt->suppress_pin_screen = true; - - // Custom Widget - app->widget = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewError, widget_get_view(app->widget)); - - app->var_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewConfig, variable_item_list_get_view(app->var_item_list)); - - app->bad_bt_view = bad_bt_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewWork, bad_bt_get_view(app->bad_bt_view)); - - app->text_input = text_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewConfigName, text_input_get_view(app->text_input)); - - app->byte_input = byte_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBtAppViewConfigMac, byte_input_get_view(app->byte_input)); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->conn_init_thread = furi_thread_alloc_ex( - "BadBtConnInit", 1024, (FuriThreadCallback)bad_bt_connection_init, app); - furi_thread_start(app->conn_init_thread); - if(!furi_string_empty(app->file_path)) { - app->bad_bt_script = bad_bt_script_open(app->file_path, app->bt, app); - bad_bt_script_set_keyboard_layout(app->bad_bt_script, app->keyboard_layout); - scene_manager_next_scene(app->scene_manager, BadBtSceneWork); - } else { - furi_string_set(app->file_path, BAD_BT_APP_BASE_FOLDER); - scene_manager_next_scene(app->scene_manager, BadBtSceneFileSelect); - } - - return app; -} - -void bad_bt_app_free(BadBtApp* app) { - furi_assert(app); - - if(app->bad_bt_script) { - bad_bt_script_close(app->bad_bt_script); - app->bad_bt_script = NULL; - } - - // Views - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewWork); - bad_bt_free(app->bad_bt_view); - - // Custom Widget - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewError); - widget_free(app->widget); - - // Variable item list - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfig); - variable_item_list_free(app->var_item_list); - - // Text Input - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigName); - text_input_free(app->text_input); - - // Byte Input - view_dispatcher_remove_view(app->view_dispatcher, BadBtAppViewConfigMac); - byte_input_free(app->byte_input); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Restore bt config - app->bt->suppress_pin_screen = false; - if(app->conn_init_thread) { - furi_thread_join(app->conn_init_thread); - furi_thread_free(app->conn_init_thread); - bad_bt_connection_deinit(app); - } - - // Close records - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_DIALOGS); - furi_record_close(RECORD_BT); - - bad_bt_save_settings(app); - - furi_string_free(app->file_path); - furi_string_free(app->keyboard_layout); - - free(app); -} - -int32_t bad_bt_app(void* p) { - BadBtApp* bad_bt_app = bad_bt_app_alloc((char*)p); - - view_dispatcher_run(bad_bt_app->view_dispatcher); - - bad_bt_app_free(bad_bt_app); - return 0; -} diff --git a/applications/external/bad_bt/bad_bt_app.h b/applications/external/bad_bt/bad_bt_app.h deleted file mode 100644 index 13b0844b0..000000000 --- a/applications/external/bad_bt/bad_bt_app.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "scenes/bad_bt_scene.h" -#include "helpers/ducky_script.h" - -#include -#include -#include -#include -#include -#include "bad_bt_icons.h" - -#define BAD_BT_APP_BASE_FOLDER EXT_PATH("badusb") -#define BAD_BT_APP_BASE_CONFIG_FOLDER EXT_PATH("badbt") -#define BAD_BT_APP_PATH_LAYOUT_FOLDER BAD_BT_APP_BASE_FOLDER "/assets/layouts" -#define BAD_BT_APP_SCRIPT_EXTENSION ".txt" -#define BAD_BT_APP_LAYOUT_EXTENSION ".kl" - -typedef enum BadBtCustomEvent { - BadBtAppCustomEventTextEditResult, - BadBtAppCustomEventByteInputDone, - BadBtCustomEventErrorBack -} BadBtCustomEvent; - -typedef enum { - BadBtAppViewError, - BadBtAppViewWork, - BadBtAppViewConfig, - BadBtAppViewConfigMac, - BadBtAppViewConfigName -} BadBtAppView; - -void bad_bt_config_switch_remember_mode(BadBtApp* app); - -int32_t bad_bt_connection_init(BadBtApp* app); - -void bad_bt_connection_deinit(BadBtApp* app); - -void bad_kb_config_refresh_menu(BadBtApp* app); \ No newline at end of file diff --git a/applications/external/bad_bt/helpers/ducky_script.c b/applications/external/bad_bt/helpers/ducky_script.c deleted file mode 100644 index bef965d1e..000000000 --- a/applications/external/bad_bt/helpers/ducky_script.c +++ /dev/null @@ -1,792 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "ducky_script.h" -#include "ducky_script_i.h" -#include -#include -#include "../bad_bt_app.h" - -const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] = - {0x41, 0x4a, 0xef, 0xb6, 0xa9, 0xd4}; -const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN] = - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -#define TAG "BadBT" -#define WORKER_TAG TAG "Worker" - -#define BADBT_ASCII_TO_KEY(script, x) \ - (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) - -/** - * Delays for waiting between HID key press and key release -*/ -const uint8_t bt_hid_delays[LevelRssiNum] = { - 60, // LevelRssi122_100 - 55, // LevelRssi99_80 - 50, // LevelRssi79_60 - 47, // LevelRssi59_40 - 34, // LevelRssi39_0 -}; - -uint8_t bt_timeout = 0; - -static LevelRssiRange bt_remote_rssi_range(Bt* bt) { - uint8_t rssi; - - if(!bt_remote_rssi(bt, &rssi)) return LevelRssiError; - - if(rssi <= 39) - return LevelRssi39_0; - else if(rssi <= 59) - return LevelRssi59_40; - else if(rssi <= 79) - return LevelRssi79_60; - else if(rssi <= 99) - return LevelRssi99_80; - else if(rssi <= 122) - return LevelRssi122_100; - - return LevelRssiError; -} - -static inline void update_bt_timeout(Bt* bt) { - LevelRssiRange r = bt_remote_rssi_range(bt); - if(r < LevelRssiNum) { - bt_timeout = bt_hid_delays[r]; - FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); - } -} - -typedef enum { - WorkerEvtStartStop = (1 << 0), - WorkerEvtPauseResume = (1 << 1), - WorkerEvtEnd = (1 << 2), - WorkerEvtConnect = (1 << 3), - WorkerEvtDisconnect = (1 << 4), -} WorkerEvtFlags; - -static const char ducky_cmd_id[] = {"ID"}; -static const char ducky_cmd_bt_id[] = {"BT_ID"}; - -static const uint8_t numpad_keys[10] = { - HID_KEYPAD_0, - HID_KEYPAD_1, - HID_KEYPAD_2, - HID_KEYPAD_3, - HID_KEYPAD_4, - HID_KEYPAD_5, - HID_KEYPAD_6, - HID_KEYPAD_7, - HID_KEYPAD_8, - HID_KEYPAD_9, -}; - -uint32_t ducky_get_command_len(const char* line) { - uint32_t len = strlen(line); - for(uint32_t i = 0; i < len; i++) { - if(line[i] == ' ') return i; - } - return 0; -} - -bool ducky_is_line_end(const char chr) { - return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); -} - -uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars) { - uint16_t keycode = ducky_get_keycode_by_name(param); - if(keycode != HID_KEYBOARD_NONE) { - return keycode; - } - - if((accept_chars) && (strlen(param) > 0)) { - return (BADBT_ASCII_TO_KEY(bad_bt, param[0]) & 0xFF); - } - return 0; -} - -bool ducky_get_number(const char* param, uint32_t* val) { - uint32_t value = 0; - if(sscanf(param, "%lu", &value) == 1) { - *val = value; - return true; - } - return false; -} - -void ducky_numlock_on(BadBtScript* bad_bt) { - UNUSED(bad_bt); - if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); - } -} - -bool ducky_numpad_press(BadBtScript* bad_bt, const char num) { - UNUSED(bad_bt); - if((num < '0') || (num > '9')) return false; - - uint16_t key = numpad_keys[num - '0']; - furi_hal_bt_hid_kb_press(key); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - - return true; -} - -bool ducky_altchar(BadBtScript* bad_bt, const char* charcode) { - uint8_t i = 0; - bool state = false; - - furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT); - - while(!ducky_is_line_end(charcode[i])) { - state = ducky_numpad_press(bad_bt, charcode[i]); - if(state == false) break; - i++; - } - - furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT); - - return state; -} - -bool ducky_altstring(BadBtScript* bad_bt, const char* param) { - uint32_t i = 0; - bool state = false; - - while(param[i] != '\0') { - if((param[i] < ' ') || (param[i] > '~')) { - i++; - continue; // Skip non-printable chars - } - - char temp_str[4]; - snprintf(temp_str, 4, "%u", param[i]); - - state = ducky_altchar(bad_bt, temp_str); - if(state == false) break; - i++; - } - return state; -} - -int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...) { - va_list args; - va_start(args, text); - - vsnprintf(bad_bt->st.error, sizeof(bad_bt->st.error), text, args); - - va_end(args); - return SCRIPT_STATE_ERROR; -} - -bool ducky_string(BadBtScript* bad_bt, const char* param) { - uint32_t i = 0; - - while(param[i] != '\0') { - if(param[i] != '\n') { - uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, param[i]); - if(keycode != HID_KEYBOARD_NONE) { - furi_hal_bt_hid_kb_press(keycode); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(keycode); - } - } else { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN); - } - i++; - } - bad_bt->stringdelay = 0; - return true; -} - -static bool ducky_string_next(BadBtScript* bad_bt) { - if(bad_bt->string_print_pos >= furi_string_size(bad_bt->string_print)) { - return true; - } - - char print_char = furi_string_get_char(bad_bt->string_print, bad_bt->string_print_pos); - - if(print_char != '\n') { - uint16_t keycode = BADBT_ASCII_TO_KEY(bad_bt, print_char); - if(keycode != HID_KEYBOARD_NONE) { - furi_hal_bt_hid_kb_press(keycode); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(keycode); - } - } else { - furi_hal_bt_hid_kb_press(HID_KEYBOARD_RETURN); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_RETURN); - } - - bad_bt->string_print_pos++; - - return false; -} - -static int32_t ducky_parse_line(BadBtScript* bad_bt, FuriString* line) { - uint32_t line_len = furi_string_size(line); - const char* line_tmp = furi_string_get_cstr(line); - - if(line_len == 0) { - return SCRIPT_STATE_NEXT_LINE; // Skip empty lines - } - FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); - - // Ducky Lang Functions - int32_t cmd_result = ducky_execute_cmd(bad_bt, line_tmp); - if(cmd_result != SCRIPT_STATE_CMD_UNKNOWN) { - return cmd_result; - } - - // Special keys + modifiers - uint16_t key = ducky_get_keycode(bad_bt, line_tmp, false); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_bt, "No keycode defined for %s", line_tmp); - } - if((key & 0xFF00) != 0) { - // It's a modifier key - uint32_t offset = ducky_get_command_len(line_tmp) + 1; - // ducky_get_command_len() returns 0 without space, so check for != 1 - if(offset != 1 && line_len > offset) { - // It's also a key combination - key |= ducky_get_keycode(bad_bt, line_tmp + offset, true); - } - } - furi_hal_bt_hid_kb_press(key); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - - return 0; -} - -static bool ducky_set_bt_id(BadBtScript* bad_bt, const char* line) { - size_t line_len = strlen(line); - size_t mac_len = BAD_BT_MAC_ADDRESS_LEN * 3; - if(line_len < mac_len + 1) return false; // MAC + at least 1 char for name - - uint8_t mac[BAD_BT_MAC_ADDRESS_LEN]; - for(size_t i = 0; i < BAD_BT_MAC_ADDRESS_LEN; i++) { - char a = line[i * 3]; - char b = line[i * 3 + 1]; - if((a < 'A' && a > 'F') || (a < '0' && a > '9') || (b < 'A' && b > 'F') || - (b < '0' && b > '9') || !hex_char_to_uint8(a, b, &mac[i])) { - return false; - } - } - furi_hal_bt_reverse_mac_addr(mac); - - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, line + mac_len); - bt_set_profile_mac_address(bad_bt->bt, mac); - return true; -} - -static bool ducky_script_preload(BadBtScript* bad_bt, File* script_file) { - uint8_t ret = 0; - uint32_t line_len = 0; - - furi_string_reset(bad_bt->line); - - do { - ret = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN); - for(uint16_t i = 0; i < ret; i++) { - if(bad_bt->file_buf[i] == '\n' && line_len > 0) { - bad_bt->st.line_nb++; - line_len = 0; - } else { - if(bad_bt->st.line_nb == 0) { // Save first line - furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]); - } - line_len++; - } - } - if(storage_file_eof(script_file)) { - if(line_len > 0) { - bad_bt->st.line_nb++; - break; - } - } - } while(ret > 0); - - const char* line_tmp = furi_string_get_cstr(bad_bt->line); - if(bad_bt->app->switch_mode_thread) { - furi_thread_join(bad_bt->app->switch_mode_thread); - furi_thread_free(bad_bt->app->switch_mode_thread); - bad_bt->app->switch_mode_thread = NULL; - } - // Looking for ID or BT_ID command at first line - bad_bt->set_usb_id = false; - bad_bt->set_bt_id = false; - bad_bt->has_usb_id = strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0; - // TODO: We setting has_usb_id to its value but ignoring it for now and not using anywhere here, may be used in a future to detect script type - bad_bt->has_bt_id = strncmp(line_tmp, ducky_cmd_bt_id, strlen(ducky_cmd_bt_id)) == 0; - if(bad_bt->has_bt_id) { - if(!bad_bt->app->bt_remember) { - bad_bt->set_bt_id = ducky_set_bt_id(bad_bt, &line_tmp[strlen(ducky_cmd_bt_id) + 1]); - } - } - - bad_kb_config_refresh_menu(bad_bt->app); - - if(!bad_bt->set_bt_id) { - const char* bt_name = bad_bt->app->config.bt_name; - const uint8_t* bt_mac = bad_bt->app->bt_remember ? (uint8_t*)&BAD_BT_BOUND_MAC_ADDRESS : - bad_bt->app->config.bt_mac; - bool reset_name = strncmp( - bt_name, - furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard), - BAD_BT_ADV_NAME_MAX_LEN); - bool reset_mac = memcmp( - bt_mac, - furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard), - BAD_BT_MAC_ADDRESS_LEN); - if(reset_name && reset_mac) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, bt_name); - } else if(reset_name) { - bt_set_profile_adv_name(bad_bt->bt, bt_name); - } - if(reset_mac) { - bt_set_profile_mac_address(bad_bt->bt, bt_mac); - } - } - - storage_file_seek(script_file, 0, true); - furi_string_reset(bad_bt->line); - - return true; -} - -static int32_t ducky_script_execute_next(BadBtScript* bad_bt, File* script_file) { - int32_t delay_val = 0; - - if(bad_bt->repeat_cnt > 0) { - bad_bt->repeat_cnt--; - delay_val = ducky_parse_line(bad_bt, bad_bt->line_prev); - if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line - return 0; - } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays - return delay_val; - } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button - return delay_val; - } else if(delay_val < 0) { // Script error - bad_bt->st.error_line = bad_bt->st.line_cur - 1; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur - 1U); - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_bt->defdelay); - } - } - - furi_string_set(bad_bt->line_prev, bad_bt->line); - furi_string_reset(bad_bt->line); - - while(1) { - if(bad_bt->buf_len == 0) { - bad_bt->buf_len = storage_file_read(script_file, bad_bt->file_buf, FILE_BUFFER_LEN); - if(storage_file_eof(script_file)) { - if((bad_bt->buf_len < FILE_BUFFER_LEN) && (bad_bt->file_end == false)) { - bad_bt->file_buf[bad_bt->buf_len] = '\n'; - bad_bt->buf_len++; - bad_bt->file_end = true; - } - } - - bad_bt->buf_start = 0; - if(bad_bt->buf_len == 0) return SCRIPT_STATE_END; - } - for(uint8_t i = bad_bt->buf_start; i < (bad_bt->buf_start + bad_bt->buf_len); i++) { - if(bad_bt->file_buf[i] == '\n' && furi_string_size(bad_bt->line) > 0) { - bad_bt->st.line_cur++; - bad_bt->buf_len = bad_bt->buf_len + bad_bt->buf_start - (i + 1); - bad_bt->buf_start = i + 1; - furi_string_trim(bad_bt->line); - delay_val = ducky_parse_line(bad_bt, bad_bt->line); - if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line - return 0; - } else if(delay_val == SCRIPT_STATE_STRING_START) { // Print string with delays - return delay_val; - } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // wait for button - return delay_val; - } else if(delay_val < 0) { - bad_bt->st.error_line = bad_bt->st.line_cur; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_bt->st.line_cur); - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_bt->defdelay); - } - } else { - furi_string_push_back(bad_bt->line, bad_bt->file_buf[i]); - } - } - bad_bt->buf_len = 0; - if(bad_bt->file_end) return SCRIPT_STATE_END; - } - - return 0; -} - -static void bad_bt_bt_hid_state_callback(BtStatus status, void* context) { - furi_assert(context); - BadBtScript* bad_bt = context; - bool state = (status == BtStatusConnected); - - if(state == true) { - LevelRssiRange r = bt_remote_rssi_range(bad_bt->bt); - if(r != LevelRssiError) { - bt_timeout = bt_hid_delays[r]; - } - furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtConnect); - } else { - furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtDisconnect); - } -} - -static uint32_t bad_bt_flags_get(uint32_t flags_mask, uint32_t timeout) { - uint32_t flags = furi_thread_flags_get(); - furi_check((flags & FuriFlagError) == 0); - if(flags == 0) { - flags = furi_thread_flags_wait(flags_mask, FuriFlagWaitAny, timeout); - furi_check(((flags & FuriFlagError) == 0) || (flags == (unsigned)FuriFlagErrorTimeout)); - } else { - uint32_t state = furi_thread_flags_clear(flags); - furi_check((state & FuriFlagError) == 0); - } - return flags; -} - -static int32_t bad_bt_worker(void* context) { - BadBtScript* bad_bt = context; - - BadBtWorkerState worker_state = BadBtStateInit; - int32_t delay_val = 0; - - FURI_LOG_I(WORKER_TAG, "Init"); - File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - bad_bt->line = furi_string_alloc(); - bad_bt->line_prev = furi_string_alloc(); - bad_bt->string_print = furi_string_alloc(); - - bt_set_status_changed_callback(bad_bt->bt, bad_bt_bt_hid_state_callback, bad_bt); - - while(1) { - if(worker_state == BadBtStateInit) { // State: initialization - if(storage_file_open( - script_file, - furi_string_get_cstr(bad_bt->file_path), - FSAM_READ, - FSOM_OPEN_EXISTING)) { - if((ducky_script_preload(bad_bt, script_file)) && (bad_bt->st.line_nb > 0)) { - if(furi_hal_bt_is_connected()) { - worker_state = BadBtStateIdle; // Ready to run - } else { - worker_state = BadBtStateNotConnected; // Not connected - } - - } else { - worker_state = BadBtStateScriptError; // Script preload error - } - } else { - FURI_LOG_E(WORKER_TAG, "File open error"); - worker_state = BadBtStateFileError; // File open error - } - bad_bt->st.state = worker_state; - - } else if(worker_state == BadBtStateNotConnected) { // State: Not connected - uint32_t flags = bad_bt_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, - FuriWaitForever); - - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { - worker_state = BadBtStateIdle; // Ready to run - } else if(flags & WorkerEvtStartStop) { - worker_state = BadBtStateWillRun; // Will run when connected - } - bad_bt->st.state = worker_state; - - } else if(worker_state == BadBtStateIdle) { // State: ready to start - uint32_t flags = bad_bt_flags_get( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect, - FuriWaitForever); - - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { // Start executing script - delay_val = 0; - bad_bt->buf_len = 0; - bad_bt->st.line_cur = 0; - bad_bt->defdelay = 0; - bad_bt->stringdelay = 0; - bad_bt->repeat_cnt = 0; - bad_bt->key_hold_nb = 0; - bad_bt->file_end = false; - storage_file_seek(script_file, 0, true); - bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout); - worker_state = BadBtStateRunning; - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBtStateNotConnected; // Disconnected - } - bad_bt->st.state = worker_state; - - } else if(worker_state == BadBtStateWillRun) { // State: start on connection - uint32_t flags = bad_bt_flags_get( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtDisconnect | WorkerEvtStartStop, - FuriWaitForever); - - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { // Start executing script - delay_val = 0; - bad_bt->buf_len = 0; - bad_bt->st.line_cur = 0; - bad_bt->defdelay = 0; - bad_bt->stringdelay = 0; - bad_bt->repeat_cnt = 0; - bad_bt->file_end = false; - storage_file_seek(script_file, 0, true); - // extra time for PC to recognize Flipper as keyboard - flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtDisconnect | WorkerEvtStartStop, - FuriFlagWaitAny | FuriFlagNoClear, - 1500); - if(flags == (unsigned)FuriFlagErrorTimeout) { - // If nothing happened - start script execution - worker_state = BadBtStateRunning; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadBtStateIdle; - furi_thread_flags_clear(WorkerEvtStartStop); - } - - update_bt_timeout(bad_bt->bt); - - bad_bt_script_set_keyboard_layout(bad_bt, bad_bt->keyboard_layout); - } else if(flags & WorkerEvtStartStop) { // Cancel scheduled execution - worker_state = BadBtStateNotConnected; - } - bad_bt->st.state = worker_state; - - } else if(worker_state == BadBtStateRunning) { // State: running - uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtConnect | WorkerEvtDisconnect, - FuriFlagWaitAny, - delay_cur); - - delay_val -= delay_cur; - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadBtStateIdle; // Stop executing script - - furi_hal_bt_hid_kb_release_all(); - - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBtStateNotConnected; // Disconnected - - furi_hal_bt_hid_kb_release_all(); - } - bad_bt->st.state = worker_state; - continue; - } else if( - (flags == (unsigned)FuriFlagErrorTimeout) || - (flags == (unsigned)FuriFlagErrorResource)) { - if(delay_val > 0) { - bad_bt->st.delay_remain--; - continue; - } - bad_bt->st.state = BadBtStateRunning; - delay_val = ducky_script_execute_next(bad_bt, script_file); - if(delay_val == SCRIPT_STATE_ERROR) { // Script error - delay_val = 0; - worker_state = BadBtStateScriptError; - bad_bt->st.state = worker_state; - - furi_hal_bt_hid_kb_release_all(); - - } else if(delay_val == SCRIPT_STATE_END) { // End of script - delay_val = 0; - worker_state = BadBtStateIdle; - bad_bt->st.state = BadBtStateDone; - - furi_hal_bt_hid_kb_release_all(); - - continue; - } else if(delay_val == SCRIPT_STATE_STRING_START) { // Start printing string with delays - delay_val = bad_bt->defdelay; - bad_bt->string_print_pos = 0; - worker_state = BadBtStateStringDelay; - } else if(delay_val == SCRIPT_STATE_WAIT_FOR_BTN) { // set state to wait for user input - worker_state = BadBtStateWaitForBtn; - bad_bt->st.state = BadBtStateWaitForBtn; // Show long delays - } else if(delay_val > 1000) { - bad_bt->st.state = BadBtStateDelay; // Show long delays - bad_bt->st.delay_remain = delay_val / 1000; - } - } else { - furi_check((flags & FuriFlagError) == 0); - } - } else if(worker_state == BadBtStateWaitForBtn) { // State: Wait for button Press - uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | - WorkerEvtDisconnect, - FuriFlagWaitAny, - delay_cur); - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - delay_val = 0; - worker_state = BadBtStateRunning; - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBtStateNotConnected; // Disconnected - furi_hal_hid_kb_release_all(); - } - bad_bt->st.state = worker_state; - continue; - } - } else if(worker_state == BadBtStateStringDelay) { // State: print string with delays - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtStartStop | WorkerEvtPauseResume | WorkerEvtConnect | - WorkerEvtDisconnect, - FuriFlagWaitAny, - bad_bt->stringdelay); - - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtStartStop) { - worker_state = BadBtStateIdle; // Stop executing script - - furi_hal_bt_hid_kb_release_all(); - - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBtStateNotConnected; // Disconnected - - furi_hal_bt_hid_kb_release_all(); - } - bad_bt->st.state = worker_state; - continue; - } else if( - (flags == (unsigned)FuriFlagErrorTimeout) || - (flags == (unsigned)FuriFlagErrorResource)) { - bool string_end = ducky_string_next(bad_bt); - if(string_end) { - bad_bt->stringdelay = 0; - worker_state = BadBtStateRunning; - } - } else { - furi_check((flags & FuriFlagError) == 0); - } - } else if( - (worker_state == BadBtStateFileError) || - (worker_state == BadBtStateScriptError)) { // State: error - uint32_t flags = - bad_bt_flags_get(WorkerEvtEnd, FuriWaitForever); // Waiting for exit command - - if(flags & WorkerEvtEnd) { - break; - } - } - - update_bt_timeout(bad_bt->bt); - } - - bt_set_status_changed_callback(bad_bt->bt, NULL, NULL); - - storage_file_close(script_file); - storage_file_free(script_file); - furi_string_free(bad_bt->line); - furi_string_free(bad_bt->line_prev); - furi_string_free(bad_bt->string_print); - - FURI_LOG_I(WORKER_TAG, "End"); - - return 0; -} - -static void bad_bt_script_set_default_keyboard_layout(BadBtScript* bad_bt) { - furi_assert(bad_bt); - furi_string_set_str(bad_bt->keyboard_layout, ""); - memset(bad_bt->layout, HID_KEYBOARD_NONE, sizeof(bad_bt->layout)); - memcpy(bad_bt->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_bt->layout))); -} - -BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app) { - furi_assert(file_path); - - BadBtScript* bad_bt = malloc(sizeof(BadBtScript)); - bad_bt->app = app; - bad_bt->file_path = furi_string_alloc(); - furi_string_set(bad_bt->file_path, file_path); - bad_bt->keyboard_layout = furi_string_alloc(); - bad_bt_script_set_default_keyboard_layout(bad_bt); - - bad_bt->st.state = BadBtStateInit; - bad_bt->st.error[0] = '\0'; - - bad_bt->bt = bt; - - bad_bt->thread = furi_thread_alloc_ex("BadBtWorker", 2048, bad_bt_worker, bad_bt); - furi_thread_start(bad_bt->thread); - return bad_bt; -} - -void bad_bt_script_close(BadBtScript* bad_bt) { - furi_assert(bad_bt); - furi_record_close(RECORD_STORAGE); - furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtEnd); - furi_thread_join(bad_bt->thread); - furi_thread_free(bad_bt->thread); - furi_string_free(bad_bt->file_path); - furi_string_free(bad_bt->keyboard_layout); - free(bad_bt); -} - -void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path) { - furi_assert(bad_bt); - - if((bad_bt->st.state == BadBtStateRunning) || (bad_bt->st.state == BadBtStateDelay)) { - // do not update keyboard layout while a script is running - return; - } - - File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(!furi_string_empty(layout_path)) { //-V1051 - furi_string_set(bad_bt->keyboard_layout, layout_path); - if(storage_file_open( - layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { - uint16_t layout[128]; - if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { - memcpy(bad_bt->layout, layout, sizeof(layout)); - } - } - storage_file_close(layout_file); - } else { - bad_bt_script_set_default_keyboard_layout(bad_bt); - } - storage_file_free(layout_file); -} - -void bad_bt_script_toggle(BadBtScript* bad_bt) { - furi_assert(bad_bt); - furi_thread_flags_set(furi_thread_get_id(bad_bt->thread), WorkerEvtStartStop); -} - -BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt) { - furi_assert(bad_bt); - return &(bad_bt->st); -} \ No newline at end of file diff --git a/applications/external/bad_bt/helpers/ducky_script.h b/applications/external/bad_bt/helpers/ducky_script.h deleted file mode 100644 index ea0f91040..000000000 --- a/applications/external/bad_bt/helpers/ducky_script.h +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include -#include "../views/bad_bt_view.h" - -#define FILE_BUFFER_LEN 16 - -typedef enum { - LevelRssi122_100, - LevelRssi99_80, - LevelRssi79_60, - LevelRssi59_40, - LevelRssi39_0, - LevelRssiNum, - LevelRssiError = 0xFF, -} LevelRssiRange; - -extern const uint8_t bt_hid_delays[LevelRssiNum]; - -extern uint8_t bt_timeout; - -typedef enum { - BadBtStateInit, - BadBtStateNotConnected, - BadBtStateIdle, - BadBtStateWillRun, - BadBtStateRunning, - BadBtStateDelay, - BadBtStateStringDelay, - BadBtStateWaitForBtn, - BadBtStateDone, - BadBtStateScriptError, - BadBtStateFileError, -} BadBtWorkerState; - -struct BadBtState { - BadBtWorkerState state; - uint32_t pin; - uint16_t line_cur; - uint16_t line_nb; - uint32_t delay_remain; - uint16_t error_line; - char error[64]; -}; - -typedef struct BadBtApp BadBtApp; - -typedef struct { - FuriHalUsbHidConfig hid_cfg; - FuriThread* thread; - BadBtState st; - - FuriString* file_path; - FuriString* keyboard_layout; - uint8_t file_buf[FILE_BUFFER_LEN + 1]; - uint8_t buf_start; - uint8_t buf_len; - bool file_end; - - uint32_t defdelay; - uint32_t stringdelay; - uint16_t layout[128]; - - FuriString* line; - FuriString* line_prev; - uint32_t repeat_cnt; - uint8_t key_hold_nb; - - bool set_usb_id; - bool set_bt_id; - bool has_usb_id; - bool has_bt_id; - - FuriString* string_print; - size_t string_print_pos; - - Bt* bt; - BadBtApp* app; -} BadBtScript; - -BadBtScript* bad_bt_script_open(FuriString* file_path, Bt* bt, BadBtApp* app); - -void bad_bt_script_close(BadBtScript* bad_bt); - -void bad_bt_script_set_keyboard_layout(BadBtScript* bad_bt, FuriString* layout_path); - -void bad_bt_script_start(BadBtScript* bad_bt); - -void bad_bt_script_stop(BadBtScript* bad_bt); - -void bad_bt_script_toggle(BadBtScript* bad_bt); - -BadBtState* bad_bt_script_get_state(BadBtScript* bad_bt); - -#define BAD_BT_ADV_NAME_MAX_LEN FURI_HAL_BT_ADV_NAME_LENGTH -#define BAD_BT_MAC_ADDRESS_LEN GAP_MAC_ADDR_SIZE - -// this is the MAC address used when we do not forget paired device (BOUND STATE) -extern const uint8_t BAD_BT_BOUND_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN]; -extern const uint8_t BAD_BT_EMPTY_MAC_ADDRESS[BAD_BT_MAC_ADDRESS_LEN]; - -typedef enum { - BadBtAppErrorNoFiles, - BadBtAppErrorCloseRpc, -} BadBtAppError; - -typedef struct { - char bt_name[BAD_BT_ADV_NAME_MAX_LEN]; - uint8_t bt_mac[BAD_BT_MAC_ADDRESS_LEN]; - GapPairing bt_mode; -} BadBtConfig; - -struct BadBtApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Widget* widget; - VariableItemList* var_item_list; - TextInput* text_input; - ByteInput* byte_input; - - BadBtAppError error; - FuriString* file_path; - FuriString* keyboard_layout; - BadBt* bad_bt_view; - BadBtScript* bad_bt_script; - - Bt* bt; - bool bt_remember; - BadBtConfig config; - BadBtConfig prev_config; - FuriThread* conn_init_thread; - FuriThread* switch_mode_thread; -}; - -int32_t bad_bt_config_switch_mode(BadBtApp* app); - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/bad_bt/helpers/ducky_script_commands.c b/applications/external/bad_bt/helpers/ducky_script_commands.c deleted file mode 100644 index eddab96ac..000000000 --- a/applications/external/bad_bt/helpers/ducky_script_commands.c +++ /dev/null @@ -1,201 +0,0 @@ -#include -#include -#include "ducky_script.h" -#include "ducky_script_i.h" - -typedef int32_t (*DuckyCmdCallback)(BadBtScript* bad_bt, const char* line, int32_t param); - -typedef struct { - char* name; - DuckyCmdCallback callback; - int32_t param; -} DuckyCmd; - -static int32_t ducky_fnc_delay(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint32_t delay_val = 0; - bool state = ducky_get_number(line, &delay_val); - if((state) && (delay_val > 0)) { - return (int32_t)delay_val; - } - - return ducky_error(bad_bt, "Invalid number %s", line); -} - -static int32_t ducky_fnc_defdelay(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_bt->defdelay); - if(!state) { - return ducky_error(bad_bt, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_strdelay(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_bt->stringdelay); - if(!state) { - return ducky_error(bad_bt, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_string(BadBtScript* bad_bt, const char* line, int32_t param) { - line = &line[ducky_get_command_len(line) + 1]; - furi_string_set_str(bad_bt->string_print, line); - if(param == 1) { - furi_string_cat(bad_bt->string_print, "\n"); - } - - if(bad_bt->stringdelay == 0) { // stringdelay not set - run command immidiately - bool state = ducky_string(bad_bt, furi_string_get_cstr(bad_bt->string_print)); - if(!state) { - return ducky_error(bad_bt, "Invalid string %s", line); - } - } else { // stringdelay is set - run command in thread to keep handling external events - return SCRIPT_STATE_STRING_START; - } - - return 0; -} - -static int32_t ducky_fnc_repeat(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - bool state = ducky_get_number(line, &bad_bt->repeat_cnt); - if((!state) || (bad_bt->repeat_cnt == 0)) { - return ducky_error(bad_bt, "Invalid number %s", line); - } - return 0; -} - -static int32_t ducky_fnc_sysrq(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_bt, line, true); - - furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); - furi_hal_bt_hid_kb_press(key); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); - return 0; -} - -static int32_t ducky_fnc_altchar(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - ducky_numlock_on(bad_bt); - bool state = ducky_altchar(bad_bt, line); - if(!state) { - return ducky_error(bad_bt, "Invalid altchar %s", line); - } - return 0; -} - -static int32_t ducky_fnc_altstring(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - ducky_numlock_on(bad_bt); - bool state = ducky_altstring(bad_bt, line); - if(!state) { - return ducky_error(bad_bt, "Invalid altstring %s", line); - } - return 0; -} - -static int32_t ducky_fnc_hold(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_bt, line, true); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_bt, "No keycode defined for %s", line); - } - bad_bt->key_hold_nb++; - if(bad_bt->key_hold_nb > (HID_KB_MAX_KEYS - 1)) { - return ducky_error(bad_bt, "Too many keys are hold"); - } - furi_hal_bt_hid_kb_press(key); - - return 0; -} - -static int32_t ducky_fnc_release(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - - line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_bt, line, true); - if(key == HID_KEYBOARD_NONE) { - return ducky_error(bad_bt, "No keycode defined for %s", line); - } - if(bad_bt->key_hold_nb == 0) { - return ducky_error(bad_bt, "No keys are hold"); - } - bad_bt->key_hold_nb--; - furi_hal_bt_hid_kb_release(key); - return 0; -} - -static int32_t ducky_fnc_waitforbutton(BadBtScript* bad_bt, const char* line, int32_t param) { - UNUSED(param); - UNUSED(bad_bt); - UNUSED(line); - - return SCRIPT_STATE_WAIT_FOR_BTN; -} - -static const DuckyCmd ducky_commands[] = { - {"REM", NULL, -1}, - {"ID", NULL, -1}, - {"BT_ID", NULL, -1}, - {"DELAY", ducky_fnc_delay, -1}, - {"STRING", ducky_fnc_string, 0}, - {"STRINGLN", ducky_fnc_string, 1}, - {"DEFAULT_DELAY", ducky_fnc_defdelay, -1}, - {"DEFAULTDELAY", ducky_fnc_defdelay, -1}, - {"STRINGDELAY", ducky_fnc_strdelay, -1}, - {"STRING_DELAY", ducky_fnc_strdelay, -1}, - {"REPEAT", ducky_fnc_repeat, -1}, - {"SYSRQ", ducky_fnc_sysrq, -1}, - {"ALTCHAR", ducky_fnc_altchar, -1}, - {"ALTSTRING", ducky_fnc_altstring, -1}, - {"ALTCODE", ducky_fnc_altstring, -1}, - {"HOLD", ducky_fnc_hold, -1}, - {"RELEASE", ducky_fnc_release, -1}, - {"WAIT_FOR_BUTTON_PRESS", ducky_fnc_waitforbutton, -1}, -}; - -#define TAG "BadBT" -#define WORKER_TAG TAG "Worker" - -int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line) { - size_t cmd_word_len = strcspn(line, " "); - for(size_t i = 0; i < COUNT_OF(ducky_commands); i++) { - size_t cmd_compare_len = strlen(ducky_commands[i].name); - - if(cmd_compare_len != cmd_word_len) { - continue; - } - - if(strncmp(line, ducky_commands[i].name, cmd_compare_len) == 0) { - if(ducky_commands[i].callback == NULL) { - return 0; - } else { - return ((ducky_commands[i].callback)(bad_bt, line, ducky_commands[i].param)); - } - } - } - - return SCRIPT_STATE_CMD_UNKNOWN; -} diff --git a/applications/external/bad_bt/helpers/ducky_script_i.h b/applications/external/bad_bt/helpers/ducky_script_i.h deleted file mode 100644 index 08afa65a4..000000000 --- a/applications/external/bad_bt/helpers/ducky_script_i.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "ducky_script.h" - -#define SCRIPT_STATE_ERROR (-1) -#define SCRIPT_STATE_END (-2) -#define SCRIPT_STATE_NEXT_LINE (-3) -#define SCRIPT_STATE_CMD_UNKNOWN (-4) -#define SCRIPT_STATE_STRING_START (-5) -#define SCRIPT_STATE_WAIT_FOR_BTN (-6) - -uint16_t ducky_get_keycode(BadBtScript* bad_bt, const char* param, bool accept_chars); - -uint32_t ducky_get_command_len(const char* line); - -bool ducky_is_line_end(const char chr); - -uint16_t ducky_get_keycode_by_name(const char* param); - -bool ducky_get_number(const char* param, uint32_t* val); - -void ducky_numlock_on(BadBtScript* bad_bt); - -bool ducky_numpad_press(BadBtScript* bad_bt, const char num); - -bool ducky_altchar(BadBtScript* bad_bt, const char* charcode); - -bool ducky_altstring(BadBtScript* bad_bt, const char* param); - -bool ducky_string(BadBtScript* bad_bt, const char* param); - -int32_t ducky_execute_cmd(BadBtScript* bad_bt, const char* line); - -int32_t ducky_error(BadBtScript* bad_bt, const char* text, ...); - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/bad_bt/helpers/ducky_script_keycodes.c b/applications/external/bad_bt/helpers/ducky_script_keycodes.c deleted file mode 100644 index 55c52810f..000000000 --- a/applications/external/bad_bt/helpers/ducky_script_keycodes.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include "ducky_script_i.h" - -typedef struct { - char* name; - uint16_t keycode; -} DuckyKey; - -static const DuckyKey ducky_keys[] = { - {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, - {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, - {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, - {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, - {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, - {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL}, - - {"CTRL", KEY_MOD_LEFT_CTRL}, - {"CONTROL", KEY_MOD_LEFT_CTRL}, - {"SHIFT", KEY_MOD_LEFT_SHIFT}, - {"ALT", KEY_MOD_LEFT_ALT}, - {"GUI", KEY_MOD_LEFT_GUI}, - {"WINDOWS", KEY_MOD_LEFT_GUI}, - - {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, - {"DOWN", HID_KEYBOARD_DOWN_ARROW}, - {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, - {"LEFT", HID_KEYBOARD_LEFT_ARROW}, - {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW}, - {"RIGHT", HID_KEYBOARD_RIGHT_ARROW}, - {"UPARROW", HID_KEYBOARD_UP_ARROW}, - {"UP", HID_KEYBOARD_UP_ARROW}, - - {"ENTER", HID_KEYBOARD_RETURN}, - {"BREAK", HID_KEYBOARD_PAUSE}, - {"PAUSE", HID_KEYBOARD_PAUSE}, - {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, - {"DELETE", HID_KEYBOARD_DELETE_FORWARD}, - {"BACKSPACE", HID_KEYBOARD_DELETE}, - {"END", HID_KEYBOARD_END}, - {"ESC", HID_KEYBOARD_ESCAPE}, - {"ESCAPE", HID_KEYBOARD_ESCAPE}, - {"HOME", HID_KEYBOARD_HOME}, - {"INSERT", HID_KEYBOARD_INSERT}, - {"NUMLOCK", HID_KEYPAD_NUMLOCK}, - {"PAGEUP", HID_KEYBOARD_PAGE_UP}, - {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, - {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, - {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, - {"SPACE", HID_KEYBOARD_SPACEBAR}, - {"TAB", HID_KEYBOARD_TAB}, - {"MENU", HID_KEYBOARD_APPLICATION}, - {"APP", HID_KEYBOARD_APPLICATION}, - - {"F1", HID_KEYBOARD_F1}, - {"F2", HID_KEYBOARD_F2}, - {"F3", HID_KEYBOARD_F3}, - {"F4", HID_KEYBOARD_F4}, - {"F5", HID_KEYBOARD_F5}, - {"F6", HID_KEYBOARD_F6}, - {"F7", HID_KEYBOARD_F7}, - {"F8", HID_KEYBOARD_F8}, - {"F9", HID_KEYBOARD_F9}, - {"F10", HID_KEYBOARD_F10}, - {"F11", HID_KEYBOARD_F11}, - {"F12", HID_KEYBOARD_F12}, -}; - -uint16_t ducky_get_keycode_by_name(const char* param) { - for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) { - size_t key_cmd_len = strlen(ducky_keys[i].name); - if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && - (ducky_is_line_end(param[key_cmd_len]))) { - return ducky_keys[i].keycode; - } - } - - return HID_KEYBOARD_NONE; -} diff --git a/applications/external/bad_bt/images/badbt_10px.png b/applications/external/bad_bt/images/badbt_10px.png deleted file mode 100644 index 037474aa3bc9c2e1aca79a68483e69980432bcf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 576 zcmV-G0>AxEX>4Tx04R}tkv&MmKpe$i(`rSk4t5Z6$WWau6cusQDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yan9G%FATG`(u3 z5^*t;T@{0`5D-8=V(6BcWz0!Z5}xDh9zMR_MR}I@xj#prnzI<-6NzV;VOEJZh^IHJ z2Iqa^Fe}O`@j3ChNf#u3C`-Nm{=@yu+qV-Xlle$#1U1~DPPFA zta9Gstd(o5bx;1nP)=W2<~q$0B(R7jND!f*h7!uCB1)@HiiH&I$36VRj$a~|Laq`R zITlcX2HEk0|H1EWt^DMKn-q!zT`#u%F$x5Cfo9#dzmILZc>?&Kfh)c3uQY&}Ptxmc zEph}5Yy%h9ZB5w&E_Z;TCqp)6NAlAY@_FF>jJ_!g4Bi60Yi@6?eVjf3Y3eF@0~{Oz zV+G1y_jq?tXK(+WY4!I5C=YUpXXIhH00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru<^lu`HWXkp{t5s906j@WK~xyijZi@f05Awj>HlAL zr$MwDdI>{Qf+U53tOUR#xOeyy)jcQo#JNRv)7r6DVVK|+*(cmT+R+EbO(O#X#REG4 O0000 - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) BadBtScene##id, -typedef enum { -#include "bad_bt_scene_config.h" - BadBtSceneNum, -} BadBtScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers bad_bt_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "bad_bt_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "bad_bt_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "bad_bt_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config.c b/applications/external/bad_bt/scenes/bad_bt_scene_config.c deleted file mode 100644 index 5fc9c0012..000000000 --- a/applications/external/bad_bt/scenes/bad_bt_scene_config.c +++ /dev/null @@ -1,104 +0,0 @@ -#include "../bad_bt_app.h" -#include "../helpers/ducky_script.h" -#include "furi_hal_power.h" - -enum VarItemListIndex { - VarItemListIndexKeyboardLayout, - VarItemListIndexBtRemember, - VarItemListIndexBtDeviceName, - VarItemListIndexBtMacAddress, - VarItemListIndexRandomizeBtMac, -}; - -void bad_bt_scene_config_bt_remember_callback(VariableItem* item) { - BadBtApp* bad_bt = variable_item_get_context(item); - bad_bt->bt_remember = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF"); - view_dispatcher_send_custom_event(bad_bt->view_dispatcher, VarItemListIndexBtRemember); -} - -void bad_bt_scene_config_var_item_list_callback(void* context, uint32_t index) { - BadBtApp* bad_bt = context; - view_dispatcher_send_custom_event(bad_bt->view_dispatcher, index); -} - -void bad_bt_scene_config_on_enter(void* context) { - BadBtApp* bad_bt = context; - VariableItemList* var_item_list = bad_bt->var_item_list; - VariableItem* item; - - item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_bt); - - item = variable_item_list_add( - var_item_list, "BT Remember", 2, bad_bt_scene_config_bt_remember_callback, bad_bt); - variable_item_set_current_value_index(item, bad_bt->bt_remember); - variable_item_set_current_value_text(item, bad_bt->bt_remember ? "ON" : "OFF"); - - item = variable_item_list_add(var_item_list, "BT Device Name", 0, NULL, bad_bt); - if(bad_bt->bad_bt_script->set_bt_id) { - variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset Name!"); - } - - item = variable_item_list_add(var_item_list, "BT MAC Address", 0, NULL, bad_bt); - if(bad_bt->bt_remember) { - variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } else if(bad_bt->bad_bt_script->set_bt_id) { - variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); - } - - item = variable_item_list_add(var_item_list, "Randomize BT MAC", 0, NULL, bad_bt); - if(bad_bt->bt_remember) { - variable_item_set_locked(item, true, "Remember\nmust be Off!"); - } else if(bad_bt->bad_bt_script->set_bt_id) { - variable_item_set_locked(item, true, "Script has\nBT_ID cmd!\nLocked to\nset MAC!"); - } - - variable_item_list_set_enter_callback( - var_item_list, bad_bt_scene_config_var_item_list_callback, bad_bt); - - variable_item_list_set_selected_item( - var_item_list, scene_manager_get_scene_state(bad_bt->scene_manager, BadBtSceneConfig)); - - view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfig); -} - -bool bad_bt_scene_config_on_event(void* context, SceneManagerEvent event) { - BadBtApp* bad_bt = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(bad_bt->scene_manager, BadBtSceneConfig, event.event); - consumed = true; - switch(event.event) { - case VarItemListIndexKeyboardLayout: - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigLayout); - break; - case VarItemListIndexBtRemember: - bad_bt_config_switch_remember_mode(bad_bt); - scene_manager_previous_scene(bad_bt->scene_manager); - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfig); - break; - case VarItemListIndexBtDeviceName: - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigName); - break; - case VarItemListIndexBtMacAddress: - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneConfigMac); - break; - case VarItemListIndexRandomizeBtMac: - furi_hal_random_fill_buf(bad_bt->config.bt_mac, BAD_BT_MAC_ADDRESS_LEN); - bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac); - break; - default: - break; - } - } - - return consumed; -} - -void bad_bt_scene_config_on_exit(void* context) { - BadBtApp* bad_bt = context; - VariableItemList* var_item_list = bad_bt->var_item_list; - - variable_item_list_reset(var_item_list); -} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config.h b/applications/external/bad_bt/scenes/bad_bt_scene_config.h deleted file mode 100644 index f7914e6dd..000000000 --- a/applications/external/bad_bt/scenes/bad_bt_scene_config.h +++ /dev/null @@ -1,7 +0,0 @@ -ADD_SCENE(bad_bt, file_select, FileSelect) -ADD_SCENE(bad_bt, work, Work) -ADD_SCENE(bad_bt, error, Error) -ADD_SCENE(bad_bt, config, Config) -ADD_SCENE(bad_bt, config_layout, ConfigLayout) -ADD_SCENE(bad_bt, config_name, ConfigName) -ADD_SCENE(bad_bt, config_mac, ConfigMac) diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config_layout.c b/applications/external/bad_bt/scenes/bad_bt_scene_config_layout.c deleted file mode 100644 index b0ce2d084..000000000 --- a/applications/external/bad_bt/scenes/bad_bt_scene_config_layout.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "../bad_bt_app.h" -#include "furi_hal_power.h" -#include - -static bool bad_bt_layout_select(BadBtApp* bad_bt) { - furi_assert(bad_bt); - - FuriString* predefined_path; - predefined_path = furi_string_alloc(); - if(!furi_string_empty(bad_bt->keyboard_layout)) { - furi_string_set(predefined_path, bad_bt->keyboard_layout); - } else { - furi_string_set(predefined_path, BAD_BT_APP_PATH_LAYOUT_FOLDER); - } - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_BT_APP_LAYOUT_EXTENSION, &I_keyboard_10px); - browser_options.base_path = BAD_BT_APP_PATH_LAYOUT_FOLDER; - browser_options.skip_assets = false; - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_bt->dialogs, bad_bt->keyboard_layout, predefined_path, &browser_options); - - furi_string_free(predefined_path); - return res; -} - -void bad_bt_scene_config_layout_on_enter(void* context) { - BadBtApp* bad_bt = context; - - if(bad_bt_layout_select(bad_bt)) { - bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout); - } - scene_manager_previous_scene(bad_bt->scene_manager); -} - -bool bad_bt_scene_config_layout_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void bad_bt_scene_config_layout_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config_mac.c b/applications/external/bad_bt/scenes/bad_bt_scene_config_mac.c deleted file mode 100644 index dcc783f0f..000000000 --- a/applications/external/bad_bt/scenes/bad_bt_scene_config_mac.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "../bad_bt_app.h" - -#define TAG "BadBtConfigMac" - -void bad_bt_scene_config_mac_byte_input_callback(void* context) { - BadBtApp* bad_bt = context; - - view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventByteInputDone); -} - -void bad_bt_scene_config_mac_on_enter(void* context) { - BadBtApp* bad_bt = context; - - furi_hal_bt_reverse_mac_addr(bad_bt->config.bt_mac); - - // Setup view - ByteInput* byte_input = bad_bt->byte_input; - byte_input_set_header_text(byte_input, "Set BT MAC address"); - byte_input_set_result_callback( - byte_input, - bad_bt_scene_config_mac_byte_input_callback, - NULL, - bad_bt, - bad_bt->config.bt_mac, - GAP_MAC_ADDR_SIZE); - view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigMac); -} - -bool bad_bt_scene_config_mac_on_event(void* context, SceneManagerEvent event) { - BadBtApp* bad_bt = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadBtAppCustomEventByteInputDone) { - scene_manager_previous_scene(bad_bt->scene_manager); - consumed = true; - } - } - return consumed; -} - -void bad_bt_scene_config_mac_on_exit(void* context) { - BadBtApp* bad_bt = context; - - furi_hal_bt_reverse_mac_addr(bad_bt->config.bt_mac); - - bt_set_profile_mac_address(bad_bt->bt, bad_bt->config.bt_mac); - - // Clear view - byte_input_set_result_callback(bad_bt->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(bad_bt->byte_input, ""); -} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_config_name.c b/applications/external/bad_bt/scenes/bad_bt_scene_config_name.c deleted file mode 100644 index 61d198b8c..000000000 --- a/applications/external/bad_bt/scenes/bad_bt_scene_config_name.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "../bad_bt_app.h" - -static void bad_bt_scene_config_name_text_input_callback(void* context) { - BadBtApp* bad_bt = context; - - view_dispatcher_send_custom_event(bad_bt->view_dispatcher, BadBtAppCustomEventTextEditResult); -} - -void bad_bt_scene_config_name_on_enter(void* context) { - BadBtApp* bad_bt = context; - TextInput* text_input = bad_bt->text_input; - - text_input_set_header_text(text_input, "Set BT device name"); - - text_input_set_result_callback( - text_input, - bad_bt_scene_config_name_text_input_callback, - bad_bt, - bad_bt->config.bt_name, - BAD_BT_ADV_NAME_MAX_LEN, - true); - - view_dispatcher_switch_to_view(bad_bt->view_dispatcher, BadBtAppViewConfigName); -} - -bool bad_bt_scene_config_name_on_event(void* context, SceneManagerEvent event) { - BadBtApp* bad_bt = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == BadBtAppCustomEventTextEditResult) { - bt_set_profile_adv_name(bad_bt->bt, bad_bt->config.bt_name); - } - scene_manager_previous_scene(bad_bt->scene_manager); - } - return consumed; -} - -void bad_bt_scene_config_name_on_exit(void* context) { - BadBtApp* bad_bt = context; - TextInput* text_input = bad_bt->text_input; - - text_input_reset(text_input); -} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_error.c b/applications/external/bad_bt/scenes/bad_bt_scene_error.c deleted file mode 100644 index e25703e7d..000000000 --- a/applications/external/bad_bt/scenes/bad_bt_scene_error.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "../bad_bt_app.h" - -static void - bad_bt_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - BadBtApp* app = context; - - if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(app->view_dispatcher, BadBtCustomEventErrorBack); - } -} - -void bad_bt_scene_error_on_enter(void* context) { - BadBtApp* app = context; - - if(app->error == BadBtAppErrorNoFiles) { - widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); - widget_add_string_multiline_element( - app->widget, - 81, - 4, - AlignCenter, - AlignTop, - FontSecondary, - "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", bad_bt_scene_error_event_callback, app); - } else if(app->error == BadBtAppErrorCloseRpc) { - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } - - view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewError); -} - -bool bad_bt_scene_error_on_event(void* context, SceneManagerEvent event) { - BadBtApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadBtCustomEventErrorBack) { - view_dispatcher_stop(app->view_dispatcher); - consumed = true; - } - } - return consumed; -} - -void bad_bt_scene_error_on_exit(void* context) { - BadBtApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_file_select.c b/applications/external/bad_bt/scenes/bad_bt_scene_file_select.c deleted file mode 100644 index b86dc6d71..000000000 --- a/applications/external/bad_bt/scenes/bad_bt_scene_file_select.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "../bad_bt_app.h" -#include -#include - -static bool bad_bt_file_select(BadBtApp* bad_bt) { - furi_assert(bad_bt); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_BT_APP_SCRIPT_EXTENSION, &I_badbt_10px); - browser_options.base_path = BAD_BT_APP_BASE_FOLDER; - browser_options.skip_assets = true; - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_bt->dialogs, bad_bt->file_path, bad_bt->file_path, &browser_options); - - return res; -} - -void bad_bt_scene_file_select_on_enter(void* context) { - BadBtApp* bad_bt = context; - - if(bad_bt->bad_bt_script) { - bad_bt_script_close(bad_bt->bad_bt_script); - bad_bt->bad_bt_script = NULL; - } - - if(bad_bt_file_select(bad_bt)) { - bad_bt->bad_bt_script = bad_bt_script_open(bad_bt->file_path, bad_bt->bt, bad_bt); - bad_bt_script_set_keyboard_layout(bad_bt->bad_bt_script, bad_bt->keyboard_layout); - - scene_manager_next_scene(bad_bt->scene_manager, BadBtSceneWork); - } else { - view_dispatcher_stop(bad_bt->view_dispatcher); - } -} - -bool bad_bt_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - // BadBtApp* bad_bt = context; - return false; -} - -void bad_bt_scene_file_select_on_exit(void* context) { - UNUSED(context); - // BadBtApp* bad_bt = context; -} diff --git a/applications/external/bad_bt/scenes/bad_bt_scene_work.c b/applications/external/bad_bt/scenes/bad_bt_scene_work.c deleted file mode 100644 index 684bb8b74..000000000 --- a/applications/external/bad_bt/scenes/bad_bt_scene_work.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "../helpers/ducky_script.h" -#include "../bad_bt_app.h" -#include "../views/bad_bt_view.h" -#include -#include "toolbox/path.h" - -void bad_bt_scene_work_button_callback(InputKey key, void* context) { - furi_assert(context); - BadBtApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, key); -} - -bool bad_bt_scene_work_on_event(void* context, SceneManagerEvent event) { - BadBtApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InputKeyLeft) { - if(bad_bt_is_idle_state(app->bad_bt_view)) { - scene_manager_next_scene(app->scene_manager, BadBtSceneConfig); - } - consumed = true; - } else if(event.event == InputKeyOk) { - bad_bt_script_toggle(app->bad_bt_script); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script)); - } - return consumed; -} - -void bad_bt_scene_work_on_enter(void* context) { - BadBtApp* app = context; - - FuriString* file_name; - file_name = furi_string_alloc(); - path_extract_filename(app->file_path, file_name, true); - bad_bt_set_file_name(app->bad_bt_view, furi_string_get_cstr(file_name)); - furi_string_free(file_name); - - FuriString* layout; - layout = furi_string_alloc(); - path_extract_filename(app->keyboard_layout, layout, true); - bad_bt_set_layout(app->bad_bt_view, furi_string_get_cstr(layout)); - furi_string_free(layout); - - bad_bt_set_state(app->bad_bt_view, bad_bt_script_get_state(app->bad_bt_script)); - - bad_bt_set_button_callback(app->bad_bt_view, bad_bt_scene_work_button_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, BadBtAppViewWork); -} - -void bad_bt_scene_work_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/external/bad_bt/views/bad_bt_view.c b/applications/external/bad_bt/views/bad_bt_view.c deleted file mode 100644 index 4a9bf589c..000000000 --- a/applications/external/bad_bt/views/bad_bt_view.c +++ /dev/null @@ -1,233 +0,0 @@ -#include "bad_bt_view.h" -#include "../helpers/ducky_script.h" -#include "../bad_bt_app.h" -#include -#include -#include - -#define MAX_NAME_LEN 64 - -typedef struct { - char file_name[MAX_NAME_LEN]; - char layout[MAX_NAME_LEN]; - BadBtState state; - uint8_t anim_frame; -} BadBtModel; - -static void bad_bt_draw_callback(Canvas* canvas, void* _model) { - BadBtModel* model = _model; - - FuriString* disp_str; - disp_str = furi_string_alloc_set("(BT) "); - furi_string_cat_str(disp_str, model->file_name); - elements_string_fit_width(canvas, disp_str, 128 - 2); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); - - if(strlen(model->layout) == 0) { - furi_string_set(disp_str, "(default)"); - } else { - furi_string_reset(disp_str); - furi_string_push_back(disp_str, '('); - for(size_t i = 0; i < strlen(model->layout); i++) - furi_string_push_back(disp_str, model->layout[i]); - furi_string_push_back(disp_str, ')'); - } - if(model->state.pin) { - furi_string_cat_printf(disp_str, " PIN: %ld", model->state.pin); - } - elements_string_fit_width(canvas, disp_str, 128 - 2); - canvas_draw_str( - canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); - - furi_string_reset(disp_str); - - if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) || - (model->state.state == BadBtStateNotConnected)) { - elements_button_center(canvas, "Run"); - elements_button_left(canvas, "Config"); - } else if((model->state.state == BadBtStateRunning) || (model->state.state == BadBtStateDelay)) { - elements_button_center(canvas, "Stop"); - } else if(model->state.state == BadBtStateWaitForBtn) { - elements_button_center(canvas, "Press to continue"); - } else if(model->state.state == BadBtStateWillRun) { - elements_button_center(canvas, "Cancel"); - } - - if(model->state.state == BadBtStateNotConnected) { - canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device"); - } else if(model->state.state == BadBtStateWillRun) { - canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); - } else if(model->state.state == BadBtStateFileError) { - canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); - } else if(model->state.state == BadBtStateScriptError) { - canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); - canvas_set_font(canvas, FontSecondary); - furi_string_printf(disp_str, "line %u", model->state.error_line); - canvas_draw_str_aligned( - canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - furi_string_set_str(disp_str, model->state.error); - elements_string_fit_width(canvas, disp_str, canvas_width(canvas)); - canvas_draw_str_aligned( - canvas, 127, 56, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - } else if(model->state.state == BadBtStateIdle) { - canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadBtStateRunning) { - if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); - } else { - canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); - } - canvas_set_font(canvas, FontBigNumbers); - furi_string_printf( - disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); - canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadBtStateDone) { - canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadBtStateDelay) { - if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); - } else { - canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); - } - canvas_set_font(canvas, FontBigNumbers); - furi_string_printf( - disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); - canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); - canvas_draw_str_aligned( - canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - } else { - canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); - } - - furi_string_free(disp_str); -} - -static bool bad_bt_input_callback(InputEvent* event, void* context) { - furi_assert(context); - BadBt* bad_bt = context; - bool consumed = false; - - if(event->type == InputTypeShort) { - if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { - consumed = true; - furi_assert(bad_bt->callback); - bad_bt->callback(event->key, bad_bt->context); - } - } - - return consumed; -} - -BadBt* bad_bt_alloc() { - BadBt* bad_bt = malloc(sizeof(BadBt)); - - bad_bt->view = view_alloc(); - view_allocate_model(bad_bt->view, ViewModelTypeLocking, sizeof(BadBtModel)); - view_set_context(bad_bt->view, bad_bt); - view_set_draw_callback(bad_bt->view, bad_bt_draw_callback); - view_set_input_callback(bad_bt->view, bad_bt_input_callback); - - return bad_bt; -} - -void bad_bt_free(BadBt* bad_bt) { - furi_assert(bad_bt); - view_free(bad_bt->view); - free(bad_bt); -} - -View* bad_bt_get_view(BadBt* bad_bt) { - furi_assert(bad_bt); - return bad_bt->view; -} - -void bad_bt_set_button_callback(BadBt* bad_bt, BadBtButtonCallback callback, void* context) { - furi_assert(bad_bt); - furi_assert(callback); - with_view_model( - bad_bt->view, - BadBtModel * model, - { - UNUSED(model); - bad_bt->callback = callback; - bad_bt->context = context; - }, - true); -} - -void bad_bt_set_file_name(BadBt* bad_bt, const char* name) { - furi_assert(name); - with_view_model( - bad_bt->view, BadBtModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true); -} - -void bad_bt_set_layout(BadBt* bad_bt, const char* layout) { - furi_assert(layout); - with_view_model( - bad_bt->view, BadBtModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true); -} - -void bad_bt_set_state(BadBt* bad_bt, BadBtState* st) { - furi_assert(st); - uint32_t pin = 0; - if(bad_bt->context != NULL) { - BadBtApp* app = bad_bt->context; - if(app->bt != NULL) { - pin = app->bt->pin; - } - } - st->pin = pin; - with_view_model( - bad_bt->view, - BadBtModel * model, - { - memcpy(&(model->state), st, sizeof(BadBtState)); - model->anim_frame ^= 1; - }, - true); -} - -bool bad_bt_is_idle_state(BadBt* bad_bt) { - bool is_idle = false; - with_view_model( - bad_bt->view, - BadBtModel * model, - { - if((model->state.state == BadBtStateIdle) || (model->state.state == BadBtStateDone) || - (model->state.state == BadBtStateNotConnected)) { - is_idle = true; - } - }, - false); - return is_idle; -} diff --git a/applications/external/bad_bt/views/bad_bt_view.h b/applications/external/bad_bt/views/bad_bt_view.h deleted file mode 100644 index 850a71057..000000000 --- a/applications/external/bad_bt/views/bad_bt_view.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -typedef void (*BadBtButtonCallback)(InputKey key, void* context); - -typedef struct { - View* view; - BadBtButtonCallback callback; - void* context; -} BadBt; - -typedef struct BadBtState BadBtState; - -BadBt* bad_bt_alloc(); - -void bad_bt_free(BadBt* bad_bt); - -View* bad_bt_get_view(BadBt* bad_bt); - -void bad_bt_set_button_callback(BadBt* bad_bt, BadBtButtonCallback callback, void* context); - -void bad_bt_set_file_name(BadBt* bad_bt, const char* name); - -void bad_bt_set_layout(BadBt* bad_bt, const char* layout); - -void bad_bt_set_state(BadBt* bad_bt, BadBtState* st); - -bool bad_bt_is_idle_state(BadBt* bad_bt); diff --git a/applications/external/barcode_gen/LICENSE b/applications/external/barcode_gen/LICENSE deleted file mode 100644 index 4c02d8221..000000000 --- a/applications/external/barcode_gen/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ - -MIT License - -Copyright (c) 2023 Alan Tsui - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/applications/external/barcode_gen/README.md b/applications/external/barcode_gen/README.md deleted file mode 100644 index ec944cb26..000000000 --- a/applications/external/barcode_gen/README.md +++ /dev/null @@ -1,88 +0,0 @@ -

-

Barcode Generator

-

- -A barcode generator for the Flipper Zero that supports **UPC-A**, **EAN-8**, **EAN-13**, **Code-39**, **Codabar**, and **Code-128**[1] -

- -Note: Barcode save locations have been moved from `/barcodes` to `/apps_data/barcodes` - -## Table of Contents -- [Table of Contents](#table-of-contents) -- [Installing](#installing) -- [Building](#building) -- [Usage](#usage) - - [Creating a barcode](#creating-a-barcode) - - [Editing a barcode](#editing-a-barcode) - - [Deleting a barcode](#deleting-a-barcode) - - [Viewing a barcode](#viewing-a-barcode) -- [Screenshots](#screenshots) -- [Credits](#credits) - - -## Installing -1) Download the `.zip` file from the release section -2) Extract/unzip the `.zip` file onto your computer -3) Open qFlipper and go to the file manager -4) Navigate to the `apps` folder -5) Drag & drop the `.fap` file into the `apps` folder -6) Navigate back to the root folder of the SD card and create the folder `apps_data`, if not already there -7) Navigate into `apps_data` and create another folder called `barcode_data` -8) Navigate into `barcode_data` -9) Drag & drop the encoding txts (`code39_encodings.txt`, `code128_encodings.txt` & `codabar_encodings.txt`) into the `barcode_data` folder - -## Building -1) Clone the [flipperzero-firmware](https://github.com/flipperdevices/flipperzero-firmware) repository or a firmware of your choice -2) Clone this repository and put it in the `applications_user` folder -3) Build this app by using the command `./fbt fap_Barcode_App` -4) Copy the `.fap` from `build\f7-firmware-D\.extapps\Barcode_App.fap` to `apps\Misc` using the qFlipper app -5) While still in the qFlipper app, navigate to the root folder of the SD card and create the folder `apps_data`, if not already there -6) Navigate into `apps_data` and create another folder called `barcode_data` -7) Navigate into `barcode_data` -8) Drag & drop the encoding txts (`code39_encodings.txt`, `code128_encodings.txt` & `codabar_encodings.txt`) from the `encoding_tables` folder in this repository into the `barcode_data` folder - -## Usage - -### Creating a barcode -1) To create a barcode click on `Create Barcode` -2) Next select your type using the left and right arrows -3) Enter your filename and then your barcode data -4) Click save - -**Note**: For Codabar barcodes, you must manually add the start and stop codes to the barcode data -Start/Stop codes can be A, B, C, or D -For example, if you wanted to represent `1234` as a barcode you will need to enter something like `A1234A`. (You can replace the letters A with either A, B, C, or D) - -![Codabar Data Example](screenshots/Codabar%20Data%20Example.png "Codabar Data Example") - -### Editing a barcode -1) To edit a barcode click on `Edit Barcode` -2) Next select the barcode file you want to edit -3) Edit the type, name, or data -4) Click save - -### Deleting a barcode -1) To delete a barcode click on `Edit Barcode` -2) Next select the barcode file you want to delete -3) Scroll all the way to the bottom -4) Click delete - -### Viewing a barcode -1) To view a barcode click on `Load Barcode` -2) Next select the barcode file you want to view - -## Screenshots -![Barcode Create Screen](screenshots/Creating%20Barcode.png "Barcode Create Screen") - -![Flipper Code-128 Barcode](screenshots/Flipper%20Barcode.png "Flipper Code-128 Barcode") - -![Flipper Box EAN-13 Barcode](screenshots/Flipper%20Box%20Barcode.png "Flipper Box EAN-13 Barcode") - -## Credits - -- [Kingal1337](https://github.com/Kingal1337) - Developer -- [Z0wl](https://github.com/Z0wl) - Added Code128-C Support -- [@teeebor](https://github.com/teeebor) - Menu Code Snippet - - -[1] - supports Set B (only the characters from 0-94). Also supports Set C diff --git a/applications/external/barcode_gen/application.fam b/applications/external/barcode_gen/application.fam deleted file mode 100644 index 0564b785a..000000000 --- a/applications/external/barcode_gen/application.fam +++ /dev/null @@ -1,16 +0,0 @@ -App( - appid="barcode_app", - name="Barcode App", - apptype=FlipperAppType.EXTERNAL, - entry_point="barcode_main", - requires=["gui", "storage"], - stack_size=2 * 1024, - fap_category="Tools", - fap_icon="images/barcode_10.png", - fap_icon_assets="images", - fap_file_assets="barcode_encoding_files", - fap_author="@Kingal1337", - fap_weburl="https://github.com/Kingal1337/flipper-barcode-generator", - fap_version="1.1", - fap_description="App allows you to display various barcodes on flipper screen", -) diff --git a/applications/external/barcode_gen/barcode_app.c b/applications/external/barcode_gen/barcode_app.c deleted file mode 100644 index 99e5769d2..000000000 --- a/applications/external/barcode_gen/barcode_app.c +++ /dev/null @@ -1,348 +0,0 @@ -#include "barcode_app.h" - -#include "barcode_app_icons.h" - -/** - * Opens a file browser dialog and returns the filepath of the selected file - * - * @param folder the folder to view when the browser opens - * @param file_path a string pointer for the file_path when a file is selected, - * file_path will be the folder path is nothing is selected - * @returns true if a file is selected -*/ -static bool select_file(const char* folder, FuriString* file_path) { - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, "", &I_barcode_10); - browser_options.base_path = DEFAULT_USER_BARCODES; - furi_string_set(file_path, folder); - - bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); - - furi_record_close(RECORD_DIALOGS); - - return res; -} - -/** - * Reads the data from a file and stores them in the FuriStrings raw_type and raw_data -*/ -ErrorCode read_raw_data(FuriString* file_path, FuriString* raw_type, FuriString* raw_data) { - //Open Storage - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_file_alloc(storage); - - ErrorCode reason = OKCode; - - if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(file_path))) { - FURI_LOG_E(TAG, "Could not open file %s", furi_string_get_cstr(file_path)); - reason = FileOpening; - } else { - if(!flipper_format_read_string(ff, "Type", raw_type)) { - FURI_LOG_E(TAG, "Could not read \"Type\" string"); - reason = InvalidFileData; - } - if(!flipper_format_read_string(ff, "Data", raw_data)) { - FURI_LOG_E(TAG, "Could not read \"Data\" string"); - reason = InvalidFileData; - } - } - - //Close Storage - flipper_format_free(ff); - furi_record_close(RECORD_STORAGE); - - return reason; -} - -/** - * Gets the file name from a file path - * @param file_path the file path - * @param file_name the FuriString to store the file name - * @param remove_extension true if the extension should be removed, otherwise false -*/ -bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool remove_extension) { - if(file_path == NULL || file_name == NULL) { - return false; - } - uint32_t slash_index = furi_string_search_rchar(file_path, '/', 0); - if(slash_index == FURI_STRING_FAILURE || slash_index >= (furi_string_size(file_path) - 1)) { - return false; - } - - furi_string_set(file_name, file_path); - furi_string_right(file_name, slash_index + 1); - if(remove_extension) { - uint32_t ext_index = furi_string_search_rchar(file_name, '.', 0); - if(ext_index != FURI_STRING_FAILURE && ext_index < (furi_string_size(file_path))) { - furi_string_left(file_name, ext_index); - } - } - - return true; -} - -/** - * Creates the barcode folder -*/ -void init_folder() { - Storage* storage = furi_record_open(RECORD_STORAGE); - FURI_LOG_I(TAG, "Creating barcodes folder"); - if(storage_simply_mkdir(storage, DEFAULT_USER_BARCODES)) { - FURI_LOG_I(TAG, "Barcodes folder successfully created!"); - } else { - FURI_LOG_I(TAG, "Barcodes folder already exists."); - } - furi_record_close(RECORD_STORAGE); -} - -void select_barcode_item(BarcodeApp* app) { - FuriString* file_path = furi_string_alloc(); - FuriString* raw_type = furi_string_alloc(); - FuriString* raw_data = furi_string_alloc(); - - //this determines if the data was read correctly or if the - bool loaded_success = true; - ErrorCode reason = OKCode; - - bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path); - if(file_selected) { - FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path)); - Barcode* barcode = app->barcode_view; - - reason = read_raw_data(file_path, raw_type, raw_data); - if(reason != OKCode) { - loaded_success = false; - FURI_LOG_E(TAG, "Could not read data correctly"); - } - - //Free the data from the previous barcode - barcode_free_model(barcode); - - with_view_model( - barcode->view, - BarcodeModel * model, - { - model->file_path = furi_string_alloc_set(file_path); - - model->data = malloc(sizeof(BarcodeData)); - model->data->valid = loaded_success; - - if(loaded_success) { - model->data->raw_data = furi_string_alloc_set(raw_data); - model->data->correct_data = furi_string_alloc(); - - model->data->type_obj = get_type(raw_type); - - barcode_loader(model->data); - } else { - model->data->reason = reason; - } - }, - true); - - view_dispatcher_switch_to_view(app->view_dispatcher, BarcodeView); - } - - furi_string_free(raw_type); - furi_string_free(raw_data); - furi_string_free(file_path); -} - -void edit_barcode_item(BarcodeApp* app) { - FuriString* file_path = furi_string_alloc(); - FuriString* file_name = furi_string_alloc(); - FuriString* raw_type = furi_string_alloc(); - FuriString* raw_data = furi_string_alloc(); - - //this determines if the data was read correctly or if the - ErrorCode reason = OKCode; - - bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path); - if(file_selected) { - FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path)); - CreateView* create_view_object = app->create_view; - - reason = read_raw_data(file_path, raw_type, raw_data); - if(reason != OKCode) { - FURI_LOG_E(TAG, "Could not read data correctly"); - with_view_model( - app->message_view->view, - MessageViewModel * model, - { model->message = get_error_code_message(reason); }, - true); - - view_dispatcher_switch_to_view( - create_view_object->barcode_app->view_dispatcher, MessageErrorView); - - } else { - BarcodeTypeObj* type_obj = get_type(raw_type); - if(type_obj->type == UNKNOWN) { - type_obj = barcode_type_objs[0]; - } - get_file_name_from_path(file_path, file_name, true); - - create_view_free_model(create_view_object); - with_view_model( - create_view_object->view, - CreateViewModel * model, - { - model->selected_menu_item = 0; - model->barcode_type = type_obj; - model->file_path = furi_string_alloc_set(file_path); - model->file_name = furi_string_alloc_set(file_name); - model->barcode_data = furi_string_alloc_set(raw_data); - model->mode = EditMode; - }, - true); - view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView); - } - } - - furi_string_free(raw_type); - furi_string_free(raw_data); - furi_string_free(file_name); - furi_string_free(file_path); -} - -void create_barcode_item(BarcodeApp* app) { - CreateView* create_view_object = app->create_view; - - create_view_free_model(create_view_object); - - with_view_model( - create_view_object->view, - CreateViewModel * model, - { - model->selected_menu_item = 0; - model->barcode_type = barcode_type_objs[0]; - model->file_path = furi_string_alloc(); - model->file_name = furi_string_alloc(); - model->barcode_data = furi_string_alloc(); - model->mode = NewMode; - }, - true); - view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView); -} - -void submenu_callback(void* context, uint32_t index) { - furi_assert(context); - - BarcodeApp* app = context; - - if(index == SelectBarcodeItem) { - select_barcode_item(app); - } else if(index == EditBarcodeItem) { - edit_barcode_item(app); - } else if(index == CreateBarcodeItem) { - create_barcode_item(app); - } -} - -uint32_t create_view_callback(void* context) { - UNUSED(context); - return CreateBarcodeView; -} - -uint32_t main_menu_callback(void* context) { - UNUSED(context); - return MainMenuView; -} - -uint32_t exit_callback(void* context) { - UNUSED(context); - return VIEW_NONE; -} - -void free_app(BarcodeApp* app) { - FURI_LOG_I(TAG, "Freeing Data"); - - init_folder(); - free_types(); - - view_dispatcher_remove_view(app->view_dispatcher, TextInputView); - text_input_free(app->text_input); - - view_dispatcher_remove_view(app->view_dispatcher, MessageErrorView); - message_view_free(app->message_view); - - view_dispatcher_remove_view(app->view_dispatcher, MainMenuView); - submenu_free(app->main_menu); - - view_dispatcher_remove_view(app->view_dispatcher, CreateBarcodeView); - create_view_free(app->create_view); - - view_dispatcher_remove_view(app->view_dispatcher, BarcodeView); - barcode_free(app->barcode_view); - - //free the dispatcher - view_dispatcher_free(app->view_dispatcher); - - furi_message_queue_free(app->event_queue); - - furi_record_close(RECORD_GUI); - app->gui = NULL; - - free(app); -} - -int32_t barcode_main(void* p) { - UNUSED(p); - BarcodeApp* app = malloc(sizeof(BarcodeApp)); - init_types(); - app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - - // Register view port in GUI - app->gui = furi_record_open(RECORD_GUI); - - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->main_menu = submenu_alloc(); - submenu_add_item(app->main_menu, "Load Barcode", SelectBarcodeItem, submenu_callback, app); - view_set_previous_callback(submenu_get_view(app->main_menu), exit_callback); - view_dispatcher_add_view(app->view_dispatcher, MainMenuView, submenu_get_view(app->main_menu)); - - submenu_add_item(app->main_menu, "Edit Barcode", EditBarcodeItem, submenu_callback, app); - - /***************************** - * Creating Text Input View - ******************************/ - app->text_input = text_input_alloc(); - view_set_previous_callback(text_input_get_view(app->text_input), create_view_callback); - view_dispatcher_add_view( - app->view_dispatcher, TextInputView, text_input_get_view(app->text_input)); - - /***************************** - * Creating Message View - ******************************/ - app->message_view = message_view_allocate(app); - view_dispatcher_add_view( - app->view_dispatcher, MessageErrorView, message_get_view(app->message_view)); - - /***************************** - * Creating Create View - ******************************/ - app->create_view = create_view_allocate(app); - submenu_add_item(app->main_menu, "Create Barcode", CreateBarcodeItem, submenu_callback, app); - view_set_previous_callback(create_get_view(app->create_view), main_menu_callback); - view_dispatcher_add_view( - app->view_dispatcher, CreateBarcodeView, create_get_view(app->create_view)); - - /***************************** - * Creating Barcode View - ******************************/ - app->barcode_view = barcode_view_allocate(app); - view_set_previous_callback(barcode_get_view(app->barcode_view), main_menu_callback); - view_dispatcher_add_view( - app->view_dispatcher, BarcodeView, barcode_get_view(app->barcode_view)); - - //switch view to submenu and run dispatcher - view_dispatcher_switch_to_view(app->view_dispatcher, MainMenuView); - view_dispatcher_run(app->view_dispatcher); - - free_app(app); - - return 0; -} diff --git a/applications/external/barcode_gen/barcode_app.h b/applications/external/barcode_gen/barcode_app.h deleted file mode 100644 index bddc82235..000000000 --- a/applications/external/barcode_gen/barcode_app.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "barcode_utils.h" - -#define TAG "BARCODE" -#define VERSION "1.1" -#define FILE_VERSION "1" - -#define TEXT_BUFFER_SIZE 128 - -#define BARCODE_HEIGHT 50 -#define BARCODE_Y_START 3 - -//the folder where the codabar encoding table is located -#define CODABAR_DICT_FILE_PATH APP_ASSETS_PATH("codabar_encodings.txt") - -//the folder where the code 39 encoding table is located -#define CODE39_DICT_FILE_PATH APP_ASSETS_PATH("code39_encodings.txt") - -//the folder where the code 128 encoding table is located -#define CODE128_DICT_FILE_PATH APP_ASSETS_PATH("code128_encodings.txt") - -//the folder where the code 128 C encoding table is located -#define CODE128C_DICT_FILE_PATH APP_ASSETS_PATH("code128c_encodings.txt") - -//the folder where the user stores their barcodes -#define DEFAULT_USER_BARCODES EXT_PATH("apps_data/barcodes") - -//The extension barcode files use -#define BARCODE_EXTENSION ".txt" -#define BARCODE_EXTENSION_LENGTH 4 - -#include "views/barcode_view.h" -#include "views/create_view.h" -#include "views/message_view.h" -#include "barcode_validator.h" - -typedef struct BarcodeApp BarcodeApp; - -struct BarcodeApp { - Submenu* main_menu; - ViewDispatcher* view_dispatcher; - Gui* gui; - - FuriMessageQueue* event_queue; - - CreateView* create_view; - Barcode* barcode_view; - - MessageView* message_view; - TextInput* text_input; -}; - -enum SubmenuItems { - SelectBarcodeItem, - EditBarcodeItem, - - CreateBarcodeItem -}; - -enum Views { - TextInputView, - MessageErrorView, - MainMenuView, - CreateBarcodeView, - - BarcodeView -}; - -void submenu_callback(void* context, uint32_t index); - -uint32_t main_menu_callback(void* context); - -uint32_t exit_callback(void* context); - -int32_t barcode_main(void* p); diff --git a/applications/external/barcode_gen/barcode_encoding_files/codabar_encodings.txt b/applications/external/barcode_gen/barcode_encoding_files/codabar_encodings.txt deleted file mode 100644 index 5f0684cbd..000000000 --- a/applications/external/barcode_gen/barcode_encoding_files/codabar_encodings.txt +++ /dev/null @@ -1,22 +0,0 @@ -# alternates between bars and spaces, always begins with bar -# 0 for narrow, 1 for wide -0: 0000011 -1: 0000110 -2: 0001001 -3: 1100000 -4: 0010010 -5: 1000010 -6: 0100001 -7: 0100100 -8: 0110000 -9: 1001000 --: 0001100 -$: 0011000 -:: 1000101 -/: 1010001 -.: 1010100 -+: 0010101 -A: 0011010 -B: 0101001 -C: 0001011 -D: 0001110 \ No newline at end of file diff --git a/applications/external/barcode_gen/barcode_encoding_files/code128_encodings.txt b/applications/external/barcode_gen/barcode_encoding_files/code128_encodings.txt deleted file mode 100644 index 394a34520..000000000 --- a/applications/external/barcode_gen/barcode_encoding_files/code128_encodings.txt +++ /dev/null @@ -1,202 +0,0 @@ - : 00 -!: 01 -": 02 -#: 03 -$: 04 -%: 05 -&: 06 -': 07 -(: 08 -): 09 -*: 10 -+: 11 -,: 12 --: 13 -.: 14 -/: 15 -0: 16 -1: 17 -2: 18 -3: 19 -4: 20 -5: 21 -6: 22 -7: 23 -8: 24 -9: 25 -:: 26 -;: 27 -<: 28 -=: 29 ->: 30 -?: 31 -@: 32 -A: 33 -B: 34 -C: 35 -D: 36 -E: 37 -F: 38 -G: 39 -H: 40 -I: 41 -J: 42 -K: 43 -L: 44 -M: 45 -N: 46 -O: 47 -P: 48 -Q: 49 -R: 50 -S: 51 -T: 52 -U: 53 -V: 54 -W: 55 -X: 56 -Y: 57 -Z: 58 -[: 59 -\: 60 -]: 61 -^: 62 -_: 63 -`: 64 -a: 65 -b: 66 -c: 67 -d: 68 -e: 69 -f: 70 -g: 71 -h: 72 -i: 73 -j: 74 -k: 75 -l: 76 -m: 77 -n: 78 -o: 79 -p: 80 -q: 81 -r: 82 -s: 83 -t: 84 -u: 85 -v: 86 -w: 87 -x: 88 -y: 89 -z: 90 -{: 91 -|: 92 -}: 93 -~: 94 - -00: 11011001100 -01: 11001101100 -02: 11001100110 -03: 10010011000 -04: 10010001100 -05: 10001001100 -06: 10011001000 -07: 10011000100 -08: 10001100100 -09: 11001001000 -10: 11001000100 -11: 11000100100 -12: 10110011100 -13: 10011011100 -14: 10011001110 -15: 10111001100 -16: 10011101100 -17: 10011100110 -18: 11001110010 -19: 11001011100 -20: 11001001110 -21: 11011100100 -22: 11001110100 -23: 11101101110 -24: 11101001100 -25: 11100101100 -26: 11100100110 -27: 11101100100 -28: 11100110100 -29: 11100110010 -30: 11011011000 -31: 11011000110 -32: 11000110110 -33: 10100011000 -34: 10001011000 -35: 10001000110 -36: 10110001000 -37: 10001101000 -38: 10001100010 -39: 11010001000 -40: 11000101000 -41: 11000100010 -42: 10110111000 -43: 10110001110 -44: 10001101110 -45: 10111011000 -46: 10111000110 -47: 10001110110 -48: 11101110110 -49: 11010001110 -50: 11000101110 -51: 11011101000 -52: 11011100010 -53: 11011101110 -54: 11101011000 -55: 11101000110 -56: 11100010110 -57: 11101101000 -58: 11101100010 -59: 11100011010 -60: 11101111010 -61: 11001000010 -62: 11110001010 -63: 10100110000 -64: 10100001100 -65: 10010110000 -66: 10010000110 -67: 10000101100 -68: 10000100110 -69: 10110010000 -70: 10110000100 -71: 10011010000 -72: 10011000010 -73: 10000110100 -74: 10000110010 -75: 11000010010 -76: 11001010000 -77: 11110111010 -78: 11000010100 -79: 10001111010 -80: 10100111100 -81: 10010111100 -82: 10010011110 -83: 10111100100 -84: 10011110100 -85: 10011110010 -86: 11110100100 -87: 11110010100 -88: 11110010010 -89: 11011011110 -90: 11011110110 -91: 11110110110 -92: 10101111000 -93: 10100011110 -94: 10001011110 -95: 10111101000 -96: 10111100010 -97: 11110101000 -98: 11110100010 -99: 10111011110 -100: 10111101110 -101: 11101011110 -102: 11110101110 -103: 11010000100 -104: 11010010000 -105: 11010011100 \ No newline at end of file diff --git a/applications/external/barcode_gen/barcode_encoding_files/code128c_encodings.txt b/applications/external/barcode_gen/barcode_encoding_files/code128c_encodings.txt deleted file mode 100644 index 75cc71135..000000000 --- a/applications/external/barcode_gen/barcode_encoding_files/code128c_encodings.txt +++ /dev/null @@ -1,106 +0,0 @@ -00: 11011001100 -01: 11001101100 -02: 11001100110 -03: 10010011000 -04: 10010001100 -05: 10001001100 -06: 10011001000 -07: 10011000100 -08: 10001100100 -09: 11001001000 -10: 11001000100 -11: 11000100100 -12: 10110011100 -13: 10011011100 -14: 10011001110 -15: 10111001100 -16: 10011101100 -17: 10011100110 -18: 11001110010 -19: 11001011100 -20: 11001001110 -21: 11011100100 -22: 11001110100 -23: 11101101110 -24: 11101001100 -25: 11100101100 -26: 11100100110 -27: 11101100100 -28: 11100110100 -29: 11100110010 -30: 11011011000 -31: 11011000110 -32: 11000110110 -33: 10100011000 -34: 10001011000 -35: 10001000110 -36: 10110001000 -37: 10001101000 -38: 10001100010 -39: 11010001000 -40: 11000101000 -41: 11000100010 -42: 10110111000 -43: 10110001110 -44: 10001101110 -45: 10111011000 -46: 10111000110 -47: 10001110110 -48: 11101110110 -49: 11010001110 -50: 11000101110 -51: 11011101000 -52: 11011100010 -53: 11011101110 -54: 11101011000 -55: 11101000110 -56: 11100010110 -57: 11101101000 -58: 11101100010 -59: 11100011010 -60: 11101111010 -61: 11001000010 -62: 11110001010 -63: 10100110000 -64: 10100001100 -65: 10010110000 -66: 10010000110 -67: 10000101100 -68: 10000100110 -69: 10110010000 -70: 10110000100 -71: 10011010000 -72: 10011000010 -73: 10000110100 -74: 10000110010 -75: 11000010010 -76: 11001010000 -77: 11110111010 -78: 11000010100 -79: 10001111010 -80: 10100111100 -81: 10010111100 -82: 10010011110 -83: 10111100100 -84: 10011110100 -85: 10011110010 -86: 11110100100 -87: 11110010100 -88: 11110010010 -89: 11011011110 -90: 11011110110 -91: 11110110110 -92: 10101111000 -93: 10100011110 -94: 10001011110 -95: 10111101000 -96: 10111100010 -97: 11110101000 -98: 11110100010 -99: 10111011110 -100: 10111101110 -101: 11101011110 -102: 11110101110 -103: 11010000100 -104: 11010010000 -105: 11010011100 diff --git a/applications/external/barcode_gen/barcode_encoding_files/code39_encodings.txt b/applications/external/barcode_gen/barcode_encoding_files/code39_encodings.txt deleted file mode 100644 index a41ad16e9..000000000 --- a/applications/external/barcode_gen/barcode_encoding_files/code39_encodings.txt +++ /dev/null @@ -1,44 +0,0 @@ -0: 000110100 -1: 100100001 -2: 001100001 -3: 101100000 -4: 000110001 -5: 100110000 -6: 001110000 -7: 000100101 -8: 100100100 -9: 001100100 -A: 100001001 -B: 001001001 -C: 101001000 -D: 000011001 -E: 100011000 -F: 001011000 -G: 000001101 -H: 100001100 -I: 001001100 -J: 000011100 -K: 100000011 -L: 001000011 -M: 101000010 -N: 000010011 -O: 100010010 -P: 001010010 -Q: 000000111 -R: 100000110 -S: 001000110 -T: 000010110 -U: 110000001 -V: 011000001 -W: 111000000 -X: 010010001 -Y: 110010000 -Z: 011010000 --: 010000101 -.: 110000100 - : 011000100 -*: 010010100 -$: 010101000 -/: 010100010 -+: 010001010 -%: 000101010 \ No newline at end of file diff --git a/applications/external/barcode_gen/barcode_utils.c b/applications/external/barcode_gen/barcode_utils.c deleted file mode 100644 index 31274c1fe..000000000 --- a/applications/external/barcode_gen/barcode_utils.c +++ /dev/null @@ -1,147 +0,0 @@ -#include "barcode_utils.h" - -BarcodeTypeObj* barcode_type_objs[NUMBER_OF_BARCODE_TYPES] = {NULL}; - -void init_types() { - BarcodeTypeObj* upc_a = malloc(sizeof(BarcodeTypeObj)); - upc_a->name = "UPC-A"; - upc_a->type = UPCA; - upc_a->min_digits = 11; - upc_a->max_digits = 12; - upc_a->start_pos = 16; - barcode_type_objs[UPCA] = upc_a; - - BarcodeTypeObj* ean_8 = malloc(sizeof(BarcodeTypeObj)); - ean_8->name = "EAN-8"; - ean_8->type = EAN8; - ean_8->min_digits = 7; - ean_8->max_digits = 8; - ean_8->start_pos = 32; - barcode_type_objs[EAN8] = ean_8; - - BarcodeTypeObj* ean_13 = malloc(sizeof(BarcodeTypeObj)); - ean_13->name = "EAN-13"; - ean_13->type = EAN13; - ean_13->min_digits = 12; - ean_13->max_digits = 13; - ean_13->start_pos = 16; - barcode_type_objs[EAN13] = ean_13; - - BarcodeTypeObj* code_39 = malloc(sizeof(BarcodeTypeObj)); - code_39->name = "CODE-39"; - code_39->type = CODE39; - code_39->min_digits = 1; - code_39->max_digits = -1; - code_39->start_pos = 0; - barcode_type_objs[CODE39] = code_39; - - BarcodeTypeObj* code_128 = malloc(sizeof(BarcodeTypeObj)); - code_128->name = "CODE-128"; - code_128->type = CODE128; - code_128->min_digits = 1; - code_128->max_digits = -1; - code_128->start_pos = 0; - barcode_type_objs[CODE128] = code_128; - - BarcodeTypeObj* code_128c = malloc(sizeof(BarcodeTypeObj)); - code_128c->name = "CODE-128C"; - code_128c->type = CODE128C; - code_128c->min_digits = 2; - code_128c->max_digits = -1; - code_128c->start_pos = 0; - barcode_type_objs[CODE128C] = code_128c; - - BarcodeTypeObj* codabar = malloc(sizeof(BarcodeTypeObj)); - codabar->name = "Codabar"; - codabar->type = CODABAR; - codabar->min_digits = 1; - codabar->max_digits = -1; - codabar->start_pos = 0; - barcode_type_objs[CODABAR] = codabar; - - BarcodeTypeObj* unknown = malloc(sizeof(BarcodeTypeObj)); - unknown->name = "Unknown"; - unknown->type = UNKNOWN; - unknown->min_digits = 0; - unknown->max_digits = 0; - unknown->start_pos = 0; - barcode_type_objs[UNKNOWN] = unknown; -} - -void free_types() { - for(int i = 0; i < NUMBER_OF_BARCODE_TYPES; i++) { - free(barcode_type_objs[i]); - } -} - -BarcodeTypeObj* get_type(FuriString* type_string) { - if(furi_string_cmp_str(type_string, "UPC-A") == 0) { - return barcode_type_objs[UPCA]; - } - if(furi_string_cmp_str(type_string, "EAN-8") == 0) { - return barcode_type_objs[EAN8]; - } - if(furi_string_cmp_str(type_string, "EAN-13") == 0) { - return barcode_type_objs[EAN13]; - } - if(furi_string_cmp_str(type_string, "CODE-39") == 0) { - return barcode_type_objs[CODE39]; - } - if(furi_string_cmp_str(type_string, "CODE-128") == 0) { - return barcode_type_objs[CODE128]; - } - if(furi_string_cmp_str(type_string, "CODE-128C") == 0) { - return barcode_type_objs[CODE128C]; - } - if(furi_string_cmp_str(type_string, "Codabar") == 0) { - return barcode_type_objs[CODABAR]; - } - - return barcode_type_objs[UNKNOWN]; -} - -const char* get_error_code_name(ErrorCode error_code) { - switch(error_code) { - case WrongNumberOfDigits: - return "Wrong Number Of Digits"; - case InvalidCharacters: - return "Invalid Characters"; - case UnsupportedType: - return "Unsupported Type"; - case FileOpening: - return "File Opening Error"; - case InvalidFileData: - return "Invalid File Data"; - case MissingEncodingTable: - return "Missing Encoding Table"; - case EncodingTableError: - return "Encoding Table Error"; - case OKCode: - return "OK"; - default: - return "Unknown Code"; - }; -} - -const char* get_error_code_message(ErrorCode error_code) { - switch(error_code) { - case WrongNumberOfDigits: - return "Wrong # of characters"; - case InvalidCharacters: - return "Invalid characters"; - case UnsupportedType: - return "Unsupported barcode type"; - case FileOpening: - return "Could not open file"; - case InvalidFileData: - return "Invalid file data"; - case MissingEncodingTable: - return "Missing encoding table"; - case EncodingTableError: - return "Encoding table error"; - case OKCode: - return "OK"; - default: - return "Could not read barcode data"; - }; -} diff --git a/applications/external/barcode_gen/barcode_utils.h b/applications/external/barcode_gen/barcode_utils.h deleted file mode 100644 index 0a27785cf..000000000 --- a/applications/external/barcode_gen/barcode_utils.h +++ /dev/null @@ -1,55 +0,0 @@ - -#pragma once -#include -#include - -#define NUMBER_OF_BARCODE_TYPES 8 - -typedef enum { - WrongNumberOfDigits, //There is too many or too few digits in the barcode - InvalidCharacters, //The barcode contains invalid characters - UnsupportedType, //the barcode type is not supported - FileOpening, //A problem occurred when opening the barcode data file - InvalidFileData, //One of the key in the file doesn't exist or there is a typo - MissingEncodingTable, //The encoding table txt for the barcode type is missing - EncodingTableError, //Something is wrong with the encoding table, probably missing data or typo - OKCode -} ErrorCode; - -typedef enum { - UPCA, - EAN8, - EAN13, - CODE39, - CODE128, - CODE128C, - CODABAR, - - UNKNOWN -} BarcodeType; - -typedef struct { - char* name; //The name of the barcode type - BarcodeType type; //The barcode type enum - int min_digits; //the minimum number of digits - int max_digits; //the maximum number of digits - int start_pos; //where to start drawing the barcode, set to -1 to dynamically draw barcode -} BarcodeTypeObj; - -typedef struct { - BarcodeTypeObj* type_obj; - int check_digit; //A place to store the check digit - FuriString* raw_data; //the data directly from the file - FuriString* correct_data; //the corrected/processed data - bool valid; //true if the raw data is correctly formatted, such as correct num of digits, valid characters, etc. - ErrorCode reason; //the reason why this barcode is invalid -} BarcodeData; - -//All available barcode types -extern BarcodeTypeObj* barcode_type_objs[NUMBER_OF_BARCODE_TYPES]; - -void init_types(); -void free_types(); -BarcodeTypeObj* get_type(FuriString* type_string); -const char* get_error_code_name(ErrorCode error_code); -const char* get_error_code_message(ErrorCode error_code); diff --git a/applications/external/barcode_gen/barcode_validator.c b/applications/external/barcode_gen/barcode_validator.c deleted file mode 100644 index cb493f3e9..000000000 --- a/applications/external/barcode_gen/barcode_validator.c +++ /dev/null @@ -1,532 +0,0 @@ -#include "barcode_validator.h" - -void barcode_loader(BarcodeData* barcode_data) { - switch(barcode_data->type_obj->type) { - case UPCA: - case EAN8: - case EAN13: - ean_upc_loader(barcode_data); - break; - case CODE39: - code_39_loader(barcode_data); - break; - case CODE128: - code_128_loader(barcode_data); - break; - case CODE128C: - code_128c_loader(barcode_data); - break; - case CODABAR: - codabar_loader(barcode_data); - break; - case UNKNOWN: - barcode_data->reason = UnsupportedType; - barcode_data->valid = false; - default: - break; - } -} - -/** - * Calculates the check digit of a barcode if they have one - * @param barcode_data the barcode data - * @returns a check digit or -1 for either an invalid -*/ -int calculate_check_digit(BarcodeData* barcode_data) { - int check_digit = -1; - switch(barcode_data->type_obj->type) { - case UPCA: - case EAN8: - case EAN13: - check_digit = calculate_ean_upc_check_digit(barcode_data); - break; - case CODE39: - case CODE128: - case CODE128C: - case CODABAR: - case UNKNOWN: - default: - break; - } - - return check_digit; -} - -/** - * Calculates the check digit of barcode types UPC-A, EAN-8, & EAN-13 -*/ -int calculate_ean_upc_check_digit(BarcodeData* barcode_data) { - int check_digit = 0; - int odd = 0; - int even = 0; - - int length = barcode_data->type_obj->min_digits; - - //Get sum of odd digits - for(int i = 0; i < length; i += 2) { - odd += furi_string_get_char(barcode_data->raw_data, i) - '0'; - } - - //Get sum of even digits - for(int i = 1; i < length; i += 2) { - even += furi_string_get_char(barcode_data->raw_data, i) - '0'; - } - - if(barcode_data->type_obj->type == EAN13) { - check_digit = even * 3 + odd; - } else { - check_digit = odd * 3 + even; - } - - check_digit = check_digit % 10; - - return (10 - check_digit) % 10; -} - -/** - * Loads and validates Barcode Types EAN-8, EAN-13, and UPC-A - * barcode_data and its strings should already be allocated; -*/ -void ean_upc_loader(BarcodeData* barcode_data) { - int barcode_length = furi_string_size(barcode_data->raw_data); - - int min_digits = barcode_data->type_obj->min_digits; - int max_digit = barcode_data->type_obj->max_digits; - - //check the length of the barcode - if(barcode_length < min_digits || barcode_length > max_digit) { - barcode_data->reason = WrongNumberOfDigits; - barcode_data->valid = false; - return; - } - - //checks if the barcode contains any characters that aren't a number - for(int i = 0; i < barcode_length; i++) { - char character = furi_string_get_char(barcode_data->raw_data, i); - int digit = character - '0'; //convert the number into an int (also the index) - if(digit < 0 || digit > 9) { - barcode_data->reason = InvalidCharacters; - barcode_data->valid = false; - return; - } - } - - int check_digit = calculate_check_digit(barcode_data); - char check_digit_char = check_digit + '0'; - - barcode_data->check_digit = check_digit; - - //if the barcode length is at max length then we will verify if the check digit is correct - if(barcode_length == max_digit) { - //append the raw_data to the correct data string - furi_string_cat(barcode_data->correct_data, barcode_data->raw_data); - - //append the check digit to the correct data string - furi_string_set_char(barcode_data->correct_data, min_digits, check_digit_char); - } - //if the barcode length is at min length, we will calculate the check digit - if(barcode_length == min_digits) { - //append the raw_data to the correct data string - furi_string_cat(barcode_data->correct_data, barcode_data->raw_data); - - //append the check digit to the correct data string - furi_string_push_back(barcode_data->correct_data, check_digit_char); - } -} - -void code_39_loader(BarcodeData* barcode_data) { - int barcode_length = furi_string_size(barcode_data->raw_data); - - int min_digits = barcode_data->type_obj->min_digits; - - //check the length of the barcode, must contain atleast a character, - //this can have as many characters as it wants, it might not fit on the screen - if(barcode_length < min_digits) { - barcode_data->reason = WrongNumberOfDigits; - barcode_data->valid = false; - return; - } - - FuriString* barcode_bits = furi_string_alloc(); - FuriString* temp_string = furi_string_alloc(); - - //add starting and ending * - if(!furi_string_start_with(barcode_data->raw_data, "*")) { - furi_string_push_back(temp_string, '*'); - furi_string_cat(temp_string, barcode_data->raw_data); - furi_string_set(barcode_data->raw_data, temp_string); - } - - if(!furi_string_end_with(barcode_data->raw_data, "*")) { - furi_string_push_back(barcode_data->raw_data, '*'); - } - - furi_string_free(temp_string); - barcode_length = furi_string_size(barcode_data->raw_data); - - //Open Storage - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(ff, CODE39_DICT_FILE_PATH)) { - FURI_LOG_E(TAG, "Could not open file %s", CODE39_DICT_FILE_PATH); - barcode_data->reason = MissingEncodingTable; - barcode_data->valid = false; - } else { - FuriString* char_bits = furi_string_alloc(); - for(int i = 0; i < barcode_length; i++) { - char barcode_char = toupper(furi_string_get_char(barcode_data->raw_data, i)); - - //convert a char into a string so it used in flipper_format_read_string - char current_character[2]; - snprintf(current_character, 2, "%c", barcode_char); - - if(!flipper_format_read_string(ff, current_character, char_bits)) { - FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char); - barcode_data->reason = InvalidCharacters; - barcode_data->valid = false; - break; - } else { - FURI_LOG_I( - TAG, "\"%c\" string: %s", barcode_char, furi_string_get_cstr(char_bits)); - furi_string_cat(barcode_bits, char_bits); - } - flipper_format_rewind(ff); - } - furi_string_free(char_bits); - } - - //Close Storage - flipper_format_free(ff); - furi_record_close(RECORD_STORAGE); - - furi_string_cat(barcode_data->correct_data, barcode_bits); - furi_string_free(barcode_bits); -} - -/** - * Loads a code 128 barcode - * - * Only supports character set B -*/ -void code_128_loader(BarcodeData* barcode_data) { - int barcode_length = furi_string_size(barcode_data->raw_data); - - //the start code for character set B - int start_code_value = 104; - - //The bits for the start code - const char* start_code_bits = "11010010000"; - - //The bits for the stop code - const char* stop_code_bits = "1100011101011"; - - int min_digits = barcode_data->type_obj->min_digits; - - /** - * A sum of all of the characters values - * Ex: - * Barcode Data : ABC - * A has a value of 33 - * B has a value of 34 - * C has a value of 35 - * - * the checksum_adder would be (33 * 1) + (34 * 2) + (35 * 3) + 104 = 310 - * - * Add 104 since we are using set B - */ - int checksum_adder = start_code_value; - /** - * Checksum digits is the number of characters it has read so far - * In the above example the checksum_digits would be 3 - */ - int checksum_digits = 0; - - //the calculated check digit - int final_check_digit = 0; - - //check the length of the barcode, must contain atleast a character, - //this can have as many characters as it wants, it might not fit on the screen - if(barcode_length < min_digits) { - barcode_data->reason = WrongNumberOfDigits; - barcode_data->valid = false; - return; - } - - //Open Storage - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_file_alloc(storage); - - FuriString* barcode_bits = furi_string_alloc(); - - //add the start code - furi_string_cat(barcode_bits, start_code_bits); - - if(!flipper_format_file_open_existing(ff, CODE128_DICT_FILE_PATH)) { - FURI_LOG_E(TAG, "Could not open file %s", CODE128_DICT_FILE_PATH); - barcode_data->reason = MissingEncodingTable; - barcode_data->valid = false; - } else { - FuriString* value = furi_string_alloc(); - FuriString* char_bits = furi_string_alloc(); - for(int i = 0; i < barcode_length; i++) { - char barcode_char = furi_string_get_char(barcode_data->raw_data, i); - - //convert a char into a string so it used in flipper_format_read_string - char current_character[2]; - snprintf(current_character, 2, "%c", barcode_char); - - //get the value of the character - if(!flipper_format_read_string(ff, current_character, value)) { - FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char); - barcode_data->reason = InvalidCharacters; - barcode_data->valid = false; - break; - } - //using the value of the character, get the characters bits - if(!flipper_format_read_string(ff, furi_string_get_cstr(value), char_bits)) { - FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char); - barcode_data->reason = EncodingTableError; - barcode_data->valid = false; - break; - } else { - //add the bits to the full barcode - furi_string_cat(barcode_bits, char_bits); - - //calculate the checksum - checksum_digits += 1; - checksum_adder += (atoi(furi_string_get_cstr(value)) * checksum_digits); - - FURI_LOG_D( - TAG, - "\"%c\" string: %s : %s : %d : %d : %d", - barcode_char, - furi_string_get_cstr(char_bits), - furi_string_get_cstr(value), - checksum_digits, - (atoi(furi_string_get_cstr(value)) * checksum_digits), - checksum_adder); - } - //bring the file pointer back to the beginning - flipper_format_rewind(ff); - } - - //calculate the check digit and convert it into a c string for lookup in the encoding table - final_check_digit = checksum_adder % 103; - int length = snprintf(NULL, 0, "%d", final_check_digit); - char* final_check_digit_string = malloc(length + 1); - snprintf(final_check_digit_string, length + 1, "%d", final_check_digit); - - //after the checksum has been calculated, add the bits to the full barcode - if(!flipper_format_read_string(ff, final_check_digit_string, char_bits)) { - FURI_LOG_E(TAG, "Could not read \"%s\" string", final_check_digit_string); - barcode_data->reason = EncodingTableError; - barcode_data->valid = false; - } else { - //add the check digit bits to the full barcode - furi_string_cat(barcode_bits, char_bits); - - FURI_LOG_D( - TAG, - "\"%s\" string: %s", - final_check_digit_string, - furi_string_get_cstr(char_bits)); - } - - free(final_check_digit_string); - furi_string_free(value); - furi_string_free(char_bits); - } - - //add the stop code - furi_string_cat(barcode_bits, stop_code_bits); - - //Close Storage - flipper_format_free(ff); - furi_record_close(RECORD_STORAGE); - - furi_string_cat(barcode_data->correct_data, barcode_bits); - furi_string_free(barcode_bits); -} - -/** - * Loads a code 128 C barcode -*/ -void code_128c_loader(BarcodeData* barcode_data) { - int barcode_length = furi_string_size(barcode_data->raw_data); - - //the start code for character set C - int start_code_value = 105; - - //The bits for the start code - const char* start_code_bits = "11010011100"; - - //The bits for the stop code - const char* stop_code_bits = "1100011101011"; - - int min_digits = barcode_data->type_obj->min_digits; - - int checksum_adder = start_code_value; - int checksum_digits = 0; - - //the calculated check digit - int final_check_digit = 0; - - // check the length of the barcode, must contain atleast 2 character, - // this can have as many characters as it wants, it might not fit on the screen - // code 128 C: the length must be even - if((barcode_length < min_digits) || (barcode_length & 1)) { - barcode_data->reason = WrongNumberOfDigits; - barcode_data->valid = false; - return; - } - //Open Storage - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_file_alloc(storage); - - FuriString* barcode_bits = furi_string_alloc(); - - //add the start code - furi_string_cat(barcode_bits, start_code_bits); - - if(!flipper_format_file_open_existing(ff, CODE128C_DICT_FILE_PATH)) { - FURI_LOG_E(TAG, "c128c Could not open file %s", CODE128C_DICT_FILE_PATH); - barcode_data->reason = MissingEncodingTable; - barcode_data->valid = false; - } else { - FuriString* value = furi_string_alloc(); - FuriString* char_bits = furi_string_alloc(); - for(int i = 0; i < barcode_length; i += 2) { - char barcode_char1 = furi_string_get_char(barcode_data->raw_data, i); - char barcode_char2 = furi_string_get_char(barcode_data->raw_data, i + 1); - FURI_LOG_I(TAG, "c128c bc1='%c' bc2='%c'", barcode_char1, barcode_char2); - - char current_chars[4]; - snprintf(current_chars, 3, "%c%c", barcode_char1, barcode_char2); - FURI_LOG_I(TAG, "c128c current_chars='%s'", current_chars); - - //using the value of the characters, get the characters bits - if(!flipper_format_read_string(ff, current_chars, char_bits)) { - FURI_LOG_E(TAG, "c128c Could not read \"%s\" string", current_chars); - barcode_data->reason = EncodingTableError; - barcode_data->valid = false; - break; - } else { - //add the bits to the full barcode - furi_string_cat(barcode_bits, char_bits); - - // calculate the checksum - checksum_digits += 1; - checksum_adder += (atoi(current_chars) * checksum_digits); - - FURI_LOG_I( - TAG, - "c128c \"%s\" string: %s : %s : %d : %d : %d", - current_chars, - furi_string_get_cstr(char_bits), - furi_string_get_cstr(value), - checksum_digits, - (atoi(furi_string_get_cstr(value)) * checksum_digits), - checksum_adder); - } - //bring the file pointer back to the begining - flipper_format_rewind(ff); - } - //calculate the check digit and convert it into a c string for lookup in the encoding table - final_check_digit = checksum_adder % 103; - FURI_LOG_I(TAG, "c128c finale_check_digit=%d", final_check_digit); - - int length = snprintf(NULL, 0, "%d", final_check_digit); - if(final_check_digit < 100) length = 2; - char* final_check_digit_string = malloc(length + 1); - snprintf(final_check_digit_string, length + 1, "%02d", final_check_digit); - - //after the checksum has been calculated, add the bits to the full barcode - if(!flipper_format_read_string(ff, final_check_digit_string, char_bits)) { - FURI_LOG_E(TAG, "c128c cksum Could not read \"%s\" string", final_check_digit_string); - barcode_data->reason = EncodingTableError; - barcode_data->valid = false; - } else { - //add the check digit bits to the full barcode - furi_string_cat(barcode_bits, char_bits); - - FURI_LOG_I( - TAG, - "check digit \"%s\" string: %s", - final_check_digit_string, - furi_string_get_cstr(char_bits)); - } - - free(final_check_digit_string); - furi_string_free(value); - furi_string_free(char_bits); - } - - //add the stop code - furi_string_cat(barcode_bits, stop_code_bits); - - //Close Storage - flipper_format_free(ff); - furi_record_close(RECORD_STORAGE); - - FURI_LOG_I(TAG, "c128c %s", furi_string_get_cstr(barcode_bits)); - furi_string_cat(barcode_data->correct_data, barcode_bits); - furi_string_free(barcode_bits); -} - -void codabar_loader(BarcodeData* barcode_data) { - int barcode_length = furi_string_size(barcode_data->raw_data); - - int min_digits = barcode_data->type_obj->min_digits; - - //check the length of the barcode, must contain atleast a character, - //this can have as many characters as it wants, it might not fit on the screen - if(barcode_length < min_digits) { - barcode_data->reason = WrongNumberOfDigits; - barcode_data->valid = false; - return; - } - - FuriString* barcode_bits = furi_string_alloc(); - - barcode_length = furi_string_size(barcode_data->raw_data); - - //Open Storage - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* ff = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_existing(ff, CODABAR_DICT_FILE_PATH)) { - FURI_LOG_E(TAG, "Could not open file %s", CODABAR_DICT_FILE_PATH); - barcode_data->reason = MissingEncodingTable; - barcode_data->valid = false; - } else { - FuriString* char_bits = furi_string_alloc(); - for(int i = 0; i < barcode_length; i++) { - char barcode_char = toupper(furi_string_get_char(barcode_data->raw_data, i)); - - //convert a char into a string so it used in flipper_format_read_string - char current_character[2]; - snprintf(current_character, 2, "%c", barcode_char); - - if(!flipper_format_read_string(ff, current_character, char_bits)) { - FURI_LOG_E(TAG, "Could not read \"%c\" string", barcode_char); - barcode_data->reason = InvalidCharacters; - barcode_data->valid = false; - break; - } else { - FURI_LOG_I( - TAG, "\"%c\" string: %s", barcode_char, furi_string_get_cstr(char_bits)); - furi_string_cat(barcode_bits, char_bits); - } - flipper_format_rewind(ff); - } - furi_string_free(char_bits); - } - - //Close Storage - flipper_format_free(ff); - furi_record_close(RECORD_STORAGE); - - furi_string_cat(barcode_data->correct_data, barcode_bits); - furi_string_free(barcode_bits); -} diff --git a/applications/external/barcode_gen/barcode_validator.h b/applications/external/barcode_gen/barcode_validator.h deleted file mode 100644 index 2138124dd..000000000 --- a/applications/external/barcode_gen/barcode_validator.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "barcode_app.h" - -int calculate_check_digit(BarcodeData* barcode_data); -int calculate_ean_upc_check_digit(BarcodeData* barcode_data); -void ean_upc_loader(BarcodeData* barcode_data); -void upc_a_loader(BarcodeData* barcode_data); -void ean_8_loader(BarcodeData* barcode_data); -void ean_13_loader(BarcodeData* barcode_data); -void code_39_loader(BarcodeData* barcode_data); -void code_128_loader(BarcodeData* barcode_data); -void code_128c_loader(BarcodeData* barcode_data); -void codabar_loader(BarcodeData* barcode_data); -void barcode_loader(BarcodeData* barcode_data); diff --git a/applications/external/barcode_gen/encodings.c b/applications/external/barcode_gen/encodings.c deleted file mode 100644 index 764fde796..000000000 --- a/applications/external/barcode_gen/encodings.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "encodings.h" - -const char EAN_13_STRUCTURE_CODES[10][6] = { - "LLLLLL", - "LLGLGG", - "LLGGLG", - "LLGGGL", - "LGLLGG", - "LGGLLG", - "LGGGLL", - "LGLGLG", - "LGLGGL", - "LGGLGL"}; - -const char UPC_EAN_L_CODES[10][8] = { - "0001101", // 0 - "0011001", // 1 - "0010011", // 2 - "0111101", // 3 - "0100011", // 4 - "0110001", // 5 - "0101111", // 6 - "0111011", // 7 - "0110111", // 8 - "0001011" // 9 -}; - -const char EAN_G_CODES[10][8] = { - "0100111", // 0 - "0110011", // 1 - "0011011", // 2 - "0100001", // 3 - "0011101", // 4 - "0111001", // 5 - "0000101", // 6 - "0010001", // 7 - "0001001", // 8 - "0010111" // 9 -}; - -const char UPC_EAN_R_CODES[10][8] = { - "1110010", // 0 - "1100110", // 1 - "1101100", // 2 - "1000010", // 3 - "1011100", // 4 - "1001110", // 5 - "1010000", // 6 - "1000100", // 7 - "1001000", // 8 - "1110100" // 9 -}; \ No newline at end of file diff --git a/applications/external/barcode_gen/encodings.h b/applications/external/barcode_gen/encodings.h deleted file mode 100644 index c5b8d61ff..000000000 --- a/applications/external/barcode_gen/encodings.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -extern const char EAN_13_STRUCTURE_CODES[10][6]; -extern const char UPC_EAN_L_CODES[10][8]; -extern const char EAN_G_CODES[10][8]; -extern const char UPC_EAN_R_CODES[10][8]; \ No newline at end of file diff --git a/applications/external/barcode_gen/images/barcode_10.png b/applications/external/barcode_gen/images/barcode_10.png deleted file mode 100644 index 32d4971ad355874ef62be4cf9eb9586fdef48ad7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2ZGmxy8xzq=w7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`1M1^9%x0_p$%|1Z5c|1OZlS>O>_%)r2R2!t6$HM|;tf-0Uajv*44lM@6) zL{btGSSB$ks7+8-IB?=bM{COorw}H~CLRWdH%zMMw$JzmRL|h)>gTe~DWM4f+!!l! diff --git a/applications/external/barcode_gen/screenshots/Codabar Data Example.png b/applications/external/barcode_gen/screenshots/Codabar Data Example.png deleted file mode 100644 index 907dfd901317f06d5cc034e9a30866959506edb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1672 zcmeAS@N?(olHy`uVBq!ia0y~yU;;8388|?c*QZDWAjMhW5n0T@z;^_M8K-LVNi#68 zS$VoRhE&XXdpA1owgrR3Mb8OOQ)(k!u1){S^4Yu~-EC!r*R{vHGyFV8zV!95JQ6o(bS6V7uj|0R#)|!eqY|!WcvJK z;PcauZ)}q~EO-8-jWOJ$6%TD2mVUL)u9@-J}Ub`mpevDPx>g*NM z=N+$lZ5va|Tl@a|hcoF_Y3B~^J-+Ao`>x$a%y8#fc9roUOT4sYczx)5+1I}f+I=&# z4N=YhGViXp+k4%8&q~*Bzjt2ppa1q<{(sL@S3KKe{n_&Ld?eLXmQ@V)Gk4lsI2=3^ zw@i}3VG09-3nzoa70q|7PpWDfo*N^X{Pj5l!;hOUYM0jil)0*>%=F+33xk3e1H&SG z)V*tW+?g7FBiTuO992oz9sowLeSO^LSf<6toew^)-1ub0=hXh5!gyk9%(48rxx zzA;R@c5BT1#QU)vdMklb2HR2TjU?yO6+^IS?$ePgqBwu-m}h`Pw-oyG#BUuWxQMjwp`H`C51H@tzqsZzs${3X8;Q2IJRX7hhM)-)hcKZE?6d z|C!X!i+kH`*B!S%eEvfhQtbJCv}Dk@{n*YjJ7=rd{_pp0&w6h8D!rleoxn_v?+Hxubln;c{&>;BLef|*QB|8 z5AKQWum0I_kfFea=wgQW0*MHfE4D9YXZZi^*T=87llR%*pKlkPQ5z`%M)B0Xno zJ;V7FgwIp?8!qqswYTBhjpPQ~8HfnK$^1a%+@59juUOt^pBD2MV(V9UHJH8P33ft*|iGXh-O*l}B63G!RV) z9eV&(9?|{*i)$Mo4uloD;#QrdKS;1$8Mf=t7I}=&biPDPzIDq@n?2NZR zsA^yO?QZj76MjjE4Vjx~+7d0_p2XD!b)V+(^UFMylS;wCDy7&f;A zEUR_H!bX^B#`7@fj)E@&pbool=wVMDy10ZFN;)k!HS?QiqUrp4&TJ#GJ2QfWyw2_u zn;5j?0J$#kVvHTe$UxV6EvFXWr{PWODK%!EB#PFaDR@iP&odL)g&Nzt(YxV$G)Z;b zMUHJ6#~LWpTGcOUQ#k`-%tFhrGF|M`XCvdcvN50fCeUM`<>l=VwCONW?ghCYqgzvJ zeuPKj^sh`)rFe;Yy`zCcJ@TMn0p*lTYq4uvuXrX(Qm9eQ4}Vqd*F|W>_jXoJRk%x6 z@BqXU0N);fgRw&Q5~4wsNAx?r#Na>a(U;fjS@qOJmtC%Ej?Aay)(g1wMgt!5HWv|H z=WL^DpjIA9z~xnS0{;mricDER@%wu8X6^l-fkN1~h&ys`YZ=F!n$_e)rrEf) z$^j-htx^#j*tkuEBdrDn)Y(WEnp9>2QgUEhReMrN-@8+$@ITR?-cSwctF^0Kb!iCm zHB6bdFx-M>6N|ZmVH!rUu@H*-n*zs!-{YaAi`jj;RUEaMD~YlUKm5EtH*=iF9+68) z2Bkt4q}U*b?46Ie9Xv5-xs!jXd(>N(O3qP|43(;DT~4`muF;{g&<{J~EfsE|Z8G}W zH7C|T9QC}jmU=gr-ZWMqGopw2f_r;Z&-0RXZYTSoqH3MT%DPm3Sv>pCUu1 zl4@I?uVBV}0Rm?u)^1kThW>X4o}#iiN`ogj@(2Nx@;s=NDieIGGv$w{a z_#U6Zh=k?V+Og0>uA(h_mF;-`e+7b!fHHLJ1eI`N1y=o(R9m`zegAW5 z2HWqO{TUjHIT#$KFfh1q;-PH&><;|>`}z3w>E{)q_NBkQ>7x8z(z1QxM&;*wzD7hp zmznwe#p?}bWtr>G7rwqB7;fJ_v(oL&*Lw3}U$k^IdiTD4yxi_P^-<@H zKUo=^=VDOsVqjRr!k}uGMS-v2W>+w^asLAucWW)t~#lw{dgg;Bx3M%^>bP0l+XkK2Z=AV diff --git a/applications/external/barcode_gen/screenshots/Flipper Box Barcode.png b/applications/external/barcode_gen/screenshots/Flipper Box Barcode.png deleted file mode 100644 index 8dbd7d2a9c1b0564a1a2188e6de48fe9370ad426..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1372 zcmeAS@N?(olHy`uVBq!ia0y~yU;;8388|?c*QZDWAjMhW5n0T@z;^_M8K-LVNi#68 z@_D*AhE&XXd)M3VwE=^}MfN1iHT|v|?5;K6<0%!=419F!rEPl4Dzo{=UTEdb-t)Jv z{vY##=e6J17!2AO7+g3R9H!u*t{i{EYw`N;>*w#^AAc>CYrF63E%)kq`=3jBRd379 z-s`?S_e)jy?Ka!>dtZHATWTxy@t&OQ;c(gGf{#D1`af6j{LRmQ7N6e9ozB1z$iyI^ zI!cWO!H`M?`TOtB-@N_dl<({3@82)~J@s|rtRCTSD|&^IQndW$us;^dx21lGx!qB@ zclEdI^F?m@|6f!WO;4S-@zV^;UF#x`Kk7^V9cszU&>_Uopuxz{QS~aF!Tq^>`q}OC zBA<2qon`ue_w%3Sk$~yq>ajDi76?7FzF9ZuDnE;s&yBsGoVxYB z>H2N@L_AKW^J5uTZKKS~}5{XBJZ&y8os_ov?b{P*>0yYPxoyXX8zo#$ z{h9gt{2Bvsn@6?Mg@5O)j2BYgfF$?abI_=KPCw;X0F^`vvZs1y6c9o=N(pl*O@i*eBpG%&FQh5>$Y!9k4^tp tzSVHE5mM-0wqs^U&_fcySC0H=oN}tJOPWI=6Ifg_c)I$ztaD0e0suzQQWgLJ diff --git a/applications/external/barcode_gen/views/barcode_view.c b/applications/external/barcode_gen/views/barcode_view.c deleted file mode 100644 index 55ab52046..000000000 --- a/applications/external/barcode_gen/views/barcode_view.c +++ /dev/null @@ -1,510 +0,0 @@ -#include "../barcode_app.h" -#include "barcode_view.h" -#include "../encodings.h" - -/** - * @brief Draws a single bit from a barcode at a specified location - * @param canvas - * @param bit a 1 or a 0 to signify a bit of data - * @param x the top left x coordinate - * @param y the top left y coordinate - * @param width the width of the bit - * @param height the height of the bit - */ -static void draw_bit(Canvas* canvas, int bit, int x, int y, int width, int height) { - if(bit == 1) { - canvas_set_color(canvas, ColorBlack); - } else { - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_box(canvas, x, y, width, height); -} - -/** - * -*/ -static void draw_error_str(Canvas* canvas, const char* error) { - canvas_clear(canvas); - canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error); -} - -/** - * @param bits a string of 1's and 0's - * @returns the x coordinate after the bits have been drawn, useful for drawing the next section of bits -*/ -static int draw_bits(Canvas* canvas, const char* bits, int x, int y, int width, int height) { - int bits_length = strlen(bits); - for(int i = 0; i < bits_length; i++) { - char c = bits[i]; - int num = c - '0'; - - draw_bit(canvas, num, x, y, width, height); - - x += width; - } - return x; -} - -/** - * Draws an EAN-8 type barcode, does not check if the barcode is valid - * @param canvas the canvas - * @param barcode_digits the digits in the barcode, must be 8 characters long -*/ -static void draw_ean_8(Canvas* canvas, BarcodeData* barcode_data) { - FuriString* barcode_digits = barcode_data->correct_data; - BarcodeTypeObj* type_obj = barcode_data->type_obj; - - int barcode_length = furi_string_size(barcode_digits); - - int x = type_obj->start_pos; - int y = BARCODE_Y_START; - int width = 1; - int height = BARCODE_HEIGHT; - - //the guard patterns for the beginning, center, ending - const char* end_bits = "101"; - const char* center_bits = "01010"; - - //draw the starting guard pattern - x = draw_bits(canvas, end_bits, x, y, width, height + 5); - - FuriString* code_part = furi_string_alloc(); - - //loop through each digit, find the encoding, and draw it - for(int i = 0; i < barcode_length; i++) { - char current_digit = furi_string_get_char(barcode_digits, i); - - //the actual number and the index of the bits - int index = current_digit - '0'; - //use the L-codes for the first 4 digits and the R-Codes for the last 4 digits - if(i <= 3) { - furi_string_set_str(code_part, UPC_EAN_L_CODES[index]); - } else { - furi_string_set_str(code_part, UPC_EAN_R_CODES[index]); - } - - //convert the current_digit char into a string so it can be printed - char current_digit_string[2]; - snprintf(current_digit_string, 2, "%c", current_digit); - - //set the canvas color to black to print the digit - canvas_set_color(canvas, ColorBlack); - canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string); - - //draw the bits of the barcode - x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height); - - //if the index has reached 3, that means 4 digits have been drawn and now draw the center guard pattern - if(i == 3) { - x = draw_bits(canvas, center_bits, x, y, width, height + 5); - } - } - furi_string_free(code_part); - - //draw the ending guard pattern - x = draw_bits(canvas, end_bits, x, y, width, height + 5); -} - -static void draw_ean_13(Canvas* canvas, BarcodeData* barcode_data) { - FuriString* barcode_digits = barcode_data->correct_data; - BarcodeTypeObj* type_obj = barcode_data->type_obj; - - int barcode_length = furi_string_size(barcode_digits); - - int x = type_obj->start_pos; - int y = BARCODE_Y_START; - int width = 1; - int height = BARCODE_HEIGHT; - - //the guard patterns for the beginning, center, ending - const char* end_bits = "101"; - const char* center_bits = "01010"; - - //draw the starting guard pattern - x = draw_bits(canvas, end_bits, x, y, width, height + 5); - - FuriString* left_structure = furi_string_alloc(); - FuriString* code_part = furi_string_alloc(); - - //loop through each digit, find the encoding, and draw it - for(int i = 0; i < barcode_length; i++) { - char current_digit = furi_string_get_char(barcode_digits, i); - int index = current_digit - '0'; - - if(i == 0) { - furi_string_set_str(left_structure, EAN_13_STRUCTURE_CODES[index]); - - //convert the current_digit char into a string so it can be printed - char current_digit_string[2]; - snprintf(current_digit_string, 2, "%c", current_digit); - - //set the canvas color to black to print the digit - canvas_set_color(canvas, ColorBlack); - canvas_draw_str(canvas, x - 10, y + height + 8, current_digit_string); - - continue; - } else { - //use the L-codes for the first 6 digits and the R-Codes for the last 6 digits - if(i <= 6) { - //get the encoding type at the current barcode bit position - char encoding_type = furi_string_get_char(left_structure, i - 1); - if(encoding_type == 'L') { - furi_string_set_str(code_part, UPC_EAN_L_CODES[index]); - } else { - furi_string_set_str(code_part, EAN_G_CODES[index]); - } - } else { - furi_string_set_str(code_part, UPC_EAN_R_CODES[index]); - } - - //convert the current_digit char into a string so it can be printed - char current_digit_string[2]; - snprintf(current_digit_string, 2, "%c", current_digit); - - //set the canvas color to black to print the digit - canvas_set_color(canvas, ColorBlack); - canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string); - - //draw the bits of the barcode - x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height); - - //if the index has reached 6, that means 6 digits have been drawn and we now draw the center guard pattern - if(i == 6) { - x = draw_bits(canvas, center_bits, x, y, width, height + 5); - } - } - } - - furi_string_free(left_structure); - furi_string_free(code_part); - - //draw the ending guard pattern - x = draw_bits(canvas, end_bits, x, y, width, height + 5); -} - -/** - * Draw a UPC-A barcode -*/ -static void draw_upc_a(Canvas* canvas, BarcodeData* barcode_data) { - FuriString* barcode_digits = barcode_data->correct_data; - BarcodeTypeObj* type_obj = barcode_data->type_obj; - - int barcode_length = furi_string_size(barcode_digits); - - int x = type_obj->start_pos; - int y = BARCODE_Y_START; - int width = 1; - int height = BARCODE_HEIGHT; - - //the guard patterns for the beginning, center, ending - char* end_bits = "101"; - char* center_bits = "01010"; - - //draw the starting guard pattern - x = draw_bits(canvas, end_bits, x, y, width, height + 5); - - FuriString* code_part = furi_string_alloc(); - - //loop through each digit, find the encoding, and draw it - for(int i = 0; i < barcode_length; i++) { - char current_digit = furi_string_get_char(barcode_digits, i); - int index = current_digit - '0'; //convert the number into an int (also the index) - - //use the L-codes for the first 6 digits and the R-Codes for the last 6 digits - if(i <= 5) { - furi_string_set_str(code_part, UPC_EAN_L_CODES[index]); - } else { - furi_string_set_str(code_part, UPC_EAN_R_CODES[index]); - } - - //convert the current_digit char into a string so it can be printed - char current_digit_string[2]; - snprintf(current_digit_string, 2, "%c", current_digit); - - //set the canvas color to black to print the digit - canvas_set_color(canvas, ColorBlack); - canvas_draw_str(canvas, x + 1, y + height + 8, current_digit_string); - - //draw the bits of the barcode - x = draw_bits(canvas, furi_string_get_cstr(code_part), x, y, width, height); - - //if the index has reached 6, that means 6 digits have been drawn and we now draw the center guard pattern - if(i == 5) { - x = draw_bits(canvas, center_bits, x, y, width, height + 5); - } - } - - furi_string_free(code_part); - - //draw the ending guard pattern - x = draw_bits(canvas, end_bits, x, y, width, height + 5); -} - -static void draw_code_39(Canvas* canvas, BarcodeData* barcode_data) { - FuriString* raw_data = barcode_data->raw_data; - FuriString* barcode_digits = barcode_data->correct_data; - //BarcodeTypeObj* type_obj = barcode_data->type_obj; - - int barcode_length = furi_string_size(barcode_digits); - int total_pixels = 0; - - for(int i = 0; i < barcode_length; i++) { - //1 for wide, 0 for narrow - char wide_or_narrow = furi_string_get_char(barcode_digits, i); - int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit - - if(wn_digit == 1) { - total_pixels += 3; - } else { - total_pixels += 1; - } - if((i + 1) % 9 == 0) { - total_pixels += 1; - } - } - - int x = (128 - total_pixels) / 2; - int y = BARCODE_Y_START; - int width = 1; - int height = BARCODE_HEIGHT; - bool filled_in = true; - - //set the canvas color to black to print the digit - canvas_set_color(canvas, ColorBlack); - // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error); - canvas_draw_str_aligned( - canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data)); - - for(int i = 0; i < barcode_length; i++) { - //1 for wide, 0 for narrow - char wide_or_narrow = furi_string_get_char(barcode_digits, i); - int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit - - if(filled_in) { - if(wn_digit == 1) { - x = draw_bits(canvas, "111", x, y, width, height); - } else { - x = draw_bits(canvas, "1", x, y, width, height); - } - filled_in = false; - } else { - if(wn_digit == 1) { - x = draw_bits(canvas, "000", x, y, width, height); - } else { - x = draw_bits(canvas, "0", x, y, width, height); - } - filled_in = true; - } - if((i + 1) % 9 == 0) { - x = draw_bits(canvas, "0", x, y, width, height); - filled_in = true; - } - } -} - -static void draw_code_128(Canvas* canvas, BarcodeData* barcode_data) { - FuriString* raw_data = barcode_data->raw_data; - FuriString* barcode_digits = barcode_data->correct_data; - - int barcode_length = furi_string_size(barcode_digits); - - int x = (128 - barcode_length) / 2; - int y = BARCODE_Y_START; - int width = 1; - int height = BARCODE_HEIGHT; - - x = draw_bits(canvas, furi_string_get_cstr(barcode_digits), x, y, width, height); - - //set the canvas color to black to print the digit - canvas_set_color(canvas, ColorBlack); - // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error); - canvas_draw_str_aligned( - canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data)); -} - -static void draw_codabar(Canvas* canvas, BarcodeData* barcode_data) { - FuriString* raw_data = barcode_data->raw_data; - FuriString* barcode_digits = barcode_data->correct_data; - //BarcodeTypeObj* type_obj = barcode_data->type_obj; - - int barcode_length = furi_string_size(barcode_digits); - int total_pixels = 0; - - for(int i = 0; i < barcode_length; i++) { - //1 for wide, 0 for narrow - char wide_or_narrow = furi_string_get_char(barcode_digits, i); - int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit - - if(wn_digit == 1) { - total_pixels += 3; - } else { - total_pixels += 1; - } - if((i + 1) % 7 == 0) { - total_pixels += 1; - } - } - - int x = (128 - total_pixels) / 2; - int y = BARCODE_Y_START; - int width = 1; - int height = BARCODE_HEIGHT; - bool filled_in = true; - - //set the canvas color to black to print the digit - canvas_set_color(canvas, ColorBlack); - // canvas_draw_str_aligned(canvas, 62, 30, AlignCenter, AlignCenter, error); - canvas_draw_str_aligned( - canvas, 62, y + height + 8, AlignCenter, AlignBottom, furi_string_get_cstr(raw_data)); - - for(int i = 0; i < barcode_length; i++) { - //1 for wide, 0 for narrow - char wide_or_narrow = furi_string_get_char(barcode_digits, i); - int wn_digit = wide_or_narrow - '0'; //wide(1) or narrow(0) digit - - if(filled_in) { - if(wn_digit == 1) { - x = draw_bits(canvas, "111", x, y, width, height); - } else { - x = draw_bits(canvas, "1", x, y, width, height); - } - filled_in = false; - } else { - if(wn_digit == 1) { - x = draw_bits(canvas, "000", x, y, width, height); - } else { - x = draw_bits(canvas, "0", x, y, width, height); - } - filled_in = true; - } - if((i + 1) % 7 == 0) { - x = draw_bits(canvas, "0", x, y, width, height); - filled_in = true; - } - } -} - -static void barcode_draw_callback(Canvas* canvas, void* ctx) { - furi_assert(ctx); - BarcodeModel* barcode_model = ctx; - BarcodeData* data = barcode_model->data; - // const char* barcode_digits =; - - canvas_clear(canvas); - if(data->valid) { - switch(data->type_obj->type) { - case UPCA: - draw_upc_a(canvas, data); - break; - case EAN8: - draw_ean_8(canvas, data); - break; - case EAN13: - draw_ean_13(canvas, data); - break; - case CODE39: - draw_code_39(canvas, data); - break; - case CODE128: - case CODE128C: - draw_code_128(canvas, data); - break; - case CODABAR: - draw_codabar(canvas, data); - break; - case UNKNOWN: - default: - break; - } - } else { - switch(data->reason) { - case WrongNumberOfDigits: - draw_error_str(canvas, "Wrong # of characters"); - break; - case InvalidCharacters: - draw_error_str(canvas, "Invalid characters"); - break; - case UnsupportedType: - draw_error_str(canvas, "Unsupported barcode type"); - break; - case FileOpening: - draw_error_str(canvas, "Could not open file"); - break; - case InvalidFileData: - draw_error_str(canvas, "Invalid file data"); - break; - case MissingEncodingTable: - draw_error_str(canvas, "Missing encoding table"); - break; - case EncodingTableError: - draw_error_str(canvas, "Encoding table error"); - break; - default: - draw_error_str(canvas, "Could not read barcode data"); - break; - } - } -} - -bool barcode_input_callback(InputEvent* input_event, void* ctx) { - UNUSED(ctx); - //furi_assert(ctx); - - //Barcode* test_view_object = ctx; - - if(input_event->key == InputKeyBack) { - return false; - } else { - return true; - } -} - -Barcode* barcode_view_allocate(BarcodeApp* barcode_app) { - furi_assert(barcode_app); - - Barcode* barcode = malloc(sizeof(Barcode)); - - barcode->view = view_alloc(); - barcode->barcode_app = barcode_app; - - view_set_context(barcode->view, barcode); - view_allocate_model(barcode->view, ViewModelTypeLocking, sizeof(BarcodeModel)); - view_set_draw_callback(barcode->view, barcode_draw_callback); - view_set_input_callback(barcode->view, barcode_input_callback); - - return barcode; -} - -void barcode_free_model(Barcode* barcode) { - with_view_model( - barcode->view, - BarcodeModel * model, - { - if(model->file_path != NULL) { - furi_string_free(model->file_path); - } - if(model->data != NULL) { - if(model->data->raw_data != NULL) { - furi_string_free(model->data->raw_data); - } - if(model->data->correct_data != NULL) { - furi_string_free(model->data->correct_data); - } - free(model->data); - } - }, - false); -} - -void barcode_free(Barcode* barcode) { - furi_assert(barcode); - - barcode_free_model(barcode); - view_free(barcode->view); - free(barcode); -} - -View* barcode_get_view(Barcode* barcode) { - furi_assert(barcode); - return barcode->view; -} diff --git a/applications/external/barcode_gen/views/barcode_view.h b/applications/external/barcode_gen/views/barcode_view.h deleted file mode 100644 index 828428c08..000000000 --- a/applications/external/barcode_gen/views/barcode_view.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -typedef struct BarcodeApp BarcodeApp; - -typedef struct { - View* view; - BarcodeApp* barcode_app; -} Barcode; - -typedef struct { - FuriString* file_path; - BarcodeData* data; -} BarcodeModel; - -Barcode* barcode_view_allocate(BarcodeApp* barcode_app); - -void barcode_free_model(Barcode* barcode); - -void barcode_free(Barcode* barcode); - -View* barcode_get_view(Barcode* barcode); diff --git a/applications/external/barcode_gen/views/create_view.c b/applications/external/barcode_gen/views/create_view.c deleted file mode 100644 index e4e489113..000000000 --- a/applications/external/barcode_gen/views/create_view.c +++ /dev/null @@ -1,494 +0,0 @@ -#include "../barcode_app.h" -#include "create_view.h" -#include - -#define LINE_HEIGHT 16 -#define TEXT_PADDING 4 -#define TOTAL_MENU_ITEMS 5 - -typedef enum { - TypeMenuItem, - FileNameMenuItem, - BarcodeDataMenuItem, - SaveMenuButton, - DeleteMenuButton -} MenuItems; - -/** - * Took this function from blackjack - * @author @teeebor -*/ -void draw_menu_item( - Canvas* const canvas, - const char* text, - const char* value, - int y, - bool left_caret, - bool right_caret, - bool selected) { - UNUSED(selected); - if(y < 0 || y >= 64) { - return; - } - - if(selected) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT); - canvas_set_color(canvas, ColorWhite); - } - - canvas_draw_str_aligned(canvas, 4, y + TEXT_PADDING, AlignLeft, AlignTop, text); - if(left_caret) { - canvas_draw_str_aligned(canvas, 60, y + TEXT_PADDING, AlignLeft, AlignTop, "<"); - } - - canvas_draw_str_aligned(canvas, 90, y + TEXT_PADDING, AlignCenter, AlignTop, value); - if(right_caret) { - canvas_draw_str_aligned(canvas, 120, y + TEXT_PADDING, AlignRight, AlignTop, ">"); - } - - canvas_set_color(canvas, ColorBlack); -} - -void draw_button(Canvas* const canvas, const char* text, int y, bool selected) { - if(selected) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT); - canvas_set_color(canvas, ColorWhite); - } - - canvas_draw_str_aligned(canvas, 64, y + TEXT_PADDING, AlignCenter, AlignTop, text); - - canvas_set_color(canvas, ColorBlack); -} - -static void app_draw_callback(Canvas* canvas, void* ctx) { - furi_assert(ctx); - - CreateViewModel* create_view_model = ctx; - - BarcodeTypeObj* type_obj = create_view_model->barcode_type; - if(create_view_model->barcode_type == NULL) { - return; - } - BarcodeType selected_type = type_obj->type; - - int selected_menu_item = create_view_model->selected_menu_item; - - int total_menu_items = create_view_model->mode == EditMode ? TOTAL_MENU_ITEMS : - TOTAL_MENU_ITEMS - 1; - - int startY = 0; - - //the menu items index that is/would be in view - //int current_last_menu_item = selected_menu_item + 3; - if(selected_menu_item > 1) { - int offset = 2; - if(selected_menu_item + offset > total_menu_items) { - offset = 3; - } - startY -= (LINE_HEIGHT * (selected_menu_item - offset)); - } - - //ensure that the scroll height is atleast 1 - int scrollHeight = ceil(64.0 / total_menu_items); - int scrollPos = scrollHeight * selected_menu_item; - - canvas_set_color(canvas, ColorBlack); - //draw the scroll bar box - canvas_draw_box(canvas, 125, scrollPos, 3, scrollHeight); - //draw the scroll bar track - canvas_draw_box(canvas, 126, 0, 1, 64); - - draw_menu_item( - canvas, - "Type", - type_obj->name, - TypeMenuItem * LINE_HEIGHT + startY, - selected_type > 0, - selected_type < NUMBER_OF_BARCODE_TYPES - 2, - selected_menu_item == TypeMenuItem); - - draw_menu_item( - canvas, - "Name", - furi_string_empty(create_view_model->file_name) ? - "--" : - furi_string_get_cstr(create_view_model->file_name), - FileNameMenuItem * LINE_HEIGHT + startY, - false, - false, - selected_menu_item == FileNameMenuItem); - - draw_menu_item( - canvas, - "Data", - furi_string_empty(create_view_model->barcode_data) ? - "--" : - furi_string_get_cstr(create_view_model->barcode_data), - BarcodeDataMenuItem * LINE_HEIGHT + startY, - false, - false, - selected_menu_item == BarcodeDataMenuItem); - - draw_button( - canvas, - "Save", - SaveMenuButton * LINE_HEIGHT + startY, - selected_menu_item == SaveMenuButton); - - if(create_view_model->mode == EditMode) { - draw_button( - canvas, - "Delete", - DeleteMenuButton * LINE_HEIGHT + startY, - selected_menu_item == DeleteMenuButton); - } -} - -void text_input_callback(void* ctx) { - CreateView* create_view_object = ctx; - - with_view_model( - create_view_object->view, - CreateViewModel * model, - { - if(create_view_object->setter == FileNameSetter) { - furi_string_set_str(model->file_name, create_view_object->input); - } - if(create_view_object->setter == BarcodeDataSetter) { - furi_string_set_str(model->barcode_data, create_view_object->input); - } - }, - true); - - view_dispatcher_switch_to_view( - create_view_object->barcode_app->view_dispatcher, CreateBarcodeView); -} - -static bool app_input_callback(InputEvent* input_event, void* ctx) { - furi_assert(ctx); - - if(input_event->key == InputKeyBack) { - return false; - } - - CreateView* create_view_object = ctx; - - //get the currently selected menu item from the model - int selected_menu_item = 0; - BarcodeTypeObj* barcode_type = NULL; - FuriString* file_name; - FuriString* barcode_data; - CreateMode mode; - - with_view_model( - create_view_object->view, - CreateViewModel * model, - { - selected_menu_item = model->selected_menu_item; - barcode_type = model->barcode_type; - file_name = model->file_name; - barcode_data = model->barcode_data; - mode = model->mode; - }, - true); - - int total_menu_items = mode == EditMode ? TOTAL_MENU_ITEMS : TOTAL_MENU_ITEMS - 1; - - if(input_event->type == InputTypePress) { - if(input_event->key == InputKeyUp && selected_menu_item > 0) { - selected_menu_item--; - } else if(input_event->key == InputKeyDown && selected_menu_item < total_menu_items - 1) { - selected_menu_item++; - } else if(input_event->key == InputKeyLeft) { - if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type - if(barcode_type->type > 0) { - barcode_type = barcode_type_objs[barcode_type->type - 1]; - } - } - } else if(input_event->key == InputKeyRight) { - if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type - if(barcode_type->type < NUMBER_OF_BARCODE_TYPES - 2) { - barcode_type = barcode_type_objs[barcode_type->type + 1]; - } - } - } else if(input_event->key == InputKeyOk) { - if(selected_menu_item == FileNameMenuItem && barcode_type != NULL) { - create_view_object->setter = FileNameSetter; - - snprintf( - create_view_object->input, - sizeof(create_view_object->input), - "%s", - furi_string_get_cstr(file_name)); - - text_input_set_result_callback( - create_view_object->barcode_app->text_input, - text_input_callback, - create_view_object, - create_view_object->input, - TEXT_BUFFER_SIZE - BARCODE_EXTENSION_LENGTH, //remove the barcode length - //clear default text - false); - text_input_set_header_text( - create_view_object->barcode_app->text_input, "File Name"); - - view_dispatcher_switch_to_view( - create_view_object->barcode_app->view_dispatcher, TextInputView); - } - if(selected_menu_item == BarcodeDataMenuItem && barcode_type != NULL) { - create_view_object->setter = BarcodeDataSetter; - - snprintf( - create_view_object->input, - sizeof(create_view_object->input), - "%s", - furi_string_get_cstr(barcode_data)); - - text_input_set_result_callback( - create_view_object->barcode_app->text_input, - text_input_callback, - create_view_object, - create_view_object->input, - TEXT_BUFFER_SIZE, - //clear default text - false); - text_input_set_header_text( - create_view_object->barcode_app->text_input, "Barcode Data"); - - view_dispatcher_switch_to_view( - create_view_object->barcode_app->view_dispatcher, TextInputView); - } - if(selected_menu_item == SaveMenuButton && barcode_type != NULL) { - save_barcode(create_view_object); - } - if(selected_menu_item == DeleteMenuButton && barcode_type != NULL) { - if(mode == EditMode) { - remove_barcode(create_view_object); - } else if(mode == NewMode) { - view_dispatcher_switch_to_view( - create_view_object->barcode_app->view_dispatcher, MainMenuView); - } - } - } - } - - //change the currently selected menu item - with_view_model( - create_view_object->view, - CreateViewModel * model, - { - model->selected_menu_item = selected_menu_item; - model->barcode_type = barcode_type; - }, - true); - - return true; -} - -CreateView* create_view_allocate(BarcodeApp* barcode_app) { - furi_assert(barcode_app); - - CreateView* create_view_object = malloc(sizeof(CreateView)); - - create_view_object->view = view_alloc(); - create_view_object->barcode_app = barcode_app; - - view_set_context(create_view_object->view, create_view_object); - view_allocate_model(create_view_object->view, ViewModelTypeLocking, sizeof(CreateViewModel)); - view_set_draw_callback(create_view_object->view, app_draw_callback); - view_set_input_callback(create_view_object->view, app_input_callback); - - return create_view_object; -} - -void create_view_free_model(CreateView* create_view_object) { - with_view_model( - create_view_object->view, - CreateViewModel * model, - { - if(model->file_path != NULL) { - furi_string_free(model->file_path); - } - if(model->file_name != NULL) { - furi_string_free(model->file_name); - } - if(model->barcode_data != NULL) { - furi_string_free(model->barcode_data); - } - }, - true); -} - -void remove_barcode(CreateView* create_view_object) { - Storage* storage = furi_record_open(RECORD_STORAGE); - - bool success = false; - - with_view_model( - create_view_object->view, - CreateViewModel * model, - { - FURI_LOG_I(TAG, "Attempting to remove file"); - if(model->file_path != NULL) { - FURI_LOG_I(TAG, "Removing File: %s", furi_string_get_cstr(model->file_path)); - if(storage_simply_remove(storage, furi_string_get_cstr(model->file_path))) { - FURI_LOG_I( - TAG, - "File: \"%s\" was successfully removed", - furi_string_get_cstr(model->file_path)); - success = true; - } else { - FURI_LOG_E(TAG, "Unable to remove file!"); - success = false; - } - } else { - FURI_LOG_E(TAG, "Could not remove barcode file"); - success = false; - } - }, - true); - furi_record_close(RECORD_STORAGE); - - with_view_model( - create_view_object->barcode_app->message_view->view, - MessageViewModel * model, - { - if(success) { - model->message = "File Deleted"; - } else { - model->message = "Could not delete file"; - } - }, - true); - - view_dispatcher_switch_to_view( - create_view_object->barcode_app->view_dispatcher, MessageErrorView); -} - -void save_barcode(CreateView* create_view_object) { - BarcodeTypeObj* barcode_type = NULL; - FuriString* file_path; //this may be empty - FuriString* file_name; - FuriString* barcode_data; - CreateMode mode; - - with_view_model( - create_view_object->view, - CreateViewModel * model, - { - file_path = model->file_path; - file_name = model->file_name; - barcode_data = model->barcode_data; - barcode_type = model->barcode_type; - mode = model->mode; - }, - true); - - if(file_name == NULL || furi_string_empty(file_name)) { - FURI_LOG_E(TAG, "File Name cannot be empty"); - return; - } - if(barcode_data == NULL || furi_string_empty(barcode_data)) { - FURI_LOG_E(TAG, "Barcode Data cannot be empty"); - return; - } - if(barcode_type == NULL) { - FURI_LOG_E(TAG, "Type not defined"); - return; - } - - bool success = false; - - FuriString* full_file_path = furi_string_alloc_set(DEFAULT_USER_BARCODES); - furi_string_push_back(full_file_path, '/'); - furi_string_cat(full_file_path, file_name); - furi_string_cat_str(full_file_path, BARCODE_EXTENSION); - - Storage* storage = furi_record_open(RECORD_STORAGE); - - if(mode == EditMode) { - if(!furi_string_empty(file_path)) { - if(!furi_string_equal(file_path, full_file_path)) { - FS_Error error = storage_common_rename( - storage, - furi_string_get_cstr(file_path), - furi_string_get_cstr(full_file_path)); - if(error != FSE_OK) { - FURI_LOG_E(TAG, "Rename error: %s", storage_error_get_desc(error)); - } else { - FURI_LOG_I(TAG, "Rename Success"); - } - } - } - } - - FlipperFormat* ff = flipper_format_file_alloc(storage); - - FURI_LOG_I(TAG, "Saving Barcode to: %s", furi_string_get_cstr(full_file_path)); - - bool file_opened_status = false; - if(mode == NewMode) { - file_opened_status = - flipper_format_file_open_new(ff, furi_string_get_cstr(full_file_path)); - } else if(mode == EditMode) { - file_opened_status = - flipper_format_file_open_always(ff, furi_string_get_cstr(full_file_path)); - } - - if(file_opened_status) { - // Filetype: Barcode - // Version: 1 - - // # Types - UPC-A, EAN-8, EAN-13, CODE-39 - // Type: CODE-39 - // Data: AB - flipper_format_write_string_cstr(ff, "Filetype", "Barcode"); - - flipper_format_write_string_cstr(ff, "Version", FILE_VERSION); - - flipper_format_write_comment_cstr( - ff, "Types - UPC-A, EAN-8, EAN-13, CODE-39, CODE-128, Codabar"); - - flipper_format_write_string_cstr(ff, "Type", barcode_type->name); - - flipper_format_write_string_cstr(ff, "Data", furi_string_get_cstr(barcode_data)); - - success = true; - } else { - FURI_LOG_E(TAG, "Save error"); - success = false; - } - furi_string_free(full_file_path); - flipper_format_free(ff); - furi_record_close(RECORD_STORAGE); - - with_view_model( - create_view_object->barcode_app->message_view->view, - MessageViewModel * model, - { - if(success) { - model->message = "File Saved!"; - } else { - model->message = "A saving error has occurred"; - } - }, - true); - - view_dispatcher_switch_to_view( - create_view_object->barcode_app->view_dispatcher, MessageErrorView); -} - -void create_view_free(CreateView* create_view_object) { - furi_assert(create_view_object); - - create_view_free_model(create_view_object); - view_free(create_view_object->view); - free(create_view_object); -} - -View* create_get_view(CreateView* create_view_object) { - furi_assert(create_view_object); - return create_view_object->view; -} \ No newline at end of file diff --git a/applications/external/barcode_gen/views/create_view.h b/applications/external/barcode_gen/views/create_view.h deleted file mode 100644 index 6063786d9..000000000 --- a/applications/external/barcode_gen/views/create_view.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include - -typedef struct BarcodeApp BarcodeApp; - -typedef enum { - FileNameSetter, - BarcodeDataSetter -} InputSetter; //what value to set for the text input view - -typedef enum { - EditMode, - - NewMode -} CreateMode; - -typedef struct { - View* view; - BarcodeApp* barcode_app; - - InputSetter setter; - char input[TEXT_BUFFER_SIZE]; -} CreateView; - -typedef struct { - int selected_menu_item; - - CreateMode mode; - BarcodeTypeObj* barcode_type; - FuriString* file_path; //the current file that is opened - FuriString* file_name; - FuriString* barcode_data; -} CreateViewModel; - -CreateView* create_view_allocate(BarcodeApp* barcode_app); - -void remove_barcode(CreateView* create_view_object); - -void save_barcode(CreateView* create_view_object); - -void create_view_free_model(CreateView* create_view_object); - -void create_view_free(CreateView* create_view_object); - -View* create_get_view(CreateView* create_view_object); diff --git a/applications/external/barcode_gen/views/message_view.c b/applications/external/barcode_gen/views/message_view.c deleted file mode 100644 index 3a9aa90b3..000000000 --- a/applications/external/barcode_gen/views/message_view.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "../barcode_app.h" -#include "message_view.h" - -static void app_draw_callback(Canvas* canvas, void* ctx) { - furi_assert(ctx); - - MessageViewModel* message_view_model = ctx; - - canvas_clear(canvas); - if(message_view_model->message != NULL) { - canvas_draw_str_aligned( - canvas, 62, 30, AlignCenter, AlignCenter, message_view_model->message); - } - - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 100, 52, 28, 12); - canvas_set_color(canvas, ColorWhite); - canvas_draw_str_aligned(canvas, 114, 58, AlignCenter, AlignCenter, "OK"); -} - -static bool app_input_callback(InputEvent* input_event, void* ctx) { - furi_assert(ctx); - - MessageView* message_view_object = ctx; - - if(input_event->key == InputKeyBack) { - view_dispatcher_switch_to_view( - message_view_object->barcode_app->view_dispatcher, MainMenuView); - } - if(input_event->type == InputTypeShort) { - if(input_event->key == InputKeyOk) { - view_dispatcher_switch_to_view( - message_view_object->barcode_app->view_dispatcher, MainMenuView); - } - } - - return true; -} - -MessageView* message_view_allocate(BarcodeApp* barcode_app) { - furi_assert(barcode_app); - - MessageView* message_view_object = malloc(sizeof(MessageView)); - - message_view_object->view = view_alloc(); - message_view_object->barcode_app = barcode_app; - - view_set_context(message_view_object->view, message_view_object); - view_allocate_model(message_view_object->view, ViewModelTypeLocking, sizeof(MessageViewModel)); - view_set_draw_callback(message_view_object->view, app_draw_callback); - view_set_input_callback(message_view_object->view, app_input_callback); - - return message_view_object; -} - -void message_view_free(MessageView* message_view_object) { - furi_assert(message_view_object); - - view_free(message_view_object->view); - free(message_view_object); -} - -View* message_get_view(MessageView* message_view_object) { - furi_assert(message_view_object); - return message_view_object->view; -} \ No newline at end of file diff --git a/applications/external/barcode_gen/views/message_view.h b/applications/external/barcode_gen/views/message_view.h deleted file mode 100644 index 33acc3d0c..000000000 --- a/applications/external/barcode_gen/views/message_view.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -typedef struct BarcodeApp BarcodeApp; - -typedef struct { - View* view; - BarcodeApp* barcode_app; -} MessageView; - -typedef struct { - const char* message; -} MessageViewModel; - -MessageView* message_view_allocate(BarcodeApp* barcode_app); - -void message_view_free_model(MessageView* message_view_object); - -void message_view_free(MessageView* message_view_object); - -View* message_get_view(MessageView* message_view_object); diff --git a/applications/external/blackjack/application.fam b/applications/external/blackjack/application.fam deleted file mode 100644 index 271620d72..000000000 --- a/applications/external/blackjack/application.fam +++ /dev/null @@ -1,15 +0,0 @@ -App( - appid="blackjack", - name="Blackjack", - apptype=FlipperAppType.EXTERNAL, - entry_point="blackjack_app", - requires=["gui","storage","canvas"], - stack_size=2 * 1024, - order=30, - fap_icon="blackjack_10px.png", - fap_category="Games", - fap_icon_assets="assets", - fap_author="@teeebor", - fap_version="1.0", - fap_description="Blackjack Game", -) \ No newline at end of file diff --git a/applications/external/blackjack/assets/blackjack.png b/applications/external/blackjack/assets/blackjack.png deleted file mode 100644 index bb367f28e971c12e2cb31c99a4aeec27b403c4d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1324 zcmV+{1=IS8P)Px(=1D|BRCt{2TFH{*APmLM{QoDLIgnB*+7M!KNc$bS(tw4~j_f1noF3eV^KJ&d z2w;mVjq-Rr;9fi)a5EkSeBd~a#XCLG4=b(s&J=}n zI8Vs1#zSgF6@;?|FztN(1liK#^3EwyULI)l%Xp0D6a^d%;sTq9=gvv!3{RNnd16~J zYA37%-cz!+XyqvYTij%doYr34%0}x-{(1oaCX$7eKsK`Xov46};D1EZhp|PRMV+iT zUU<8%D@kI-DZwB@@UMqaC%Dh%oX_(lBMJF9JS84OJgT8Neq|M@X|vcVJ+%#7Bg9#P zi5>t6fXmUS8LG+FX2%Ss#!-9xTbzHhhDcSfP0Y ziuHGY2(r_uF|MI{Fux*gYIrbq_x5L|d%B@Gd^>s7LP}yKpfmbci+7%u_$>dowccHk z1GFjpgR*s=WC|xqqt3&W$(lkF4+6Eo4NK%9aNAhq0i3N;%q1mBR`0ZSQb|f2*Qk+_ z3>a$)KC!#rqHgFUG^IN~P2RWQwG$YS_>>>RJYcvEJcVW}omM6_cO>K0(%C>nZ<#e@ z?(zTwvFasB9$=rY(+eXW&{XZ+g_!g){1@7u<45iR+_=OfHWru$95Xu zH%1Q;+EK%yh7eE8yB*{ zz-vCP5|ABPNfwvCC2x_(L4{4N!yas-0Dv>-Lp;I1as4iuu=b$?QR=|Nxt{_6)j&~g zLcoQVw?nE;x>49q0lZ$sQ`pSQTY+S%N((re9}F)VcPc|WzrwVMSJi`6JCKc?6rha{ z=_m5&PfF5Bg4#v_RrDC9{p{3c*M2zwz+1$vkHDb)>{J{59S<0Yu~FW}HXhJvDAmWS zSFJD20o9Uuwkc`+4{1jap;MthL$NdD-pZ%PEQa&hM9`%GQkIER_&(_oQ>>^6D8OKz zGsAz_%ou`(wss(arWKe&EU%_ypba{!CXZOu72+P+#&Ac) zZh>tO8jyC=cph+n0vLe5yjO~k038mpk}M+)l7gxZf}bYhFp5XG7sq_OADqLK((CoY zj1}KCxFS7po|eUvayE?f#=i~4-0)@=xAFBC4$dKAtkn@gnmC{VedB0vQU8QEBkP1`D7tN0K1CkMLO_x&XR0000Px$Q%OWYR7i=%R?89tAqWG<|NmusprtekbbIM?*iqyWAUf;k9CtUfbB>wCI=3Nd zkf&e|zMV0MyGy;9?L3P`+JU}vH`1-1@>YgochGRI-4)H10h(B| zu?)rikl@mNhYz!-W=#V~0KAmRK+~04F`1bkqP(mdn%5c6D6h*(rq>F9=Af6x^3-1f zL5A%URz$fnekt74ZgADU;s&o+tZ=(d4uHb%6NF*|d6@v*Hbn?_24XTYSs2`#s+{HB zt&Xj%;ir{>eoZXlre_D_j|PV6WL%OHRp!4I>_|q!^r> z=1*6(z84Z7Kc`qgR-c`O1<-b2v9y7hT^W!Oe-q>z!=01&K$^jK=yvNbnTDWEt0U-ExF~o%D-WCQ~}Km-Ad{Hoy_b3ZonT2xiD0D00000NkvXXu0mjf DEMm7A diff --git a/applications/external/blackjack/assets/endscreen.png b/applications/external/blackjack/assets/endscreen.png deleted file mode 100644 index 7a3abc927a5294323ffd3affee953e3a24efd14d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1241 zcmV;~1Sb25P)Px(lSxEDRCt{2TidpyFbFlS`TtMWd9cG!Hn+q}(zN@jM^ixVU{Bfiefgmu*4_eq zJAMY}jGqCPWm!7r;g$3L+Imn*>BjnNl|BgGQqa#B&qGE)$6$O6L;2YsymX;_`@UNi zJ-9q?@6kL=F#tixT5A|b`$+t2@u~r+TvSl4%8v6L<9(=9W8q~0ps=j7v^ z45Ksnh#>o)f-qwUWznDOs`pOh^5O%Y;fz$1sFJMfDqMjFSt+HIBAf$7vJ}w^|5a)E zOJT5-g-Q_wtov`W2yfhu}C!ts;a@F)s;^rt6N@tXtx{oP}td4xiYzP0&Pj zV-_6;UKeycjM^`r_bE&=n4Llhb&u@n7CZwSITx>s%$wvSl+`u>9z@jA($~jT`7`q9 z20(wr0wA;*YS4)xwF#~bg^=bQKv&4$Jz&-VY$SMgwm4-Fs)l%}51@*nb^&*T!p^UA zzIQ5qJ!Y{=B8c=LddPB_iTc(>Z@siiU5fT$cteKbo5pq_b@scG9JkbzQ?H zg2QP32*6=YEy4m+lhu}w!h#~ep935l%vOXELi@anRkhYOS~iE;v<48`!7S`3nmf49 zg@UPh5}rQ=aRli&@#?}Vr%oyMtMs%HPzBs(TYHy=#1#yNUlIXgyJc+#9WSN>wHm8i zMNE$ZuaBI)6F+9%rCZy!S^HxL)21NH?`YXT12EuqFd_i(IB zGo}sWV2B8~)=6(F25S4ni+TR*T*BuJpzdUz;cit+F&r#xwSdP^gJLTeilKT&hX@$$ zttXd9l}&W6;=6d#;UZ>^rF#wFKB&GaGsxPs@!N%0}v(W`t_sVI*9y)=>{bRNX$ z!NSCieu_h^byB_CkOtK`i;r?B4-R?)LrlVaDCL^PGwPq#V#nd7!Ve1)%<^<@2CL*E zIvAk2Md4XE3>Tl2n$5#d2saI67;(~}e7gbA{%sXlBo>EV#!k&n;8PZHnIU+)0jeI= z%0E6qt9sY6&P$j-AEN&xoKX|cyjC-AT|Xm6*ms`CSS|UI$i@0N^Y6C~SdcGQ*|iwU z097Mp*!g^fF>W!yn2&lAH$BuXHNfxHJ`~n4d=h~H3WE>@g_|DMgovYfo~CC2i>_c< z-v3jsS(L%S_ek6${m2M&fH?=K6s;mDgurQKf?R|%3iiafccQx%*~S{cx0rml#eYIf zo0Dm3a#dSJkMI8$xP1~T<{{PyG!`ZeXmDc%1nv`@m}`p{{R=LiUa#`hf0 z=;9X#79M&ZFq8JLb=ijuFjLOC)|s9#z?AvtDwp~T<$Jc0(92j>00000NkvXXu0mjf Dj4@Yv diff --git a/applications/external/blackjack/blackjack.c b/applications/external/blackjack/blackjack.c deleted file mode 100644 index c4ef8d3a7..000000000 --- a/applications/external/blackjack/blackjack.c +++ /dev/null @@ -1,633 +0,0 @@ - -#include -#include -#include -#include -#include - -#include -#include "util.h" -#include "defines.h" -#include "common/card.h" -#include "common/dml.h" -#include "common/queue.h" -#include "util.h" -#include "ui.h" - -#include "blackjack_icons.h" - -#define DEALER_MAX 17 - -void start_round(GameState* game_state); - -void init(GameState* game_state); - -static void draw_ui(Canvas* const canvas, const GameState* game_state) { - draw_money(canvas, game_state->player_score); - - draw_score(canvas, true, hand_count(game_state->player_cards, game_state->player_card_count)); - - if(!game_state->queue_state.running && game_state->state == GameStatePlay) { - render_menu(game_state->menu, canvas, 2, 47); - } -} - -static void render_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - const GameState* game_state = ctx; - furi_mutex_acquire(game_state->mutex, FuriWaitForever); - - canvas_set_color(canvas, ColorBlack); - canvas_draw_frame(canvas, 0, 0, 128, 64); - - if(game_state->state == GameStateStart) { - canvas_draw_icon(canvas, 0, 0, &I_blackjack); - } - if(game_state->state == GameStateGameOver) { - canvas_draw_icon(canvas, 0, 0, &I_endscreen); - } - - if(game_state->state == GameStatePlay || game_state->state == GameStateDealer) { - if(game_state->state == GameStatePlay) - draw_player_scene(canvas, game_state); - else - draw_dealer_scene(canvas, game_state); - render_queue(&(game_state->queue_state), game_state, canvas); - draw_ui(canvas, game_state); - } else if(game_state->state == GameStateSettings) { - settings_page(canvas, game_state); - } - - furi_mutex_release(game_state->mutex); -} - -//region card draw -Card draw_card(GameState* game_state) { - Card c = game_state->deck.cards[game_state->deck.index]; - game_state->deck.index++; - return c; -} - -void drawPlayerCard(void* ctx) { - GameState* game_state = ctx; - Card c = draw_card(game_state); - game_state->player_cards[game_state->player_card_count] = c; - game_state->player_card_count++; - if(game_state->player_score < game_state->settings.round_price || game_state->doubled) { - set_menu_state(game_state->menu, 0, false); - } -} - -void drawDealerCard(void* ctx) { - GameState* game_state = ctx; - Card c = draw_card(game_state); - game_state->dealer_cards[game_state->dealer_card_count] = c; - game_state->dealer_card_count++; -} -//endregion - -//region queue callbacks -void to_lose_state(const void* ctx, Canvas* const canvas) { - const GameState* game_state = ctx; - if(game_state->settings.message_duration == 0) return; - popup_frame(canvas); - elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You lost"); -} - -void to_bust_state(const void* ctx, Canvas* const canvas) { - const GameState* game_state = ctx; - if(game_state->settings.message_duration == 0) return; - popup_frame(canvas); - elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Busted!"); -} - -void to_draw_state(const void* ctx, Canvas* const canvas) { - const GameState* game_state = ctx; - if(game_state->settings.message_duration == 0) return; - popup_frame(canvas); - elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Draw"); -} - -void to_dealer_turn(const void* ctx, Canvas* const canvas) { - const GameState* game_state = ctx; - if(game_state->settings.message_duration == 0) return; - popup_frame(canvas); - elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Dealers turn"); -} - -void to_win_state(const void* ctx, Canvas* const canvas) { - const GameState* game_state = ctx; - if(game_state->settings.message_duration == 0) return; - popup_frame(canvas); - elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "You win"); -} - -void to_start(const void* ctx, Canvas* const canvas) { - const GameState* game_state = ctx; - if(game_state->settings.message_duration == 0) return; - popup_frame(canvas); - elements_multiline_text_aligned(canvas, 64, 22, AlignCenter, AlignCenter, "Round started"); -} - -void before_start(void* ctx) { - GameState* game_state = ctx; - game_state->dealer_card_count = 0; - game_state->player_card_count = 0; -} - -void start(void* ctx) { - GameState* game_state = ctx; - start_round(game_state); -} - -void draw(void* ctx) { - GameState* game_state = ctx; - game_state->player_score += game_state->bet; - game_state->bet = 0; - enqueue( - &(game_state->queue_state), - game_state, - start, - before_start, - to_start, - game_state->settings.message_duration); -} - -void game_over(void* ctx) { - GameState* game_state = ctx; - game_state->state = GameStateGameOver; -} - -void lose(void* ctx) { - GameState* game_state = ctx; - game_state->state = GameStatePlay; - game_state->bet = 0; - if(game_state->player_score >= game_state->settings.round_price) { - enqueue( - &(game_state->queue_state), - game_state, - start, - before_start, - to_start, - game_state->settings.message_duration); - } else { - enqueue(&(game_state->queue_state), game_state, game_over, NULL, NULL, 0); - } -} - -void win(void* ctx) { - GameState* game_state = ctx; - game_state->state = GameStatePlay; - game_state->player_score += game_state->bet * 2; - game_state->bet = 0; - enqueue( - &(game_state->queue_state), - game_state, - start, - before_start, - to_start, - game_state->settings.message_duration); -} - -void dealerTurn(void* ctx) { - GameState* game_state = ctx; - game_state->state = GameStateDealer; -} - -float animationTime(const GameState* game_state) { - return (float)(furi_get_tick() - game_state->queue_state.start) / - (float)(game_state->settings.animation_duration); -} - -void dealer_card_animation(const void* ctx, Canvas* const canvas) { - const GameState* game_state = ctx; - float t = animationTime(game_state); - - Card animatingCard = game_state->deck.cards[game_state->deck.index]; - if(game_state->dealer_card_count > 1) { - Vector end = card_pos_at_index(game_state->dealer_card_count); - draw_card_animation(animatingCard, (Vector){0, 64}, (Vector){0, 32}, end, t, true, canvas); - } else { - draw_card_animation( - animatingCard, - (Vector){32, -CARD_HEIGHT}, - (Vector){64, 32}, - (Vector){2, 2}, - t, - false, - canvas); - } -} - -void dealer_back_card_animation(const void* ctx, Canvas* const canvas) { - const GameState* game_state = ctx; - float t = animationTime(game_state); - - Vector currentPos = - quadratic_2d((Vector){32, -CARD_HEIGHT}, (Vector){64, 32}, (Vector){13, 5}, t); - draw_card_back_at(currentPos.x, currentPos.y, canvas); -} - -void player_card_animation(const void* ctx, Canvas* const canvas) { - const GameState* game_state = ctx; - float t = animationTime(game_state); - - Card animatingCard = game_state->deck.cards[game_state->deck.index]; - Vector end = card_pos_at_index(game_state->player_card_count); - - draw_card_animation( - animatingCard, (Vector){32, -CARD_HEIGHT}, (Vector){0, 32}, end, t, true, canvas); -} -//endregion - -void player_tick(GameState* game_state) { - uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count); - if((game_state->doubled && score <= 21) || score == 21) { - enqueue( - &(game_state->queue_state), - game_state, - dealerTurn, - NULL, - to_dealer_turn, - game_state->settings.message_duration); - } else if(score > 21) { - enqueue( - &(game_state->queue_state), - game_state, - lose, - NULL, - to_bust_state, - game_state->settings.message_duration); - } else { - if(game_state->selectDirection == DirectionUp || - game_state->selectDirection == DirectionDown) { - move_menu(game_state->menu, game_state->selectDirection == DirectionUp ? -1 : 1); - } - - if(game_state->selectDirection == Select) { - activate_menu(game_state->menu, game_state); - } - } -} - -void dealer_tick(GameState* game_state) { - uint8_t dealer_score = hand_count(game_state->dealer_cards, game_state->dealer_card_count); - uint8_t player_score = hand_count(game_state->player_cards, game_state->player_card_count); - - if(dealer_score >= DEALER_MAX) { - if(dealer_score > 21 || dealer_score < player_score) { - dolphin_deed(DolphinDeedPluginGameWin); - enqueue( - &(game_state->queue_state), - game_state, - win, - NULL, - to_win_state, - game_state->settings.message_duration); - } else if(dealer_score > player_score) { - enqueue( - &(game_state->queue_state), - game_state, - lose, - NULL, - to_lose_state, - game_state->settings.message_duration); - } else if(dealer_score == player_score) { - enqueue( - &(game_state->queue_state), - game_state, - draw, - NULL, - to_draw_state, - game_state->settings.message_duration); - } - } else { - enqueue( - &(game_state->queue_state), - game_state, - drawDealerCard, - NULL, - dealer_card_animation, - game_state->settings.animation_duration); - } -} - -void settings_tick(GameState* game_state) { - if(game_state->selectDirection == DirectionDown && game_state->selectedMenu < 4) { - game_state->selectedMenu++; - } - if(game_state->selectDirection == DirectionUp && game_state->selectedMenu > 0) { - game_state->selectedMenu--; - } - - if(game_state->selectDirection == DirectionLeft || - game_state->selectDirection == DirectionRight) { - int nextScore = 0; - switch(game_state->selectedMenu) { - case 0: - nextScore = game_state->settings.starting_money; - if(game_state->selectDirection == DirectionLeft) - nextScore -= 10; - else - nextScore += 10; - if(nextScore >= (int)game_state->settings.round_price && nextScore < 400) - game_state->settings.starting_money = nextScore; - break; - case 1: - nextScore = game_state->settings.round_price; - if(game_state->selectDirection == DirectionLeft) - nextScore -= 10; - else - nextScore += 10; - if(nextScore >= 5 && nextScore <= (int)game_state->settings.starting_money) - game_state->settings.round_price = nextScore; - break; - case 2: - nextScore = game_state->settings.animation_duration; - if(game_state->selectDirection == DirectionLeft) - nextScore -= 100; - else - nextScore += 100; - if(nextScore >= 0 && nextScore < 2000) - game_state->settings.animation_duration = nextScore; - break; - case 3: - nextScore = game_state->settings.message_duration; - if(game_state->selectDirection == DirectionLeft) - nextScore -= 100; - else - nextScore += 100; - if(nextScore >= 0 && nextScore < 2000) - game_state->settings.message_duration = nextScore; - break; - case 4: - game_state->settings.sound_effects = !game_state->settings.sound_effects; - default: - break; - } - } -} - -void tick(GameState* game_state) { - game_state->last_tick = furi_get_tick(); - bool queue_ran = run_queue(&(game_state->queue_state), game_state); - - switch(game_state->state) { - case GameStateGameOver: - case GameStateStart: - if(game_state->selectDirection == Select) - init(game_state); - else if(game_state->selectDirection == DirectionRight) { - game_state->selectedMenu = 0; - game_state->state = GameStateSettings; - } - break; - case GameStatePlay: - if(!game_state->started) { - game_state->selectedMenu = 0; - game_state->started = true; - enqueue( - &(game_state->queue_state), - game_state, - drawDealerCard, - NULL, - dealer_back_card_animation, - game_state->settings.animation_duration); - enqueue( - &(game_state->queue_state), - game_state, - drawPlayerCard, - NULL, - player_card_animation, - game_state->settings.animation_duration); - enqueue( - &(game_state->queue_state), - game_state, - drawDealerCard, - NULL, - dealer_card_animation, - game_state->settings.animation_duration); - enqueue( - &(game_state->queue_state), - game_state, - drawPlayerCard, - NULL, - player_card_animation, - game_state->settings.animation_duration); - } - if(!queue_ran) player_tick(game_state); - break; - case GameStateDealer: - if(!queue_ran) dealer_tick(game_state); - break; - case GameStateSettings: - settings_tick(game_state); - break; - default: - break; - } - - game_state->selectDirection = None; -} - -void start_round(GameState* game_state) { - game_state->menu->current_menu = 1; - game_state->player_card_count = 0; - game_state->dealer_card_count = 0; - set_menu_state(game_state->menu, 0, true); - game_state->menu->enabled = true; - game_state->started = false; - game_state->doubled = false; - game_state->queue_state.running = true; - shuffle_deck(&(game_state->deck)); - game_state->doubled = false; - game_state->bet = game_state->settings.round_price; - if(game_state->player_score < game_state->settings.round_price) { - game_state->state = GameStateGameOver; - } else { - game_state->player_score -= game_state->settings.round_price; - } - game_state->state = GameStatePlay; -} - -void init(GameState* game_state) { - set_menu_state(game_state->menu, 0, true); - game_state->menu->enabled = true; - game_state->menu->current_menu = 1; - game_state->settings = load_settings(); - game_state->last_tick = 0; - game_state->processing = true; - game_state->selectedMenu = 0; - game_state->player_score = game_state->settings.starting_money; - generate_deck(&(game_state->deck), 6); - start_round(game_state); -} - -static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - AppEvent event = {.type = EventTypeKey, .input = *input_event}; - furi_message_queue_put(event_queue, &event, FuriWaitForever); -} - -static void update_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - AppEvent event = {.type = EventTypeTick}; - furi_message_queue_put(event_queue, &event, 0); -} - -void doubleAction(void* state) { - GameState* game_state = state; - if(!game_state->doubled && game_state->player_score >= game_state->settings.round_price) { - game_state->player_score -= game_state->settings.round_price; - game_state->bet += game_state->settings.round_price; - game_state->doubled = true; - enqueue( - &(game_state->queue_state), - game_state, - drawPlayerCard, - NULL, - player_card_animation, - game_state->settings.animation_duration); - game_state->player_cards[game_state->player_card_count] = - game_state->deck.cards[game_state->deck.index]; - uint8_t score = hand_count(game_state->player_cards, game_state->player_card_count + 1); - if(score > 21) { - enqueue( - &(game_state->queue_state), - game_state, - lose, - NULL, - to_bust_state, - game_state->settings.message_duration); - } else { - enqueue( - &(game_state->queue_state), - game_state, - dealerTurn, - NULL, - to_dealer_turn, - game_state->settings.message_duration); - } - set_menu_state(game_state->menu, 0, false); - } -} - -void hitAction(void* state) { - GameState* game_state = state; - enqueue( - &(game_state->queue_state), - game_state, - drawPlayerCard, - NULL, - player_card_animation, - game_state->settings.animation_duration); -} -void stayAction(void* state) { - GameState* game_state = state; - enqueue( - &(game_state->queue_state), - game_state, - dealerTurn, - NULL, - to_dealer_turn, - game_state->settings.message_duration); -} - -int32_t blackjack_app(void* p) { - UNUSED(p); - - int32_t return_code = 0; - - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(AppEvent)); - - GameState* game_state = malloc(sizeof(GameState)); - game_state->menu = malloc(sizeof(Menu)); - game_state->menu->menu_width = 40; - init(game_state); - add_menu(game_state->menu, "Double", doubleAction); - add_menu(game_state->menu, "Hit", hitAction); - add_menu(game_state->menu, "Stay", stayAction); - set_card_graphics(&I_card_graphics); - - game_state->state = GameStateStart; - - game_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!game_state->mutex) { - FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); - return_code = 255; - goto free_and_exit; - } - - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, game_state); - view_port_input_callback_set(view_port, input_callback, event_queue); - - FuriTimer* timer = furi_timer_alloc(update_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25); - - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - AppEvent event; - - // Call dolphin deed on game start - dolphin_deed(DolphinDeedPluginGameStart); - - for(bool processing = true; processing;) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - furi_mutex_acquire(game_state->mutex, FuriWaitForever); - if(event_status == FuriStatusOk) { - if(event.type == EventTypeKey) { - if(event.input.type == InputTypePress) { - switch(event.input.key) { - case InputKeyUp: - game_state->selectDirection = DirectionUp; - break; - case InputKeyDown: - game_state->selectDirection = DirectionDown; - break; - case InputKeyRight: - game_state->selectDirection = DirectionRight; - break; - case InputKeyLeft: - game_state->selectDirection = DirectionLeft; - break; - case InputKeyBack: - if(game_state->state == GameStateSettings) { - game_state->state = GameStateStart; - save_settings(game_state->settings); - } else - processing = false; - break; - case InputKeyOk: - game_state->selectDirection = Select; - break; - default: - break; - } - } - } else if(event.type == EventTypeTick) { - tick(game_state); - processing = game_state->processing; - } - } - view_port_update(view_port); - furi_mutex_release(game_state->mutex); - } - - furi_timer_free(timer); - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close(RECORD_GUI); - view_port_free(view_port); - furi_mutex_free(game_state->mutex); - -free_and_exit: - free(game_state->deck.cards); - free_menu(game_state->menu); - queue_clear(&(game_state->queue_state)); - free(game_state); - furi_message_queue_free(event_queue); - - return return_code; -} \ No newline at end of file diff --git a/applications/external/blackjack/blackjack_10px.png b/applications/external/blackjack/blackjack_10px.png deleted file mode 100644 index 7382d6358bde65d970575fb40995145d5d3e26d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VGmzZ%#=aj&F%}28J29*~C-V}>VGHmHasB`Q zKad%E=yDy9lJ|6R4B?oWoZ!IP)59UwbM^#Bva!*jn+{A(9T(Wx892Nd)DK8X1_G5b Nc)I$ztaD0e0sxqvA7ual diff --git a/applications/external/blackjack/common/card.c b/applications/external/blackjack/common/card.c deleted file mode 100644 index 88228fda4..000000000 --- a/applications/external/blackjack/common/card.c +++ /dev/null @@ -1,353 +0,0 @@ -#include "card.h" -#include "dml.h" -#include "ui.h" - -#define CARD_DRAW_X_START 108 -#define CARD_DRAW_Y_START 38 -#define CARD_DRAW_X_SPACE 10 -#define CARD_DRAW_Y_SPACE 8 -#define CARD_DRAW_X_OFFSET 4 -#define CARD_DRAW_FIRST_ROW_LENGTH 7 - -uint8_t pips[4][3] = { - {21, 10, 7}, //spades - {7, 10, 7}, //hearts - {0, 10, 7}, //diamonds - {14, 10, 7}, //clubs -}; -uint8_t letters[13][3] = { - {0, 0, 5}, - {5, 0, 5}, - {10, 0, 5}, - {15, 0, 5}, - {20, 0, 5}, - {25, 0, 5}, - {30, 0, 5}, - {0, 5, 5}, - {5, 5, 5}, - {10, 5, 5}, - {15, 5, 5}, - {20, 5, 5}, - {25, 5, 5}, -}; - -//region Player card positions -uint8_t playerCardPositions[22][4] = { - //first row - {108, 38}, - {98, 38}, - {88, 38}, - {78, 38}, - {68, 38}, - {58, 38}, - {48, 38}, - {38, 38}, - //second row - {104, 26}, - {94, 26}, - {84, 26}, - {74, 26}, - {64, 26}, - {54, 26}, - {44, 26}, - //third row - {99, 14}, - {89, 14}, - {79, 14}, - {69, 14}, - {59, 14}, - {49, 14}, -}; -//endregion -Icon* card_graphics = NULL; - -void set_card_graphics(const Icon* graphics) { - card_graphics = (Icon*)graphics; -} - -void draw_card_at_colored( - int8_t pos_x, - int8_t pos_y, - uint8_t pip, - uint8_t character, - bool inverted, - Canvas* const canvas) { - DrawMode primary = inverted ? Black : White; - DrawMode secondary = inverted ? White : Black; - draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, primary); - draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); - - uint8_t* drawInfo = pips[pip]; - uint8_t px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; - - uint8_t left = pos_x + 2; - uint8_t right = (pos_x + CARD_WIDTH - s - 2); - uint8_t top = pos_y + 2; - uint8_t bottom = (pos_y + CARD_HEIGHT - s - 2); - - draw_icon_clip(canvas, card_graphics, right, top, px, py, s, s, secondary); - draw_icon_clip_flipped(canvas, card_graphics, left, bottom, px, py, s, s, secondary); - - drawInfo = letters[character]; - px = drawInfo[0], py = drawInfo[1], s = drawInfo[2]; - left = pos_x + 2; - right = (pos_x + CARD_WIDTH - s - 2); - top = pos_y + 2; - bottom = (pos_y + CARD_HEIGHT - s - 2); - - draw_icon_clip(canvas, card_graphics, left, top + 1, px, py, s, s, secondary); - draw_icon_clip_flipped(canvas, card_graphics, right, bottom - 1, px, py, s, s, secondary); -} - -void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas) { - draw_card_at_colored(pos_x, pos_y, pip, character, false, canvas); -} - -void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas) { - for(int i = count - 1; i >= 0; i--) { - draw_card_at( - playerCardPositions[i][0], - playerCardPositions[i][1], - cards[i].pip, - cards[i].character, - canvas); - } -} - -Vector card_pos_at_index(uint8_t index) { - return (Vector){playerCardPositions[index][0], playerCardPositions[index][1]}; -} - -void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas) { - draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, White); - draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); - - draw_icon_clip(canvas, card_graphics, pos_x + 1, pos_y + 1, 35, 0, 15, 21, Black); -} - -void generate_deck(Deck* deck_ptr, uint8_t deck_count) { - uint16_t counter = 0; - if(deck_ptr->cards != NULL) { - free(deck_ptr->cards); - } - - deck_ptr->deck_count = deck_count; - deck_ptr->card_count = deck_count * 52; - deck_ptr->cards = malloc(sizeof(Card) * deck_ptr->card_count); - - for(uint8_t deck = 0; deck < deck_count; deck++) { - for(uint8_t pip = 0; pip < 4; pip++) { - for(uint8_t label = 0; label < 13; label++) { - deck_ptr->cards[counter] = (Card){pip, label, false, false}; - counter++; - } - } - } -} - -void shuffle_deck(Deck* deck_ptr) { - srand(DWT->CYCCNT); - deck_ptr->index = 0; - int max = deck_ptr->deck_count * 52; - for(int i = 0; i < max; i++) { - int r = i + (rand() % (max - i)); - Card tmp = deck_ptr->cards[i]; - deck_ptr->cards[i] = deck_ptr->cards[r]; - deck_ptr->cards[r] = tmp; - } -} - -uint8_t hand_count(const Card* cards, uint8_t count) { - uint8_t aceCount = 0; - uint8_t score = 0; - - for(uint8_t i = 0; i < count; i++) { - if(cards[i].character == 12) - aceCount++; - else { - if(cards[i].character > 8) - score += 10; - else - score += cards[i].character + 2; - } - } - - for(uint8_t i = 0; i < aceCount; i++) { - if((score + 11 + (aceCount - 1)) <= 21) - score += 11; - else - score++; - } - - return score; -} - -void draw_card_animation( - Card animatingCard, - Vector from, - Vector control, - Vector to, - float t, - bool extra_margin, - Canvas* const canvas) { - float time = t; - if(extra_margin) { - time += 0.2; - } - - Vector currentPos = quadratic_2d(from, control, to, time); - if(t > 1) { - draw_card_at( - currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas); - } else { - if(t < 0.5) - draw_card_back_at(currentPos.x, currentPos.y, canvas); - else - draw_card_at( - currentPos.x, currentPos.y, animatingCard.pip, animatingCard.character, canvas); - } -} - -void init_hand(Hand* hand_ptr, uint8_t count) { - hand_ptr->cards = malloc(sizeof(Card) * count); - hand_ptr->index = 0; - hand_ptr->max = count; -} - -void free_hand(Hand* hand_ptr) { - FURI_LOG_D("CARD", "Freeing hand"); - free(hand_ptr->cards); -} - -void add_to_hand(Hand* hand_ptr, Card card) { - FURI_LOG_D("CARD", "Adding to hand"); - if(hand_ptr->index < hand_ptr->max) { - hand_ptr->cards[hand_ptr->index] = card; - hand_ptr->index++; - } -} - -void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas) { - if(highlighted) { - draw_rounded_box_frame(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); - draw_rounded_box_frame( - canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White); - } else { - draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Black); - draw_rounded_box_frame( - canvas, pos_x + 2, pos_y + 2, CARD_WIDTH - 4, CARD_HEIGHT - 4, White); - } -} - -int first_non_flipped_card(Hand hand) { - for(int i = 0; i < hand.index; i++) { - if(!hand.cards[i].flipped) { - return i; - } - } - return hand.index; -} - -void draw_hand_column( - Hand hand, - int16_t pos_x, - int16_t pos_y, - int8_t highlight, - Canvas* const canvas) { - if(hand.index == 0) { - draw_card_space(pos_x, pos_y, highlight > 0, canvas); - if(highlight == 0) - draw_rounded_box(canvas, pos_x, pos_y, CARD_WIDTH, CARD_HEIGHT, Inverse); - return; - } - - int loopEnd = hand.index; - int hStart = max(loopEnd - 4, 0); - int pos = 0; - int first = first_non_flipped_card(hand); - bool wastop = false; - if(first >= 0 && first <= hStart && highlight != first) { - if(first > 0) { - draw_card_back_at(pos_x, pos_y + pos, canvas); - pos += 4; - hStart++; - wastop = true; - } - draw_card_at_colored( - pos_x, pos_y + pos, hand.cards[first].pip, hand.cards[first].character, false, canvas); - pos += 8; - hStart++; - } - if(hStart > highlight && highlight >= 0) { - if(!wastop && first > 0) { - draw_card_back_at(pos_x, pos_y + pos, canvas); - pos += 4; - hStart++; - } - draw_card_at_colored( - pos_x, - pos_y + pos, - hand.cards[highlight].pip, - hand.cards[highlight].character, - true, - canvas); - pos += 8; - hStart++; - } - for(int i = hStart; i < loopEnd; i++, pos += 4) { - if(hand.cards[i].flipped) { - draw_card_back_at(pos_x, pos_y + pos, canvas); - if(i == highlight) - draw_rounded_box( - canvas, pos_x + 1, pos_y + pos + 1, CARD_WIDTH - 2, CARD_HEIGHT - 2, Inverse); - } else { - draw_card_at_colored( - pos_x, - pos_y + pos, - hand.cards[i].pip, - hand.cards[i].character, - (i == highlight), - canvas); - if(i == highlight || i == first) pos += 4; - } - } -} - -Card remove_from_deck(uint16_t index, Deck* deck) { - FURI_LOG_D("CARD", "Removing from deck"); - Card result = {0, 0, true, false}; - if(deck->card_count > 0) { - deck->card_count--; - for(int i = 0, curr_index = 0; i <= deck->card_count; i++) { - if(i != index) { - deck->cards[curr_index] = deck->cards[i]; - curr_index++; - } else { - result = deck->cards[i]; - } - } - if(deck->index >= 0) { - deck->index--; - } - } - return result; -} - -void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index) { - FURI_LOG_D("CARD", "Extracting hand region"); - if(start_index >= hand->index) return; - - for(uint8_t i = start_index; i < hand->index; i++) { - add_to_hand(to, hand->cards[i]); - } - hand->index = start_index; -} - -void add_hand_region(Hand* to, Hand* from) { - FURI_LOG_D("CARD", "Adding hand region"); - if((to->index + from->index) <= to->max) { - for(int i = 0; i < from->index; i++) { - add_to_hand(to, from->cards[i]); - } - } -} diff --git a/applications/external/blackjack/common/card.h b/applications/external/blackjack/common/card.h deleted file mode 100644 index 8e5e23bbf..000000000 --- a/applications/external/blackjack/common/card.h +++ /dev/null @@ -1,192 +0,0 @@ -#pragma once - -#include -#include -#include -#include "dml.h" - -#define CARD_HEIGHT 23 -#define CARD_HALF_HEIGHT 11 -#define CARD_WIDTH 17 -#define CARD_HALF_WIDTH 8 - -//region types -typedef struct { - uint8_t pip; //Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs - uint8_t character; //Card letter [0-12], 0 means 2, 12 is Ace - bool disabled; - bool flipped; -} Card; - -typedef struct { - uint8_t deck_count; //Number of decks used - Card* cards; //Cards in the deck - int card_count; - int index; //Card index (to know where we at in the deck) -} Deck; - -typedef struct { - Card* cards; //Cards in the deck - uint8_t index; //Current index - uint8_t max; //How many cards we want to store -} Hand; -//endregion - -void set_card_graphics(const Icon* graphics); - -/** - * Gets card coordinates at the index (range: 0-20). - * - * @param index Index to check 0-20 - * @return Position of the card - */ -Vector card_pos_at_index(uint8_t index); - -/** - * Draws card at a given coordinate (top-left corner) - * - * @param pos_x X position - * @param pos_y Y position - * @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs - * @param character Letter [0-12] 0 is 2, 12 is A - * @param canvas Pointer to Flipper's canvas object - */ -void draw_card_at(int8_t pos_x, int8_t pos_y, uint8_t pip, uint8_t character, Canvas* const canvas); - -/** - * Draws card at a given coordinate (top-left corner) - * - * @param pos_x X position - * @param pos_y Y position - * @param pip Pip index 0:spades, 1:hearths, 2:diamonds, 3:clubs - * @param character Letter [0-12] 0 is 2, 12 is A - * @param inverted Invert colors - * @param canvas Pointer to Flipper's canvas object - */ -void draw_card_at_colored( - int8_t pos_x, - int8_t pos_y, - uint8_t pip, - uint8_t character, - bool inverted, - Canvas* const canvas); - -/** - * Draws 'count' cards at the bottom right corner - * - * @param cards List of cards - * @param count Count of cards - * @param canvas Pointer to Flipper's canvas object - */ -void draw_deck(const Card* cards, uint8_t count, Canvas* const canvas); - -/** - * Draws card back at a given coordinate (top-left corner) - * - * @param pos_x X coordinate - * @param pos_y Y coordinate - * @param canvas Pointer to Flipper's canvas object - */ -void draw_card_back_at(int8_t pos_x, int8_t pos_y, Canvas* const canvas); - -/** - * Generates the deck - * - * @param deck_ptr Pointer to the deck - * @param deck_count Number of decks - */ -void generate_deck(Deck* deck_ptr, uint8_t deck_count); - -/** - * Shuffles the deck - * - * @param deck_ptr Pointer to the deck - */ -void shuffle_deck(Deck* deck_ptr); - -/** - * Calculates the hand count for blackjack - * - * @param cards List of cards - * @param count Count of cards - * @return Hand value - */ -uint8_t hand_count(const Card* cards, uint8_t count); - -/** - * Draws card animation - * - * @param animatingCard Card to animate - * @param from Starting position - * @param control Quadratic lerp control point - * @param to End point - * @param t Current time (0-1) - * @param extra_margin Use extra margin at the end (arrives 0.2 unit before the end so it can stay there a bit) - * @param canvas Pointer to Flipper's canvas object - */ -void draw_card_animation( - Card animatingCard, - Vector from, - Vector control, - Vector to, - float t, - bool extra_margin, - Canvas* const canvas); - -/** - * Init hand pointer - * @param hand_ptr Pointer to hand - * @param count Number of cards we want to store - */ -void init_hand(Hand* hand_ptr, uint8_t count); - -/** - * Free hand resources - * @param hand_ptr Pointer to hand - */ -void free_hand(Hand* hand_ptr); - -/** - * Add card to hand - * @param hand_ptr Pointer to hand - * @param card Card to add - */ -void add_to_hand(Hand* hand_ptr, Card card); - -/** - * Draw card placement position at coordinate - * @param pos_x X coordinate - * @param pos_y Y coordinate - * @param highlighted Apply highlight effect - * @param canvas Canvas object - */ -void draw_card_space(int16_t pos_x, int16_t pos_y, bool highlighted, Canvas* const canvas); - -/** - * Draws a column of card, displaying the last [max_cards] cards on the list - * @param hand Hand object - * @param pos_x X coordinate to draw - * @param pos_y Y coordinate to draw - * @param highlight Index to highlight, negative means no highlight - * @param canvas Canvas object - */ -void draw_hand_column( - Hand hand, - int16_t pos_x, - int16_t pos_y, - int8_t highlight, - Canvas* const canvas); - -/** - * Removes a card from the deck (Be aware, if you remove the first item, the deck index will be at -1 so you have to handle that) - * @param index Index to remove - * @param deck Deck reference - * @return The removed card - */ -Card remove_from_deck(uint16_t index, Deck* deck); - -int first_non_flipped_card(Hand hand); - -void extract_hand_region(Hand* hand, Hand* to, uint8_t start_index); - -void add_hand_region(Hand* to, Hand* from); \ No newline at end of file diff --git a/applications/external/blackjack/common/dml.c b/applications/external/blackjack/common/dml.c deleted file mode 100644 index b9a0e395f..000000000 --- a/applications/external/blackjack/common/dml.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "dml.h" -#include - -float lerp(float v0, float v1, float t) { - if(t > 1) return v1; - return (1 - t) * v0 + t * v1; -} - -Vector lerp_2d(Vector start, Vector end, float t) { - return (Vector){ - lerp(start.x, end.x, t), - lerp(start.y, end.y, t), - }; -} - -Vector quadratic_2d(Vector start, Vector control, Vector end, float t) { - return lerp_2d(lerp_2d(start, control, t), lerp_2d(control, end, t), t); -} - -Vector vector_add(Vector a, Vector b) { - return (Vector){a.x + b.x, a.y + b.y}; -} - -Vector vector_sub(Vector a, Vector b) { - return (Vector){a.x - b.x, a.y - b.y}; -} - -Vector vector_mul_components(Vector a, Vector b) { - return (Vector){a.x * b.x, a.y * b.y}; -} - -Vector vector_div_components(Vector a, Vector b) { - return (Vector){a.x / b.x, a.y / b.y}; -} - -Vector vector_normalized(Vector a) { - float length = vector_magnitude(a); - return (Vector){a.x / length, a.y / length}; -} - -float vector_magnitude(Vector a) { - return sqrt(a.x * a.x + a.y * a.y); -} - -float vector_distance(Vector a, Vector b) { - return vector_magnitude(vector_sub(a, b)); -} - -float vector_dot(Vector a, Vector b) { - Vector _a = vector_normalized(a); - Vector _b = vector_normalized(b); - return _a.x * _b.x + _a.y * _b.y; -} \ No newline at end of file diff --git a/applications/external/blackjack/common/dml.h b/applications/external/blackjack/common/dml.h deleted file mode 100644 index 0e1a23e23..000000000 --- a/applications/external/blackjack/common/dml.h +++ /dev/null @@ -1,116 +0,0 @@ -// -// Doofy's Math library -// - -#pragma once - -typedef struct { - float x; - float y; -} Vector; - -#define min(a, b) ((a) < (b) ? (a) : (b)) -#define max(a, b) ((a) > (b) ? (a) : (b)) -#define abs(x) ((x) > 0 ? (x) : -(x)) - -/** - * Lerp function - * - * @param v0 Start value - * @param v1 End value - * @param t Time (0-1 range) - * @return Point between v0-v1 at a given time - */ -float lerp(float v0, float v1, float t); - -/** - * 2D lerp function - * - * @param start Start vector - * @param end End vector - * @param t Time (0-1 range) - * @return 2d Vector between start and end at time - */ -Vector lerp_2d(Vector start, Vector end, float t); - -/** - * Quadratic lerp function - * - * @param start Start vector - * @param control Control point - * @param end End vector - * @param t Time (0-1 range) - * @return 2d Vector at time - */ -Vector quadratic_2d(Vector start, Vector control, Vector end, float t); - -/** - * Add vector components together - * - * @param a First vector - * @param b Second vector - * @return Resulting vector - */ -Vector vector_add(Vector a, Vector b); - -/** - * Subtract vector components together - * - * @param a First vector - * @param b Second vector - * @return Resulting vector - */ -Vector vector_sub(Vector a, Vector b); - -/** - * Multiplying vector components together - * - * @param a First vector - * @param b Second vector - * @return Resulting vector - */ -Vector vector_mul_components(Vector a, Vector b); - -/** - * Dividing vector components - * - * @param a First vector - * @param b Second vector - * @return Resulting vector - */ -Vector vector_div_components(Vector a, Vector b); - -/** - * Calculating Vector length - * - * @param a Direction vector - * @return Length of the vector - */ -float vector_magnitude(Vector a); - -/** - * Get a normalized vector (length of 1) - * - * @param a Direction vector - * @return Normalized vector - */ -Vector vector_normalized(Vector a); - -/** - * Calculate two vector's distance - * - * @param a First vector - * @param b Second vector - * @return Distance between vectors - */ -float vector_distance(Vector a, Vector b); - -/** - * Calculate the dot product of the vectors. - * No need to normalize, it will do it - * - * @param a First vector - * @param b Second vector - * @return value from -1 to 1 - */ -float vector_dot(Vector a, Vector b); diff --git a/applications/external/blackjack/common/menu.c b/applications/external/blackjack/common/menu.c deleted file mode 100644 index ffc3921b7..000000000 --- a/applications/external/blackjack/common/menu.c +++ /dev/null @@ -1,103 +0,0 @@ -#include "menu.h" - -void add_menu(Menu* menu, const char* name, void (*callback)(void*)) { - MenuItem* items = menu->items; - - menu->items = malloc(sizeof(MenuItem) * (menu->menu_count + 1)); - for(uint8_t i = 0; i < menu->menu_count; i++) { - menu->items[i] = items[i]; - } - free(items); - - menu->items[menu->menu_count] = (MenuItem){name, true, callback}; - menu->menu_count++; -} - -void free_menu(Menu* menu) { - free(menu->items); - free(menu); -} - -void set_menu_state(Menu* menu, uint8_t index, bool state) { - if(menu->menu_count > index) { - menu->items[index].enabled = state; - } - if(!state && menu->current_menu == index) move_menu(menu, 1); -} - -void move_menu(Menu* menu, int8_t direction) { - if(!menu->enabled) return; - int max = menu->menu_count; - for(int8_t i = 0; i < max; i++) { - FURI_LOG_D( - "MENU", - "Iteration %i, current %i, direction %i, state %i", - i, - menu->current_menu, - direction, - menu->items[menu->current_menu].enabled ? 1 : 0); - if(direction < 0 && menu->current_menu == 0) { - menu->current_menu = menu->menu_count - 1; - } else { - menu->current_menu = (menu->current_menu + direction) % menu->menu_count; - } - FURI_LOG_D( - "MENU", - "After process current %i, direction %i, state %i", - menu->current_menu, - direction, - menu->items[menu->current_menu].enabled ? 1 : 0); - if(menu->items[menu->current_menu].enabled) { - FURI_LOG_D("MENU", "Next menu %i", menu->current_menu); - return; - } - } - FURI_LOG_D("MENU", "Not found, setting false"); - menu->enabled = false; -} - -void activate_menu(Menu* menu, void* state) { - if(!menu->enabled) return; - menu->items[menu->current_menu].callback(state); -} - -void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y) { - if(!menu->enabled) return; - canvas_set_color(canvas, ColorWhite); - canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2); - - uint8_t w = pos_x + menu->menu_width; - uint8_t h = pos_y + 10; - uint8_t p1x = pos_x + 2; - uint8_t p2x = pos_x + menu->menu_width - 2; - uint8_t p1y = pos_y + 2; - uint8_t p2y = pos_y + 8; - - canvas_set_color(canvas, ColorBlack); - canvas_draw_line(canvas, p1x, pos_y, p2x, pos_y); - canvas_draw_line(canvas, p1x, h, p2x, h); - canvas_draw_line(canvas, pos_x, p1y, pos_x, p2y); - canvas_draw_line(canvas, w, p1y, w, p2y); - canvas_draw_dot(canvas, pos_x + 1, pos_y + 1); - canvas_draw_dot(canvas, w - 1, pos_y + 1); - canvas_draw_dot(canvas, w - 1, h - 1); - canvas_draw_dot(canvas, pos_x + 1, h - 1); - - // canvas_draw_rbox(canvas, pos_x, pos_y, menu->menu_width + 2, 10, 2); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, - pos_x + menu->menu_width / 2, - pos_y + 6, - AlignCenter, - AlignCenter, - menu->items[menu->current_menu].name); - //9*5 - int center = pos_x + menu->menu_width / 2; - for(uint8_t i = 0; i < 4; i++) { - for(int8_t j = -i; j <= i; j++) { - canvas_draw_dot(canvas, center + j, pos_y - 4 + i); - canvas_draw_dot(canvas, center + j, pos_y + 14 - i); - } - } -} \ No newline at end of file diff --git a/applications/external/blackjack/common/menu.h b/applications/external/blackjack/common/menu.h deleted file mode 100644 index 9f2852522..000000000 --- a/applications/external/blackjack/common/menu.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - const char* name; //Name of the menu - bool enabled; //Is the menu item enabled (it will not render, you cannot select it) - - void (*callback)( - void* state); //Callback for when the activate_menu is called while this menu is selected -} MenuItem; - -typedef struct { - MenuItem* items; //list of menu items - uint8_t menu_count; //count of menu items (do not change) - uint8_t current_menu; //currently selected menu item - uint8_t menu_width; //width of the menu - bool enabled; //is the menu enabled (it will not render and accept events when disabled) -} Menu; - -/** - * Cleans up the pointers used by the menu - * - * @param menu Pointer of the menu to clean up - */ -void free_menu(Menu* menu); - -/** - * Add a new menu item - * - * @param menu Pointer of the menu - * @param name Name of the menu item - * @param callback Callback called on activation - */ -void add_menu(Menu* menu, const char* name, void (*callback)(void*)); - -/** - * Setting menu item to be enabled/disabled - * - * @param menu Pointer of the menu - * @param index Menu index to set - * @param state Enabled (true), Disabled(false) - */ -void set_menu_state(Menu* menu, uint8_t index, bool state); - -/** - * Moves selection up or down - * - * @param menu Pointer of the menu - * @param direction Direction to move -1 down, 1 up - */ -void move_menu(Menu* menu, int8_t direction); - -/** - * Triggers the current menu callback - * - * @param menu Pointer of the menu - * @param state Usually your application state - */ -void activate_menu(Menu* menu, void* state); - -/** - * Renders the menu at a coordinate (call it in your render function). - * - * Keep in mind that Flipper has a 128x64 pixel screen resolution and the coordinate - * you give is the menu's rectangle top-left corner (arrows not included). - * The rectangle height is 10 px, the arrows have a 4 pixel height. Space needed is 18px. - * The width of the menu can be configured in the menu object. - * - * - * @param menu Pointer of the menu - * @param canvas Flippers Canvas pointer - * @param pos_x X position to draw - * @param pos_y Y position to draw - */ -void render_menu(Menu* menu, Canvas* canvas, uint8_t pos_x, uint8_t pos_y); \ No newline at end of file diff --git a/applications/external/blackjack/common/queue.c b/applications/external/blackjack/common/queue.c deleted file mode 100644 index a80373460..000000000 --- a/applications/external/blackjack/common/queue.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "queue.h" - -void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas) { - if(queue_state->current != NULL && queue_state->current->render != NULL) - ((QueueItem*)queue_state->current)->render(app_state, canvas); -} - -bool run_queue(QueueState* queue_state, void* app_state) { - if(queue_state->current != NULL) { - queue_state->running = true; - if((furi_get_tick() - queue_state->start) >= queue_state->current->duration) - dequeue(queue_state, app_state); - - return true; - } - return false; -} - -void dequeue(QueueState* queue_state, void* app_state) { - ((QueueItem*)queue_state->current)->callback(app_state); - QueueItem* f = queue_state->current; - queue_state->current = f->next; - free(f); - if(queue_state->current != NULL) { - if(queue_state->current->start != NULL) queue_state->current->start(app_state); - queue_state->start = furi_get_tick(); - } else { - queue_state->running = false; - } -} - -void queue_clear(QueueState* queue_state) { - queue_state->running = false; - QueueItem* curr = queue_state->current; - while(curr != NULL) { - QueueItem* f = curr; - curr = curr->next; - free(f); - } -} - -void enqueue( - QueueState* queue_state, - void* app_state, - void (*done)(void* state), - void (*start)(void* state), - void (*render)(const void* state, Canvas* const canvas), - uint32_t duration) { - QueueItem* next; - if(queue_state->current == NULL) { - queue_state->start = furi_get_tick(); - queue_state->current = malloc(sizeof(QueueItem)); - next = queue_state->current; - if(next->start != NULL) next->start(app_state); - - } else { - next = queue_state->current; - while(next->next != NULL) { - next = (QueueItem*)(next->next); - } - next->next = malloc(sizeof(QueueItem)); - next = next->next; - } - next->callback = done; - next->render = render; - next->start = start; - next->duration = duration; - next->next = NULL; -} \ No newline at end of file diff --git a/applications/external/blackjack/common/queue.h b/applications/external/blackjack/common/queue.h deleted file mode 100644 index dcfe0c091..000000000 --- a/applications/external/blackjack/common/queue.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include -#include - -typedef struct { - void (*callback)(void* state); //Callback for when the item is dequeued - void (*render)( - const void* state, - Canvas* const canvas); //Callback for the rendering loop while this item is running - void (*start)(void* state); //Callback when this item is started running - void* next; //Pointer to the next item - uint32_t duration; //duration of the item -} QueueItem; - -typedef struct { - unsigned int start; //current queue item start time - QueueItem* current; //current queue item - bool running; //is the queue running -} QueueState; - -/** - * Enqueue a new item. - * - * @param queue_state The queue state pointer - * @param app_state Your app state - * @param done Callback for dequeue event - * @param start Callback for when the item is activated - * @param render Callback to render loop if needed - * @param duration Length of the item - */ -void enqueue( - QueueState* queue_state, - void* app_state, - void (*done)(void* state), - void (*start)(void* state), - void (*render)(const void* state, Canvas* const canvas), - uint32_t duration); -/** - * Clears all queue items - * - * @param queue_state The queue state pointer - */ -void queue_clear(QueueState* queue_state); - -/** - * Dequeues the active queue item. Usually you don't need to call it directly. - * - * @param queue_state The queue state pointer - * @param app_state Your application state - */ -void dequeue(QueueState* queue_state, void* app_state); - -/** - * Runs the queue logic (place it in your tick function) - * - * @param queue_state The queue state pointer - * @param app_state Your application state - * @return FALSE when there is nothing to run, TRUE otherwise - */ -bool run_queue(QueueState* queue_state, void* app_state); - -/** - * Calls the currently active queue items render callback (if there is any) - * - * @param queue_state The queue state pointer - * @param app_state Your application state - * @param canvas Pointer to Flipper's canvas object - */ -void render_queue(const QueueState* queue_state, const void* app_state, Canvas* const canvas); \ No newline at end of file diff --git a/applications/external/blackjack/common/ui.c b/applications/external/blackjack/common/ui.c deleted file mode 100644 index 032877a9e..000000000 --- a/applications/external/blackjack/common/ui.c +++ /dev/null @@ -1,257 +0,0 @@ -#include "ui.h" -#include -#include -#include -#include -#include -#include - -TileMap* tileMap; -uint8_t tileMapCount = 0; - -void ui_cleanup() { - if(tileMap != NULL) { - for(uint8_t i = 0; i < tileMapCount; i++) { - if(tileMap[i].data != NULL) free(tileMap[i].data); - } - free(tileMap); - } -} - -void add_new_tilemap(uint8_t* data, unsigned long iconId) { - TileMap* old = tileMap; - tileMapCount++; - tileMap = malloc(sizeof(TileMap) * tileMapCount); - if(tileMapCount > 1) { - for(uint8_t i = 0; i < tileMapCount; i++) tileMap[i] = old[i]; - } - tileMap[tileMapCount - 1] = (TileMap){data, iconId}; -} - -uint8_t* get_tilemap(unsigned long icon_id) { - for(uint8_t i = 0; i < tileMapCount; i++) { - if(tileMap[i].iconId == icon_id) return tileMap[i].data; - } - - return NULL; -} - -uint32_t pixel_index(uint8_t x, uint8_t y) { - return y * SCREEN_WIDTH + x; -} - -bool in_screen(int16_t x, int16_t y) { - return x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT; -} - -unsigned flipBit(uint8_t x, uint8_t bit) { - return x ^ (1 << bit); -} - -unsigned setBit(uint8_t x, uint8_t bit) { - return x | (1 << bit); -} - -unsigned unsetBit(uint8_t x, uint8_t bit) { - return x & ~(1 << bit); -} - -bool test_pixel(uint8_t* data, uint8_t x, uint8_t y, uint8_t w) { - uint8_t current_bit = (y % 8); - uint8_t current_row = ((y - current_bit) / 8); - uint8_t current_value = data[current_row * w + x]; - return current_value & (1 << current_bit); -} - -uint8_t* get_buffer(Canvas* const canvas) { - return canvas->fb.tile_buf_ptr; - // return canvas_get_buffer(canvas); -} -uint8_t* make_buffer() { - return malloc(sizeof(uint8_t) * 8 * 128); -} -void clone_buffer(uint8_t* canvas, uint8_t* data) { - for(int i = 0; i < 1024; i++) { - data[i] = canvas[i]; - } -} - -bool read_pixel(Canvas* const canvas, int16_t x, int16_t y) { - if(in_screen(x, y)) { - return test_pixel(get_buffer(canvas), x, y, SCREEN_WIDTH); - } - return false; -} - -void set_pixel(Canvas* const canvas, int16_t x, int16_t y, DrawMode draw_mode) { - if(in_screen(x, y)) { - uint8_t current_bit = (y % 8); - uint8_t current_row = ((y - current_bit) / 8); - uint32_t i = pixel_index(x, current_row); - uint8_t* buffer = get_buffer(canvas); - - uint8_t current_value = buffer[i]; - if(draw_mode == Inverse) { - buffer[i] = flipBit(current_value, current_bit); - } else { - if(draw_mode == White) { - buffer[i] = unsetBit(current_value, current_bit); - } else { - buffer[i] = setBit(current_value, current_bit); - } - } - } -} - -void draw_line( - Canvas* const canvas, - int16_t x1, - int16_t y1, - int16_t x2, - int16_t y2, - DrawMode draw_mode) { - for(int16_t x = x2; x >= x1; x--) { - for(int16_t y = y2; y >= y1; y--) { - set_pixel(canvas, x, y, draw_mode); - } - } -} - -void draw_rounded_box_frame( - Canvas* const canvas, - int16_t x, - int16_t y, - uint8_t w, - uint8_t h, - DrawMode draw_mode) { - int16_t xMinCorner = x + 1; - int16_t xMax = x + w - 1; - int16_t xMaxCorner = x + w - 2; - int16_t yMinCorner = y + 1; - int16_t yMax = y + h - 1; - int16_t yMaxCorner = y + h - 2; - draw_line(canvas, xMinCorner, y, xMaxCorner, y, draw_mode); - draw_line(canvas, xMinCorner, yMax, xMaxCorner, yMax, draw_mode); - draw_line(canvas, x, yMinCorner, x, yMaxCorner, draw_mode); - draw_line(canvas, xMax, yMinCorner, xMax, yMaxCorner, draw_mode); -} - -void draw_rounded_box( - Canvas* const canvas, - int16_t x, - int16_t y, - uint8_t w, - uint8_t h, - DrawMode draw_mode) { - for(int16_t o = w - 2; o >= 1; o--) { - for(int16_t p = h - 2; p >= 1; p--) { - set_pixel(canvas, x + o, y + p, draw_mode); - } - } - draw_rounded_box_frame(canvas, x, y, w, h, draw_mode); -} - -void invert_shape(Canvas* const canvas, uint8_t* data, int16_t x, int16_t y, uint8_t w, uint8_t h) { - draw_pixels(canvas, data, x, y, w, h, Inverse); -} - -void draw_pixels( - Canvas* const canvas, - uint8_t* data, - int16_t x, - int16_t y, - uint8_t w, - uint8_t h, - DrawMode drawMode) { - for(int8_t o = 0; o < w; o++) { - for(int8_t p = 0; p < h; p++) { - if(in_screen(o + x, p + y) && data[p * w + o] == 1) - set_pixel(canvas, o + x, p + y, drawMode); - } - } -} - -void draw_rectangle( - Canvas* const canvas, - int16_t x, - int16_t y, - uint8_t w, - uint8_t h, - DrawMode drawMode) { - for(int8_t o = 0; o < w; o++) { - for(int8_t p = 0; p < h; p++) { - if(in_screen(o + x, p + y)) { - set_pixel(canvas, o + x, p + y, drawMode); - } - } - } -} - -void invert_rectangle(Canvas* const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h) { - draw_rectangle(canvas, x, y, w, h, Inverse); -} - -uint8_t* image_data(Canvas* const canvas, const Icon* icon) { - uint8_t* data = malloc(sizeof(uint8_t) * 8 * 128); - uint8_t* screen = canvas->fb.tile_buf_ptr; - canvas->fb.tile_buf_ptr = data; - canvas_draw_icon(canvas, 0, 0, icon); - canvas->fb.tile_buf_ptr = screen; - return data; -} - -uint8_t* getOrAddIconData(Canvas* const canvas, const Icon* icon) { - uint8_t* icon_data = get_tilemap((unsigned long)icon); - if(icon_data == NULL) { - icon_data = image_data(canvas, icon); - add_new_tilemap(icon_data, (unsigned long)icon); - } - return icon_data; -} - -void draw_icon_clip( - Canvas* const canvas, - const Icon* icon, - int16_t x, - int16_t y, - uint8_t left, - uint8_t top, - uint8_t w, - uint8_t h, - DrawMode drawMode) { - uint8_t* icon_data = getOrAddIconData(canvas, icon); - - for(int i = 0; i < w; i++) { - for(int j = 0; j < h; j++) { - bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH); - if(drawMode == Filled) { - set_pixel(canvas, x + i, y + j, on ? Black : White); - } else if(on) - set_pixel(canvas, x + i, y + j, drawMode); - } - } -} - -void draw_icon_clip_flipped( - Canvas* const canvas, - const Icon* icon, - int16_t x, - int16_t y, - uint8_t left, - uint8_t top, - uint8_t w, - uint8_t h, - DrawMode drawMode) { - uint8_t* icon_data = getOrAddIconData(canvas, icon); - - for(int i = 0; i < w; i++) { - for(int j = 0; j < h; j++) { - bool on = test_pixel(icon_data, left + i, top + j, SCREEN_WIDTH); - - if(drawMode == Filled) { - set_pixel(canvas, x + w - i - 1, y + h - j - 1, on ? Black : White); - } else if(on) - set_pixel(canvas, x + w - i - 1, y + h - j - 1, drawMode); - } - } -} \ No newline at end of file diff --git a/applications/external/blackjack/common/ui.h b/applications/external/blackjack/common/ui.h deleted file mode 100644 index 13d8da257..000000000 --- a/applications/external/blackjack/common/ui.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include -#include - -#define SCREEN_WIDTH 128 -#define SCREEN_HEIGHT 64 - -typedef enum { - Black, - White, - Inverse, - Filled //Currently only for Icon clip drawing -} DrawMode; - -// size is the screen size - -typedef struct { - uint8_t* data; - unsigned long iconId; -} TileMap; - -bool test_pixel(uint8_t* data, uint8_t x, uint8_t y, uint8_t w); - -uint8_t* image_data(Canvas* const canvas, const Icon* icon); - -uint32_t pixel_index(uint8_t x, uint8_t y); - -void draw_icon_clip( - Canvas* const canvas, - const Icon* icon, - int16_t x, - int16_t y, - uint8_t left, - uint8_t top, - uint8_t w, - uint8_t h, - DrawMode drawMode); - -void draw_icon_clip_flipped( - Canvas* const canvas, - const Icon* icon, - int16_t x, - int16_t y, - uint8_t left, - uint8_t top, - uint8_t w, - uint8_t h, - DrawMode drawMode); - -void draw_rounded_box( - Canvas* const canvas, - int16_t x, - int16_t y, - uint8_t w, - uint8_t h, - DrawMode drawMode); - -void draw_rounded_box_frame( - Canvas* const canvas, - int16_t x, - int16_t y, - uint8_t w, - uint8_t h, - DrawMode drawMode); - -void draw_rectangle( - Canvas* const canvas, - int16_t x, - int16_t y, - uint8_t w, - uint8_t h, - DrawMode drawMode); - -void invert_rectangle(Canvas* const canvas, int16_t x, int16_t y, uint8_t w, uint8_t h); - -void invert_shape(Canvas* const canvas, uint8_t* data, int16_t x, int16_t y, uint8_t w, uint8_t h); - -void draw_pixels( - Canvas* const canvas, - uint8_t* data, - int16_t x, - int16_t y, - uint8_t w, - uint8_t h, - DrawMode drawMode); - -bool read_pixel(Canvas* const canvas, int16_t x, int16_t y); - -void set_pixel(Canvas* const canvas, int16_t x, int16_t y, DrawMode draw_mode); - -void draw_line( - Canvas* const canvas, - int16_t x1, - int16_t y1, - int16_t x2, - int16_t y2, - DrawMode draw_mode); - -bool in_screen(int16_t x, int16_t y); - -void ui_cleanup(); -uint8_t* get_buffer(Canvas* const canvas); -uint8_t* make_buffer(); -void clone_buffer(uint8_t* canvas, uint8_t* data); \ No newline at end of file diff --git a/applications/external/blackjack/defines.h b/applications/external/blackjack/defines.h deleted file mode 100644 index 0a3fdf53e..000000000 --- a/applications/external/blackjack/defines.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include "common/card.h" -#include "common/queue.h" -#include "common/menu.h" - -#define APP_NAME "Blackjack" - -#define CONF_ANIMATION_DURATION "AnimationDuration" -#define CONF_MESSAGE_DURATION "MessageDuration" -#define CONF_STARTING_MONEY "StartingMoney" -#define CONF_ROUND_PRICE "RoundPrice" -#define CONF_SOUND_EFFECTS "SoundEffects" - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - uint32_t animation_duration; - uint32_t message_duration; - uint32_t starting_money; - uint32_t round_price; - bool sound_effects; -} Settings; - -typedef struct { - EventType type; - InputEvent input; -} AppEvent; - -typedef enum { - GameStateGameOver, - GameStateStart, - GameStatePlay, - GameStateSettings, - GameStateDealer, -} PlayState; - -typedef enum { - DirectionUp, - DirectionDown, - DirectionRight, - DirectionLeft, - Select, - Back, - None -} Direction; - -typedef struct { - FuriMutex* mutex; - Card player_cards[21]; - Card dealer_cards[21]; - uint8_t player_card_count; - uint8_t dealer_card_count; - - Direction selectDirection; - Settings settings; - - uint32_t player_score; - uint32_t bet; - uint8_t selectedMenu; - bool doubled; - bool started; - bool processing; - Deck deck; - PlayState state; - QueueState queue_state; - Menu* menu; - unsigned int last_tick; -} GameState; diff --git a/applications/external/blackjack/ui.c b/applications/external/blackjack/ui.c deleted file mode 100644 index d4ee82191..000000000 --- a/applications/external/blackjack/ui.c +++ /dev/null @@ -1,186 +0,0 @@ -#include -#include - -#include "ui.h" - -#define LINE_HEIGHT 16 -#define ITEM_PADDING 4 - -const char MoneyMul[4] = {'K', 'B', 'T', 'S'}; - -void draw_player_scene(Canvas* const canvas, const GameState* game_state) { - int max_card = game_state->player_card_count; - - if(max_card > 0) draw_deck((game_state->player_cards), max_card, canvas); - - if(game_state->dealer_card_count > 0) draw_card_back_at(13, 5, canvas); - - max_card = game_state->dealer_card_count; - if(max_card > 1) { - draw_card_at( - 2, 2, game_state->dealer_cards[1].pip, game_state->dealer_cards[1].character, canvas); - } -} - -void draw_dealer_scene(Canvas* const canvas, const GameState* game_state) { - uint8_t max_card = game_state->dealer_card_count; - draw_deck((game_state->dealer_cards), max_card, canvas); -} - -void popup_frame(Canvas* const canvas) { - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 32, 15, 66, 13); - canvas_set_color(canvas, ColorBlack); - canvas_draw_frame(canvas, 32, 15, 66, 13); - canvas_set_font(canvas, FontSecondary); -} - -void draw_play_menu(Canvas* const canvas, const GameState* game_state) { - const char* menus[3] = {"Double", "Hit", "Stay"}; - for(uint8_t m = 0; m < 3; m++) { - if(m == 0 && - (game_state->doubled || game_state->player_score < game_state->settings.round_price)) - continue; - int y = m * 13 + 25; - canvas_set_color(canvas, ColorBlack); - - if(game_state->selectedMenu == m) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 1, y, 31, 12); - } else { - canvas_set_color(canvas, ColorWhite); - canvas_draw_box(canvas, 1, y, 31, 12); - canvas_set_color(canvas, ColorBlack); - canvas_draw_frame(canvas, 1, y, 31, 12); - } - - if(game_state->selectedMenu == m) - canvas_set_color(canvas, ColorWhite); - else - canvas_set_color(canvas, ColorBlack); - canvas_draw_str_aligned(canvas, 16, y + 6, AlignCenter, AlignCenter, menus[m]); - } -} - -void draw_screen(Canvas* const canvas, const bool* points) { - for(uint8_t x = 0; x < 128; x++) { - for(uint8_t y = 0; y < 64; y++) { - if(points[y * 128 + x]) canvas_draw_dot(canvas, x, y); - } - } -} - -void draw_score(Canvas* const canvas, bool top, uint8_t amount) { - char drawChar[20]; - snprintf(drawChar, sizeof(drawChar), "Player score: %i", amount); - if(top) - canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, drawChar); - else - canvas_draw_str_aligned(canvas, 64, 62, AlignCenter, AlignBottom, drawChar); -} - -void draw_money(Canvas* const canvas, uint32_t score) { - canvas_set_font(canvas, FontSecondary); - char drawChar[11]; - uint32_t currAmount = score; - if(currAmount < 1000) { - snprintf(drawChar, sizeof(drawChar), "$%lu", currAmount); - } else { - char c = 'K'; - for(uint8_t i = 0; i < 4; i++) { - currAmount = currAmount / 1000; - if(currAmount < 1000) { - c = MoneyMul[i]; - break; - } - } - - snprintf(drawChar, sizeof(drawChar), "$%lu %c", currAmount, c); - } - canvas_draw_str_aligned(canvas, 126, 2, AlignRight, AlignTop, drawChar); -} - -void draw_menu( - Canvas* const canvas, - const char* text, - const char* value, - int8_t y, - bool left_caret, - bool right_caret, - bool selected) { - UNUSED(selected); - if(y < 0 || y >= 64) return; - - if(selected) { - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, y, 122, LINE_HEIGHT); - canvas_set_color(canvas, ColorWhite); - } - - canvas_draw_str_aligned(canvas, 4, y + ITEM_PADDING, AlignLeft, AlignTop, text); - if(left_caret) canvas_draw_str_aligned(canvas, 80, y + ITEM_PADDING, AlignLeft, AlignTop, "<"); - canvas_draw_str_aligned(canvas, 100, y + ITEM_PADDING, AlignCenter, AlignTop, value); - if(right_caret) - canvas_draw_str_aligned(canvas, 120, y + ITEM_PADDING, AlignRight, AlignTop, ">"); - - canvas_set_color(canvas, ColorBlack); -} - -void settings_page(Canvas* const canvas, const GameState* gameState) { - char drawChar[10]; - int startY = 0; - if(LINE_HEIGHT * (gameState->selectedMenu + 1) >= 64) { - startY -= (LINE_HEIGHT * (gameState->selectedMenu + 1)) - 64; - } - - int scrollHeight = round(64 / 6.0) + ITEM_PADDING * 2; - int scrollPos = 64 / (6.0 / (gameState->selectedMenu + 1)) - ITEM_PADDING * 2; - - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 123, scrollPos, 4, scrollHeight); - canvas_draw_box(canvas, 125, 0, 1, 64); - - snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.starting_money); - draw_menu( - canvas, - "Start money", - drawChar, - 0 * LINE_HEIGHT + startY, - gameState->settings.starting_money > gameState->settings.round_price, - gameState->settings.starting_money < 400, - gameState->selectedMenu == 0); - snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.round_price); - draw_menu( - canvas, - "Round price", - drawChar, - 1 * LINE_HEIGHT + startY, - gameState->settings.round_price > 10, - gameState->settings.round_price < gameState->settings.starting_money, - gameState->selectedMenu == 1); - - snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.animation_duration); - draw_menu( - canvas, - "Anim. length", - drawChar, - 2 * LINE_HEIGHT + startY, - gameState->settings.animation_duration > 0, - gameState->settings.animation_duration < 2000, - gameState->selectedMenu == 2); - snprintf(drawChar, sizeof(drawChar), "%li", gameState->settings.message_duration); - draw_menu( - canvas, - "Popup time", - drawChar, - 3 * LINE_HEIGHT + startY, - gameState->settings.message_duration > 0, - gameState->settings.message_duration < 2000, - gameState->selectedMenu == 3); - // draw_menu(canvas, "Sound", gameState->settings.sound_effects ? "Yes" : "No", - // 5 * LINE_HEIGHT + startY, - // true, - // true, - // gameState->selectedMenu == 5 - // ); -} \ No newline at end of file diff --git a/applications/external/blackjack/ui.h b/applications/external/blackjack/ui.h deleted file mode 100644 index 51b388010..000000000 --- a/applications/external/blackjack/ui.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "defines.h" -#include - -void draw_player_scene(Canvas* const canvas, const GameState* game_state); - -void draw_dealer_scene(Canvas* const canvas, const GameState* game_state); - -void draw_play_menu(Canvas* const canvas, const GameState* game_state); - -void draw_score(Canvas* const canvas, bool top, uint8_t amount); - -void draw_money(Canvas* const canvas, uint32_t score); -void settings_page(Canvas* const canvas, const GameState* gameState); - -void popup_frame(Canvas* const canvas); -void draw_screen(Canvas* const canvas, const bool* points); diff --git a/applications/external/blackjack/util.c b/applications/external/blackjack/util.c deleted file mode 100644 index 8e88c2231..000000000 --- a/applications/external/blackjack/util.c +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include "util.h" - -const char* CONFIG_FILE_PATH = EXT_PATH(".blackjack.settings"); - -void save_settings(Settings settings) { - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - FURI_LOG_D(APP_NAME, "Saving config"); - if(flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { - FURI_LOG_D( - APP_NAME, "Saving %s: %ld", CONF_ANIMATION_DURATION, settings.animation_duration); - flipper_format_update_uint32( - file, CONF_ANIMATION_DURATION, &(settings.animation_duration), 1); - - FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_MESSAGE_DURATION, settings.message_duration); - flipper_format_update_uint32(file, CONF_MESSAGE_DURATION, &(settings.message_duration), 1); - - FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_STARTING_MONEY, settings.starting_money); - flipper_format_update_uint32(file, CONF_STARTING_MONEY, &(settings.starting_money), 1); - - FURI_LOG_D(APP_NAME, "Saving %s: %ld", CONF_ROUND_PRICE, settings.round_price); - flipper_format_update_uint32(file, CONF_ROUND_PRICE, &(settings.round_price), 1); - - FURI_LOG_D(APP_NAME, "Saving %s: %i", CONF_SOUND_EFFECTS, settings.sound_effects ? 1 : 0); - flipper_format_update_bool(file, CONF_SOUND_EFFECTS, &(settings.sound_effects), 1); - FURI_LOG_D(APP_NAME, "Config saved"); - } else { - FURI_LOG_E(APP_NAME, "Save error"); - } - flipper_format_file_close(file); - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); -} - -void save_settings_file(FlipperFormat* file, Settings* settings) { - flipper_format_write_header_cstr(file, CONFIG_FILE_HEADER, CONFIG_FILE_VERSION); - flipper_format_write_comment_cstr(file, "Card animation duration in ms"); - flipper_format_write_uint32(file, CONF_ANIMATION_DURATION, &(settings->animation_duration), 1); - flipper_format_write_comment_cstr(file, "Popup message duration in ms"); - flipper_format_write_uint32(file, CONF_MESSAGE_DURATION, &(settings->message_duration), 1); - flipper_format_write_comment_cstr(file, "Player's starting money"); - flipper_format_write_uint32(file, CONF_STARTING_MONEY, &(settings->starting_money), 1); - flipper_format_write_comment_cstr(file, "Round price"); - flipper_format_write_uint32(file, CONF_ROUND_PRICE, &(settings->round_price), 1); - flipper_format_write_comment_cstr(file, "Enable sound effects"); - flipper_format_write_bool(file, CONF_SOUND_EFFECTS, &(settings->sound_effects), 1); -} - -Settings load_settings() { - Settings settings; - - FURI_LOG_D(APP_NAME, "Loading default settings"); - settings.animation_duration = 800; - settings.message_duration = 1500; - settings.starting_money = 200; - settings.round_price = 10; - settings.sound_effects = true; - - FURI_LOG_D(APP_NAME, "Opening storage"); - Storage* storage = furi_record_open(RECORD_STORAGE); - FURI_LOG_D(APP_NAME, "Allocating file"); - FlipperFormat* file = flipper_format_file_alloc(storage); - - FURI_LOG_D(APP_NAME, "Allocating string"); - FuriString* string_value; - string_value = furi_string_alloc(); - - if(storage_common_stat(storage, CONFIG_FILE_PATH, NULL) != FSE_OK) { - FURI_LOG_D(APP_NAME, "Config file %s not found, creating new one...", CONFIG_FILE_PATH); - if(!flipper_format_file_open_new(file, CONFIG_FILE_PATH)) { - FURI_LOG_E(APP_NAME, "Error creating new file %s", CONFIG_FILE_PATH); - flipper_format_file_close(file); - } else { - save_settings_file(file, &settings); - } - } else { - if(!flipper_format_file_open_existing(file, CONFIG_FILE_PATH)) { - FURI_LOG_E(APP_NAME, "Error opening existing file %s", CONFIG_FILE_PATH); - flipper_format_file_close(file); - } else { - uint32_t value; - bool valueBool; - FURI_LOG_D(APP_NAME, "Checking version"); - if(!flipper_format_read_header(file, string_value, &value)) { - FURI_LOG_E(APP_NAME, "Config file mismatch"); - } else { - FURI_LOG_D(APP_NAME, "Loading %s", CONF_ANIMATION_DURATION); - if(flipper_format_read_uint32(file, CONF_ANIMATION_DURATION, &value, 1)) { - settings.animation_duration = value; - FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ANIMATION_DURATION, value); - } - FURI_LOG_D(APP_NAME, "Loading %s", CONF_MESSAGE_DURATION); - if(flipper_format_read_uint32(file, CONF_MESSAGE_DURATION, &value, 1)) { - settings.message_duration = value; - FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_MESSAGE_DURATION, value); - } - FURI_LOG_D(APP_NAME, "Loading %s", CONF_STARTING_MONEY); - if(flipper_format_read_uint32(file, CONF_STARTING_MONEY, &value, 1)) { - settings.starting_money = value; - FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_STARTING_MONEY, value); - } - FURI_LOG_D(APP_NAME, "Loading %s", CONF_ROUND_PRICE); - if(flipper_format_read_uint32(file, CONF_ROUND_PRICE, &value, 1)) { - settings.round_price = value; - FURI_LOG_D(APP_NAME, "Loaded %s: %ld", CONF_ROUND_PRICE, value); - } - FURI_LOG_D(APP_NAME, "Loading %s", CONF_SOUND_EFFECTS); - if(flipper_format_read_bool(file, CONF_SOUND_EFFECTS, &valueBool, 1)) { - settings.sound_effects = valueBool; - FURI_LOG_D(APP_NAME, "Loaded %s: %i", CONF_ROUND_PRICE, valueBool ? 1 : 0); - } - } - flipper_format_file_close(file); - } - } - - furi_string_free(string_value); - // flipper_format_file_close(file); - flipper_format_free(file); - furi_record_close(RECORD_STORAGE); - return settings; -} \ No newline at end of file diff --git a/applications/external/blackjack/util.h b/applications/external/blackjack/util.h deleted file mode 100644 index 4bcc4d890..000000000 --- a/applications/external/blackjack/util.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "defines.h" -#define CONFIG_FILE_HEADER "Blackjack config file" -#define CONFIG_FILE_VERSION 1 - -void save_settings(Settings settings); -Settings load_settings(); \ No newline at end of file diff --git a/applications/external/bomberduck/LICENSE b/applications/external/bomberduck/LICENSE deleted file mode 100644 index 4624b249c..000000000 --- a/applications/external/bomberduck/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - -Copyright (c) 2023 лень - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/applications/external/bomberduck/README.md b/applications/external/bomberduck/README.md deleted file mode 100644 index 2d133145a..000000000 --- a/applications/external/bomberduck/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# flipperzero-bomberduck -Bomberman clone on flipper zero! diff --git a/applications/external/bomberduck/application.fam b/applications/external/bomberduck/application.fam deleted file mode 100644 index 5242d0f8c..000000000 --- a/applications/external/bomberduck/application.fam +++ /dev/null @@ -1,17 +0,0 @@ -App( - appid="bomberduck", - name="Bomberduck", - apptype=FlipperAppType.EXTERNAL, - entry_point="bomberduck_app", - requires=[ - "gui", - ], - stack_size=1 * 1024, - order=90, - fap_icon="bomb.png", - fap_category="Games", - fap_icon_assets="assets", - fap_author="@leo-need-more-coffee & @xMasterX", - fap_version="1.0", - fap_description="Bomberduck(Bomberman) Game", -) diff --git a/applications/external/bomberduck/assets/bomb0.png b/applications/external/bomberduck/assets/bomb0.png deleted file mode 100644 index 3fdc3a3c12c7ede770e1c99c2003c777b03f4d75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4536 zcmeHKYj6|S6<%y!HW&j1Oo@SwNCLLA+NY$|R^nmFU;#3+tr`c~z&`HETT5CYtu5IJ znE)k#DRI*@1C1$^5T@xs878Hv(-e#gv`h*^utTUBhjB~-37JAnNeB}jy}R;rl9`UD z<)1Yp>F&AT`R+O2Irq-qExw{f8R^r~5d_Kb6uL{`zdlYModSPTeC5~R-}PF5xmLo* z(MVL4l^{U1ng~EaLXi<9adBI5+qM(v*n7XN! z8V(A9salG3vEWq)igso#5Xb;gQUCQc;F zHl-hajl?w1R1tbak+dRm#Zgp znD5TDFF6a3&(A)&s&#QeuBm!_@hRW0pWIycY1w|?Pt*65Rh)1BxFvIs$JNr=Ij3<- z_TBOWTqFDWwT|4;>t~_|%|&(QlFkRe$lp3O^Tltvu3x%*eLDL7lBIu0A?LT|$+;7< z=(fq^*LBDX?`d9zI*S=Gf$2EV#^6q-Z8nes`Gbe z>Y8hIv#0CcT>Na2<0ma6w#7=2vkEQ_+Am@+1G;4qo@>a?!M*0|4W z?`Bcv{X74}R_@EKy!3Jt@ypij+Z&5wXWSq5 zcl@O#k6su#)qbV;g^_Jt8y+;@2><@>iN=OWb*a(T&yMDdx%FYnkN1bw zGi!IwMU-o~HL2OzDYHhr`}DsP@xU|cgI&s3w^L{Mqo4n)^w50-8Mjh_tyu0|%86>o z%uA{O%!yD0_9%il<|QJ$SP3*#00AZJG~IZo$%HDB)3n^;CA<+As89-PqoB05$S>Ab zigw8~Z*ICH!9ju$(0DWv3Wj4`!f7&iIXKqMxCu2NTBXxe?)9NAH40G1%$NzRAfZ%K zrn%{;BPz*UiF;u;1$=UvDl{#^;dneAH^*tS8V%s2-EPMT3a2OxBCuFZSmP5|I5tbC zNOHJAOpGcKO;N+B&dCdEmF6^=;5^zBUnt`B_R@!A-6}vm@B|;hNi%_mLU@0Vm{w2? zNxB32PLG%$?r6LO#MG*&2nwn}Sewcq76!AE423Z^n-#M&7B~`ENt7+TOF z%VPjoWJ(r!t0>b3l_YWt)o6%^wI4Xbg6u&PftxIys}4g@egJVR^kzA%Wom zbKzW3z-wyMuc|?(Ne>CtJ$t9Ua63u7#=Ch9KvIIDIfCJ6!cUMKVdE%!9sw==@T#Q9 zHUAB*ZywZ{51CBi0%mSl_e3i!XI*xM5{F)&33t{;m%MRnlcDti*p?;M5FByI3~hQ~eLd){|z z2IGC0pYC>hm`kAO&?1A8H;+Tm(fQ3^K4=PV`Vk4%l%WMT8i zU6+p|2Sy^F9<5lqb<_El58BHnhwrG5w6+vqZ1P6;XZ(eJBr|7J!+Vem@e~xf59Y64 F{~v;POwj-U diff --git a/applications/external/bomberduck/assets/bomb1.png b/applications/external/bomberduck/assets/bomb1.png deleted file mode 100644 index 11d05b9b7c445452253792c5a51ad6448cca8aee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4529 zcmeHKYj6|S6<*swEEzB`38t=zH-ZE|vf77MudM_zlEDIGgsmEfOoR7vSKeCE3hmmG zO&&l(8cb5>#Xtjvl7wkGJcdcr)G0%W3u&1IPlBCsjCSvy`~0E_FP>t|H6jQy#aHI7fOl4^|{MfZC%F{ugZOhB<(1vZ>mp)M`9skt&V!ivdjpyd=Jdl<4+CgUF)_3gPmz-S7 zeUE&xdgGrypCtbQUqyHSbJGVa0==Q#HNSE(ow?bsJhy~+V>;eeuoi1M`N_te6@C1w zxyygCxnz5wuSw*{zU?urHW+Izp1knw{zZ=`l)ej_uZQ+sne^e`6Y1A7bDu$ww2hL-6YzOFgNeg* zcRc#MyX@!#dGD_7TvA$OYM8d-c;J`!KizzxYH#34FDVxXq}mN zy?P(h>iDX+yC`!*K6=1h-fXVux%I-r&9kz9{$2mY&(2<)jefLr*>BUy2Re(zqUkx7 zuDi%@nvthI+)SQ0eu5J3Ei`=4U!R@7y6+X&``>-K>(z(aADnz?(=k%qc2`e*&u`kB z+ZuN{PBg!<-9gs-F@&K%TbZBF3!7eW>#e1n`H6Jx&3q3ct2L( z%c0DBcl_Q_x2LG?vlovLFLiF++FGtmGu&S@eTKK-buQ!H;IutIp7-v;=BsD#zM1wH z{Q0H#oSHPt`NfK-CUx~cajUH_y8rsI*6nvTXUxXmxeE3JOQAM=1(sjMjuDx1oKd{Z6o z6if@|8(m2T5=4N?qRB`&sxV2nN#|wYSTo}$REMZ_Zd0{CfO_OOKxs2=Ca}_^)Igc$ z8&OwW5Sa?^q5%r{n(Ik{eSn@n&X9gHs$^ZSSBqso8^P!BxG#&FV1;E@PE(nC>88z9L* zK;P@31mT9pD}W-`$9YiN0HW%gkrV9tU9%flvjmN{(7`vCkhE^3Ws* zNs*ZD1;rkPR3&j(tWmLPGkQ8B1A*>Cyra;Ax$9tv^7|RD%-3t-`MhqEwmu`syd*IC zkmfi$&su5BNzxQX+wC^YNn7EFa|pa>W$hfzj-c{I6_t(hK%;`>W(o3$qJ`pEs|}-V z0A3uNh;ck=#VDI-rwGE8h6akegtEoxa9F&8wktAuekd)Il45|Wg1$LswNfKrYtVPG;)SXS#(NTAzc zE{rD*SXGV(WjX9NX(6GS=g_ntZYP0NSud*sNJ>x^hM*Y>5hO^4aKJ$^0WBl&vLK0# z{|&8e9@Ld8d6}fZ^&53lYDZOqHL0i6V_4F+5{l}Zf?@eo3JTi*1U*j3m73yf*=PvB z(_^4r2kp}Lv;qlBE!t@d#&HDfA<+WsXAv!!jTA+aA~*_=_R;K$EUF1M4oX5$N2nES zPhBgtKrd9`Xmp|$XjOoMVFZm)&LBlHP&|qp5DdTlDbg0Z18^jPIqY!P&{mGbSc;=C zo~JlMw32o!aEuQ6e@St;C;lx(7p@(PgGF`W|55f7V8l5JrAgV~`3#SHeCWI%&`dj7 zZ{u&E-EQLwka~QPG3h%l*SK6`QeaHr@$MRzYfK7^2|V6i|2Mge!(WUb3SaaR@O!W* z{^U#WThb6Kdr(0T^BnD;mfW+T0UGa6eg4ur&QF*zcD`;u^wIl|ukC9&oAubK9~ufwSNA{`#8+DGJ+QE5!@mG~Pe@e& diff --git a/applications/external/bomberduck/assets/bomb2.png b/applications/external/bomberduck/assets/bomb2.png deleted file mode 100644 index 38ce7c732bfa5e3586d1c9b9a754b85405cd4566..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4531 zcmeHKYj6|S6<$9e*oGLGfT_peML+;Mt9|ORTZxAyg9XS4TQv?$fqmSSHA3^WiZAxzpKrA(4i6NVBeq@^iL8oQJ_lVlvzgcO=MrVOMFkKSGRIn7MR z)AG-nk#zUmbH01dcfNaP@3uhs(h1r5*$9G6@RfNh;J-1>9+wV((*xC4;NRzs!D_vN zPoS~5Dl1`t>J2e~f}|oNNb>!sS9Lyp1|5I!#1@WW-qE0gfHd5>Z{-}`g({)!%P z&Fqyw*}iyhV1DPvt7jJf<#OZ15A%B0)#T@#dW>FB2{Wmz+EVqawu+sH-rq1`&9jpy zlT|<1w5X}~iC<=pA#T>_x2q1FK6xT>z4Q)U)tK>KD3S56b|4{0TC?Ku1biM(f8ns) zeNE4}%ida4@b>zSWu--y`iZN~2A+RtYs+Ue|AFQgm(Dv8%+=D((dw2J|)~N-z zs$b?>oma1P6^+|ch#s|;w^%E>@BXM{`?TC0U-y3T(WNh@qkmey;?;C=QAe>{G%1hn zoI-xpf;{=h?c}+$=O}s3{H%9->v9X%_q^cx!`B}iIJEZs!{fi)c7~MqOzE!c{`vWq zwuS@Fb1knd+f?p)qTt>!A&kCVS`oOvsoMoOt**8PHaR{sv3be)cHmW zQ=i}$H~(#Ado6~=Gp~m;@-AZj&J1hfu{(1;cChqX#{<_Ix^6%9oB27{KTQAbk*IpU zY2N}wxl+`SQBaURYs@?A{*g?C9#!w|S8m+Qm=TO${k-zn9R#_5tpb%;?O(x(YQ)M* zssOCXNDN98L0k)yF5Y%C9ojkO{x zSr#tHb|pCo5CJ-mCL`gf#wFbr6PJTy!;D){(?zd!TdMs5)T71$%2*jIft4neddjjO z8+FAcnXB+F>4ShzZcB}>$2c5MBofvHZB^qToMc%RCn%hvFzA744N;v>Vo`0D0g=M+ z0!@r7FA_Yc5FwLTUgA9#|F;iQ$oBN2RPgr=9)Ly*3N zzA-`z!VQgA08Ooni=ea~MDLA4a?1#G0Pd62n-*<9p>F%yXg$Q{C>`>igiYMKCjzitj|fRs7RbS zWCXz>@-_xzNru80hr^Dsj17(irzFZY-XSpj5GY?%)A^_f3@8Y0RUnR?b~3cU5}4f~ z*f550Ix&G_Y#3o<8PXxM5=D_iAXdf|s7gLOG%Ev2f>2J965#Q~q&LRpDHOCZeaG7NBARw&WB zMoOp=IWX;q+ezYe-plI%f)W(X5e!EYL4xE677mIDXc^+KN{Za@ z-@J{@gSt{JFH!Uv6i8X3WCufIfj^#%7Kb2@kSi!p zlPh$d*{J!$y%ROS=mI1RBN&WggA~a@@+h*8F#O(^DB5|_LECvuq$B~Vof0u814xV( zL`oK%EW_GT`WPVae<^WL6V&h$UAS>9_BYjq|3}+X4nxjSNKMKH&u4ht;{)e?A7?P% zd->^;+r3-@MMoDI5x=8!jnXwD21evOs;*JGM#R8~oJZC5f1@jV@Wu$D@I@~H?}KlC zwr(rDOJ>E&9@Y@VI?MQ`B@ZvGhsOJKpTG3J%bC-sW}yQA_qEXUExoi__oxx$nt_;a zzd%A0ie7^nZ@;TI-=Ak(Onu(P!D$^$JAOY2A;zUQ`|e`93fbA6h0fPYDxODT?2Y63 zPrW*&>7})^$)cW1xch3(p$l^^t>3Zrh@Ae&jG38f-#JVCB8d4V+w@^|y107;Q8`s!r9<>K*$8}6g>N;sjATc4yq;7b1@BE%- zrsJ9BKTD(2-nYN+x8L{MZ+CY0`icq`7#=X_bh-teLU#%LSKEdZJ$&kYfvfOusKy^q zN|-njlSEz!14OBg0R$ujUZ+c3db;>8Pj}!cx8h58=&|)#uT^{c+77htKeX!Lk4s
- BluePill configuration example - - ```json -{ - "name": "Attach (DAP)", - "cwd": "${workspaceFolder}", - "executable": "./build/firmware.elf", - "request": "attach", - "type": "cortex-debug", - "servertype": "openocd", - "device": "cmsis-dap", - "configFiles": [ - "interface/cmsis-dap.cfg", - "target/stm32f1x.cfg", - ], -}, - ``` -
- -
- Flipper Zero configuration example - - ```json -{ - "name": "Attach (DAP)", - "cwd": "${workspaceFolder}", - "executable": "./build/latest/firmware.elf", - "request": "attach", - "type": "cortex-debug", - "servertype": "openocd", - "device": "cmsis-dap", - "svdFile": "./debug/STM32WB55_CM4.svd", - "rtos": "FreeRTOS", - "configFiles": [ - "interface/cmsis-dap.cfg", - "./debug/stm32wbx.cfg", - ], - "postAttachCommands": [ - "source debug/flipperapps.py", - ], -}, - ``` -
- -### OpenOCD -Use `interface/cmsis-dap.cfg`. You will need OpenOCD v0.11.0. - -Additional commands: -* `cmsis_dap_backend hid` for CMSIS-DAP v1 protocol. -* `cmsis_dap_backend usb_bulk` for CMSIS-DAP v2 protocol. -* `cmsis_dap_serial DAP_Oyevoxo` use DAP-Link running on Flipper named `Oyevoxo`. -* `cmsis-dap cmd 81` - reboot connected DAP-Link. - -
- Flash BluePill - - ``` -openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c init -c "program build/firmware.bin reset exit 0x8000000" - ``` -
- -
- Flash Flipper Zero using DAP v2 protocol - - ``` -openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_backend usb_bulk" -f debug/stm32wbx.cfg -c init -c "program build/latest/firmware.bin reset exit 0x8000000" - ``` -
- -
- Reboot connected DAP-Link on Flipper named Oyevoxo - - ``` -openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_serial DAP_Oyevoxo" -c "transport select swd" -c "adapter speed 4000000" -c init -c "cmsis-dap cmd 81" -c "exit" - ``` -
- -### PlatformIO -Use `debug_tool = cmsis-dap` and `upload_protocol = cmsis-dap`. [Documentation](https://docs.platformio.org/en/latest/plus/debug-tools/cmsis-dap.html#debugging-tool-cmsis-dap). Remember that Windows 8 and above do not require drivers. - -
- BluePill platformio.ini example - - ``` -[env:bluepill_f103c8] -platform = ststm32 -board = bluepill_f103c8 -debug_tool = cmsis-dap -upload_protocol = cmsis-dap - ``` -
diff --git a/applications/external/dap_link/application.fam b/applications/external/dap_link/application.fam deleted file mode 100644 index 017143803..000000000 --- a/applications/external/dap_link/application.fam +++ /dev/null @@ -1,24 +0,0 @@ -App( - appid="dap_link", - name="DAP Link", - apptype=FlipperAppType.EXTERNAL, - entry_point="dap_link_app", - requires=[ - "gui", - "dialogs", - ], - stack_size=4 * 1024, - order=20, - fap_icon="dap_link.png", - fap_category="GPIO", - fap_private_libs=[ - Lib( - name="free-dap", - cincludes=["."], - sources=[ - "dap.c", - ], - ), - ], - fap_icon_assets="icons", -) diff --git a/applications/external/dap_link/dap_config.h b/applications/external/dap_link/dap_config.h deleted file mode 100644 index 88b90bd34..000000000 --- a/applications/external/dap_link/dap_config.h +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -// Copyright (c) 2022, Alex Taradov . All rights reserved. - -#ifndef _DAP_CONFIG_H_ -#define _DAP_CONFIG_H_ - -/*- Includes ----------------------------------------------------------------*/ -#include - -/*- Definitions -------------------------------------------------------------*/ -#define DAP_CONFIG_ENABLE_JTAG - -#define DAP_CONFIG_DEFAULT_PORT DAP_PORT_SWD -#define DAP_CONFIG_DEFAULT_CLOCK 4200000 // Hz - -#define DAP_CONFIG_PACKET_SIZE 64 -#define DAP_CONFIG_PACKET_COUNT 1 - -#define DAP_CONFIG_JTAG_DEV_COUNT 8 - -// DAP_CONFIG_PRODUCT_STR must contain "CMSIS-DAP" to be compatible with the standard -#define DAP_CONFIG_VENDOR_STR "Flipper Zero" -#define DAP_CONFIG_PRODUCT_STR "Generic CMSIS-DAP Adapter" -#define DAP_CONFIG_SER_NUM_STR usb_serial_number -#define DAP_CONFIG_CMSIS_DAP_VER_STR "2.0.0" - -#define DAP_CONFIG_RESET_TARGET_FN dap_app_target_reset -#define DAP_CONFIG_VENDOR_FN dap_app_vendor_cmd - -// Attribute to use for performance-critical functions -#define DAP_CONFIG_PERFORMANCE_ATTR - -// A value at which dap_clock_test() produces 1 kHz output on the SWCLK pin -// #define DAP_CONFIG_DELAY_CONSTANT 19000 -#define DAP_CONFIG_DELAY_CONSTANT 6290 - -// A threshold for switching to fast clock (no added delays) -// This is the frequency produced by dap_clock_test(1) on the SWCLK pin -#define DAP_CONFIG_FAST_CLOCK 2400000 // Hz - -/*- Prototypes --------------------------------------------------------------*/ -extern char usb_serial_number[16]; - -/*- Implementations ---------------------------------------------------------*/ -extern GpioPin flipper_dap_swclk_pin; -extern GpioPin flipper_dap_swdio_pin; -extern GpioPin flipper_dap_reset_pin; -extern GpioPin flipper_dap_tdo_pin; -extern GpioPin flipper_dap_tdi_pin; - -extern void dap_app_vendor_cmd(uint8_t cmd); -extern void dap_app_target_reset(); -extern void dap_app_disconnect(); -extern void dap_app_connect_swd(); -extern void dap_app_connect_jtag(); - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWCLK_TCK_write(int value) { - furi_hal_gpio_write(&flipper_dap_swclk_pin, value); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWDIO_TMS_write(int value) { - furi_hal_gpio_write(&flipper_dap_swdio_pin, value); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_TDI_write(int value) { -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_write(&flipper_dap_tdi_pin, value); -#else - (void)value; -#endif -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_TDO_write(int value) { -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_write(&flipper_dap_tdo_pin, value); -#else - (void)value; -#endif -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_nTRST_write(int value) { - (void)value; -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_nRESET_write(int value) { - furi_hal_gpio_write(&flipper_dap_reset_pin, value); -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_SWCLK_TCK_read(void) { - return furi_hal_gpio_read(&flipper_dap_swclk_pin); -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_SWDIO_TMS_read(void) { - return furi_hal_gpio_read(&flipper_dap_swdio_pin); -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_TDO_read(void) { -#ifdef DAP_CONFIG_ENABLE_JTAG - return furi_hal_gpio_read(&flipper_dap_tdo_pin); -#else - return 0; -#endif -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_TDI_read(void) { -#ifdef DAP_CONFIG_ENABLE_JTAG - return furi_hal_gpio_read(&flipper_dap_tdi_pin); -#else - return 0; -#endif -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_nTRST_read(void) { - return 0; -} - -//----------------------------------------------------------------------------- -static inline int DAP_CONFIG_nRESET_read(void) { - return furi_hal_gpio_read(&flipper_dap_reset_pin); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWCLK_TCK_set(void) { - LL_GPIO_SetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWCLK_TCK_clr(void) { - LL_GPIO_ResetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWDIO_TMS_in(void) { - LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_INPUT); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SWDIO_TMS_out(void) { - LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_OUTPUT); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_SETUP(void) { - furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#endif -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_DISCONNECT(void) { - furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#endif - dap_app_disconnect(); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_CONNECT_SWD(void) { - furi_hal_gpio_init( - &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_swdio_pin, true); - - furi_hal_gpio_init( - &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_swclk_pin, true); - - furi_hal_gpio_init( - &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_reset_pin, true); - -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -#endif - dap_app_connect_swd(); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_CONNECT_JTAG(void) { - furi_hal_gpio_init( - &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_swdio_pin, true); - - furi_hal_gpio_init( - &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_swclk_pin, true); - - furi_hal_gpio_init( - &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_reset_pin, true); - -#ifdef DAP_CONFIG_ENABLE_JTAG - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - - furi_hal_gpio_init( - &flipper_dap_tdi_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_write(&flipper_dap_tdi_pin, true); -#endif - dap_app_connect_jtag(); -} - -//----------------------------------------------------------------------------- -static inline void DAP_CONFIG_LED(int index, int state) { - (void)index; - (void)state; -} - -//----------------------------------------------------------------------------- -__attribute__((always_inline)) static inline void DAP_CONFIG_DELAY(uint32_t cycles) { - asm volatile("1: subs %[cycles], %[cycles], #1 \n" - " bne 1b \n" - : [cycles] "+l"(cycles)); -} - -#endif // _DAP_CONFIG_H_ diff --git a/applications/external/dap_link/dap_link.c b/applications/external/dap_link/dap_link.c deleted file mode 100644 index eafb435e7..000000000 --- a/applications/external/dap_link/dap_link.c +++ /dev/null @@ -1,527 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dap_link.h" -#include "dap_config.h" -#include "gui/dap_gui.h" -#include "usb/dap_v2_usb.h" -#include -#include "dap_link_icons.h" - -/***************************************************************************/ -/****************************** DAP COMMON *********************************/ -/***************************************************************************/ - -struct DapApp { - FuriThread* dap_thread; - FuriThread* cdc_thread; - FuriThread* gui_thread; - - DapState state; - DapConfig config; -}; - -void dap_app_get_state(DapApp* app, DapState* state) { - *state = app->state; -} - -#define DAP_PROCESS_THREAD_TICK 500 - -typedef enum { - DapThreadEventStop = (1 << 0), -} DapThreadEvent; - -void dap_thread_send_stop(FuriThread* thread) { - furi_thread_flags_set(furi_thread_get_id(thread), DapThreadEventStop); -} - -GpioPin flipper_dap_swclk_pin; -GpioPin flipper_dap_swdio_pin; -GpioPin flipper_dap_reset_pin; -GpioPin flipper_dap_tdo_pin; -GpioPin flipper_dap_tdi_pin; - -/***************************************************************************/ -/****************************** DAP PROCESS ********************************/ -/***************************************************************************/ - -typedef struct { - uint8_t data[DAP_CONFIG_PACKET_SIZE]; - uint8_t size; -} DapPacket; - -typedef enum { - DAPThreadEventStop = DapThreadEventStop, - DAPThreadEventRxV1 = (1 << 1), - DAPThreadEventRxV2 = (1 << 2), - DAPThreadEventUSBConnect = (1 << 3), - DAPThreadEventUSBDisconnect = (1 << 4), - DAPThreadEventApplyConfig = (1 << 5), - DAPThreadEventAll = DAPThreadEventStop | DAPThreadEventRxV1 | DAPThreadEventRxV2 | - DAPThreadEventUSBConnect | DAPThreadEventUSBDisconnect | - DAPThreadEventApplyConfig, -} DAPThreadEvent; - -#define USB_SERIAL_NUMBER_LEN 16 -char usb_serial_number[USB_SERIAL_NUMBER_LEN] = {0}; - -const char* dap_app_get_serial(DapApp* app) { - UNUSED(app); - return usb_serial_number; -} - -static void dap_app_rx1_callback(void* context) { - furi_assert(context); - FuriThreadId thread_id = (FuriThreadId)context; - furi_thread_flags_set(thread_id, DAPThreadEventRxV1); -} - -static void dap_app_rx2_callback(void* context) { - furi_assert(context); - FuriThreadId thread_id = (FuriThreadId)context; - furi_thread_flags_set(thread_id, DAPThreadEventRxV2); -} - -static void dap_app_usb_state_callback(bool state, void* context) { - furi_assert(context); - FuriThreadId thread_id = (FuriThreadId)context; - if(state) { - furi_thread_flags_set(thread_id, DAPThreadEventUSBConnect); - } else { - furi_thread_flags_set(thread_id, DAPThreadEventUSBDisconnect); - } -} - -static void dap_app_process_v1() { - DapPacket tx_packet; - DapPacket rx_packet; - memset(&tx_packet, 0, sizeof(DapPacket)); - rx_packet.size = dap_v1_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); - dap_process_request(rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); - dap_v1_usb_tx(tx_packet.data, DAP_CONFIG_PACKET_SIZE); -} - -static void dap_app_process_v2() { - DapPacket tx_packet; - DapPacket rx_packet; - memset(&tx_packet, 0, sizeof(DapPacket)); - rx_packet.size = dap_v2_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); - size_t len = dap_process_request( - rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); - dap_v2_usb_tx(tx_packet.data, len); -} - -void dap_app_vendor_cmd(uint8_t cmd) { - // openocd -c "cmsis-dap cmd 81" - if(cmd == 0x01) { - furi_hal_power_reset(); - } -} - -void dap_app_target_reset() { - FURI_LOG_I("DAP", "Target reset"); -} - -static void dap_init_gpio(DapSwdPins swd_pins) { - switch(swd_pins) { - case DapSwdPinsPA7PA6: - flipper_dap_swclk_pin = gpio_ext_pa7; - flipper_dap_swdio_pin = gpio_ext_pa6; - break; - case DapSwdPinsPA14PA13: - flipper_dap_swclk_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_14}; - flipper_dap_swdio_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_13}; - break; - } - - flipper_dap_reset_pin = gpio_ext_pa4; - flipper_dap_tdo_pin = gpio_ext_pb3; - flipper_dap_tdi_pin = gpio_ext_pb2; -} - -static void dap_deinit_gpio(DapSwdPins swd_pins) { - // setup gpio pins to default state - furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - - if(DapSwdPinsPA14PA13 == swd_pins) { - // PA14 and PA13 are used by SWD - furi_hal_gpio_init_ex( - &flipper_dap_swclk_pin, - GpioModeAltFunctionPushPull, - GpioPullDown, - GpioSpeedLow, - GpioAltFn0JTCK_SWCLK); - furi_hal_gpio_init_ex( - &flipper_dap_swdio_pin, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn0JTMS_SWDIO); - } else { - furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - } -} - -static int32_t dap_process(void* p) { - DapApp* app = p; - DapState* dap_state = &(app->state); - - // allocate resources - FuriHalUsbInterface* usb_config_prev; - app->config.swd_pins = DapSwdPinsPA7PA6; - DapSwdPins swd_pins_prev = app->config.swd_pins; - - // init pins - dap_init_gpio(swd_pins_prev); - - // init dap - dap_init(); - - // get name - const char* name = furi_hal_version_get_name_ptr(); - if(!name) { - name = "Flipper"; - } - snprintf(usb_serial_number, USB_SERIAL_NUMBER_LEN, "DAP_%s", name); - - // init usb - usb_config_prev = furi_hal_usb_get_config(); - dap_common_usb_alloc_name(usb_serial_number); - dap_common_usb_set_context(furi_thread_get_id(furi_thread_get_current())); - dap_v1_usb_set_rx_callback(dap_app_rx1_callback); - dap_v2_usb_set_rx_callback(dap_app_rx2_callback); - dap_common_usb_set_state_callback(dap_app_usb_state_callback); - furi_hal_usb_set_config(&dap_v2_usb_hid, NULL); - - // work - uint32_t events; - while(1) { - events = furi_thread_flags_wait(DAPThreadEventAll, FuriFlagWaitAny, FuriWaitForever); - - if(!(events & FuriFlagError)) { - if(events & DAPThreadEventRxV1) { - dap_app_process_v1(); - dap_state->dap_counter++; - dap_state->dap_version = DapVersionV1; - } - - if(events & DAPThreadEventRxV2) { - dap_app_process_v2(); - dap_state->dap_counter++; - dap_state->dap_version = DapVersionV2; - } - - if(events & DAPThreadEventUSBConnect) { - dap_state->usb_connected = true; - } - - if(events & DAPThreadEventUSBDisconnect) { - dap_state->usb_connected = false; - dap_state->dap_version = DapVersionUnknown; - } - - if(events & DAPThreadEventApplyConfig) { - if(swd_pins_prev != app->config.swd_pins) { - dap_deinit_gpio(swd_pins_prev); - swd_pins_prev = app->config.swd_pins; - dap_init_gpio(swd_pins_prev); - } - } - - if(events & DAPThreadEventStop) { - break; - } - } - } - - // deinit usb - furi_hal_usb_set_config(usb_config_prev, NULL); - dap_common_usb_free_name(); - dap_deinit_gpio(swd_pins_prev); - return 0; -} - -/***************************************************************************/ -/****************************** CDC PROCESS ********************************/ -/***************************************************************************/ - -typedef enum { - CDCThreadEventStop = DapThreadEventStop, - CDCThreadEventUARTRx = (1 << 1), - CDCThreadEventCDCRx = (1 << 2), - CDCThreadEventCDCConfig = (1 << 3), - CDCThreadEventApplyConfig = (1 << 4), - CDCThreadEventAll = CDCThreadEventStop | CDCThreadEventUARTRx | CDCThreadEventCDCRx | - CDCThreadEventCDCConfig | CDCThreadEventApplyConfig, -} CDCThreadEvent; - -typedef struct { - FuriStreamBuffer* rx_stream; - FuriThreadId thread_id; - FuriHalUartId uart_id; - struct usb_cdc_line_coding line_coding; -} CDCProcess; - -static void cdc_uart_irq_cb(UartIrqEvent ev, uint8_t data, void* ctx) { - CDCProcess* app = ctx; - - if(ev == UartIrqEventRXNE) { - furi_stream_buffer_send(app->rx_stream, &data, 1, 0); - furi_thread_flags_set(app->thread_id, CDCThreadEventUARTRx); - } -} - -static void cdc_usb_rx_callback(void* context) { - CDCProcess* app = context; - furi_thread_flags_set(app->thread_id, CDCThreadEventCDCRx); -} - -static void cdc_usb_control_line_callback(uint8_t state, void* context) { - UNUSED(context); - UNUSED(state); -} - -static void cdc_usb_config_callback(struct usb_cdc_line_coding* config, void* context) { - CDCProcess* app = context; - app->line_coding = *config; - furi_thread_flags_set(app->thread_id, CDCThreadEventCDCConfig); -} - -static FuriHalUartId cdc_init_uart( - DapUartType type, - DapUartTXRX swap, - uint32_t baudrate, - void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), - void* ctx) { - FuriHalUartId uart_id = FuriHalUartIdUSART1; - if(baudrate == 0) baudrate = 115200; - - switch(type) { - case DapUartTypeUSART1: - uart_id = FuriHalUartIdUSART1; - furi_hal_console_disable(); - furi_hal_uart_deinit(uart_id); - if(swap == DapUartTXRXSwap) { - LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_SWAPPED); - } else { - LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); - } - furi_hal_uart_init(uart_id, baudrate); - furi_hal_uart_set_irq_cb(uart_id, cb, ctx); - break; - case DapUartTypeLPUART1: - uart_id = FuriHalUartIdLPUART1; - furi_hal_uart_deinit(uart_id); - if(swap == DapUartTXRXSwap) { - LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_SWAPPED); - } else { - LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); - } - furi_hal_uart_init(uart_id, baudrate); - furi_hal_uart_set_irq_cb(uart_id, cb, ctx); - break; - } - - return uart_id; -} - -static void cdc_deinit_uart(DapUartType type) { - switch(type) { - case DapUartTypeUSART1: - furi_hal_uart_deinit(FuriHalUartIdUSART1); - LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); - furi_hal_console_init(); - break; - case DapUartTypeLPUART1: - furi_hal_uart_deinit(FuriHalUartIdLPUART1); - LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); - break; - } -} - -static int32_t cdc_process(void* p) { - DapApp* dap_app = p; - DapState* dap_state = &(dap_app->state); - - dap_app->config.uart_pins = DapUartTypeLPUART1; - dap_app->config.uart_swap = DapUartTXRXNormal; - - DapUartType uart_pins_prev = dap_app->config.uart_pins; - DapUartTXRX uart_swap_prev = dap_app->config.uart_swap; - - CDCProcess* app = malloc(sizeof(CDCProcess)); - app->thread_id = furi_thread_get_id(furi_thread_get_current()); - app->rx_stream = furi_stream_buffer_alloc(512, 1); - - const uint8_t rx_buffer_size = 64; - uint8_t* rx_buffer = malloc(rx_buffer_size); - - app->uart_id = cdc_init_uart( - uart_pins_prev, uart_swap_prev, dap_state->cdc_baudrate, cdc_uart_irq_cb, app); - - dap_cdc_usb_set_context(app); - dap_cdc_usb_set_rx_callback(cdc_usb_rx_callback); - dap_cdc_usb_set_control_line_callback(cdc_usb_control_line_callback); - dap_cdc_usb_set_config_callback(cdc_usb_config_callback); - - uint32_t events; - while(1) { - events = furi_thread_flags_wait(CDCThreadEventAll, FuriFlagWaitAny, FuriWaitForever); - - if(!(events & FuriFlagError)) { - if(events & CDCThreadEventCDCConfig) { - if(dap_state->cdc_baudrate != app->line_coding.dwDTERate) { - dap_state->cdc_baudrate = app->line_coding.dwDTERate; - if(dap_state->cdc_baudrate > 0) { - furi_hal_uart_set_br(app->uart_id, dap_state->cdc_baudrate); - } - } - } - - if(events & CDCThreadEventUARTRx) { - size_t len = - furi_stream_buffer_receive(app->rx_stream, rx_buffer, rx_buffer_size, 0); - - if(len > 0) { - dap_cdc_usb_tx(rx_buffer, len); - } - dap_state->cdc_rx_counter += len; - } - - if(events & CDCThreadEventCDCRx) { - size_t len = dap_cdc_usb_rx(rx_buffer, rx_buffer_size); - if(len > 0) { - furi_hal_uart_tx(app->uart_id, rx_buffer, len); - } - dap_state->cdc_tx_counter += len; - } - - if(events & CDCThreadEventApplyConfig) { - if(uart_pins_prev != dap_app->config.uart_pins || - uart_swap_prev != dap_app->config.uart_swap) { - cdc_deinit_uart(uart_pins_prev); - uart_pins_prev = dap_app->config.uart_pins; - uart_swap_prev = dap_app->config.uart_swap; - app->uart_id = cdc_init_uart( - uart_pins_prev, - uart_swap_prev, - dap_state->cdc_baudrate, - cdc_uart_irq_cb, - app); - } - } - - if(events & CDCThreadEventStop) { - break; - } - } - } - - cdc_deinit_uart(uart_pins_prev); - free(rx_buffer); - furi_stream_buffer_free(app->rx_stream); - free(app); - - return 0; -} - -/***************************************************************************/ -/******************************* MAIN APP **********************************/ -/***************************************************************************/ - -static DapApp* dap_app_alloc() { - DapApp* dap_app = malloc(sizeof(DapApp)); - dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app); - dap_app->cdc_thread = furi_thread_alloc_ex("DAP CDC", 1024, cdc_process, dap_app); - dap_app->gui_thread = furi_thread_alloc_ex("DAP GUI", 1024, dap_gui_thread, dap_app); - return dap_app; -} - -static void dap_app_free(DapApp* dap_app) { - furi_assert(dap_app); - furi_thread_free(dap_app->dap_thread); - furi_thread_free(dap_app->cdc_thread); - furi_thread_free(dap_app->gui_thread); - free(dap_app); -} - -static DapApp* app_handle = NULL; - -void dap_app_disconnect() { - app_handle->state.dap_mode = DapModeDisconnected; -} - -void dap_app_connect_swd() { - app_handle->state.dap_mode = DapModeSWD; -} - -void dap_app_connect_jtag() { - app_handle->state.dap_mode = DapModeJTAG; -} - -void dap_app_set_config(DapApp* app, DapConfig* config) { - app->config = *config; - furi_thread_flags_set(furi_thread_get_id(app->dap_thread), DAPThreadEventApplyConfig); - furi_thread_flags_set(furi_thread_get_id(app->cdc_thread), CDCThreadEventApplyConfig); -} - -DapConfig* dap_app_get_config(DapApp* app) { - return &app->config; -} - -int32_t dap_link_app(void* p) { - UNUSED(p); - - if(furi_hal_usb_is_locked()) { - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); - dialog_message_set_text( - message, - "Disconnect from\nPC or phone to\nuse this function.", - 3, - 30, - AlignLeft, - AlignTop); - dialog_message_set_icon(message, &I_ActiveConnection_50x64, 78, 0); - dialog_message_show(dialogs, message); - dialog_message_free(message); - furi_record_close(RECORD_DIALOGS); - return -1; - } - - // alloc app - DapApp* app = dap_app_alloc(); - app_handle = app; - - furi_thread_start(app->dap_thread); - furi_thread_start(app->cdc_thread); - furi_thread_start(app->gui_thread); - - // wait until gui thread is finished - furi_thread_join(app->gui_thread); - - // send stop event to threads - dap_thread_send_stop(app->dap_thread); - dap_thread_send_stop(app->cdc_thread); - - // wait for threads to stop - furi_thread_join(app->dap_thread); - furi_thread_join(app->cdc_thread); - - // free app - dap_app_free(app); - - return 0; -} \ No newline at end of file diff --git a/applications/external/dap_link/dap_link.h b/applications/external/dap_link/dap_link.h deleted file mode 100644 index d51726c45..000000000 --- a/applications/external/dap_link/dap_link.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include - -typedef enum { - DapModeDisconnected, - DapModeSWD, - DapModeJTAG, -} DapMode; - -typedef enum { - DapVersionUnknown, - DapVersionV1, - DapVersionV2, -} DapVersion; - -typedef struct { - bool usb_connected; - DapMode dap_mode; - DapVersion dap_version; - uint32_t dap_counter; - uint32_t cdc_baudrate; - uint32_t cdc_tx_counter; - uint32_t cdc_rx_counter; -} DapState; - -typedef enum { - DapSwdPinsPA7PA6, // Pins 2, 3 - DapSwdPinsPA14PA13, // Pins 10, 12 -} DapSwdPins; - -typedef enum { - DapUartTypeUSART1, // Pins 13, 14 - DapUartTypeLPUART1, // Pins 15, 16 -} DapUartType; - -typedef enum { - DapUartTXRXNormal, - DapUartTXRXSwap, -} DapUartTXRX; - -typedef struct { - DapSwdPins swd_pins; - DapUartType uart_pins; - DapUartTXRX uart_swap; -} DapConfig; - -typedef struct DapApp DapApp; - -void dap_app_get_state(DapApp* app, DapState* state); - -const char* dap_app_get_serial(DapApp* app); - -void dap_app_set_config(DapApp* app, DapConfig* config); - -DapConfig* dap_app_get_config(DapApp* app); \ No newline at end of file diff --git a/applications/external/dap_link/dap_link.png b/applications/external/dap_link/dap_link.png deleted file mode 100644 index 2278ce2b61cf97e51720bafb2d07dae48d986560..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>@$__Y43U`H zI>D2R!GMF={pJ7tHnyBWw`O@WO=4vXnQOryxnPsm9sxmyCsV>A?tXRt_DY<&Wq)IO q>DB4a%~XFc{Gk)L^WR4G>Gvdb>|Y;BDpm&?%HZkh=d#Wzp$P!!VJ~6; diff --git a/applications/external/dap_link/gui/dap_gui.c b/applications/external/dap_link/gui/dap_gui.c deleted file mode 100644 index 4dd986153..000000000 --- a/applications/external/dap_link/gui/dap_gui.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "dap_gui.h" -#include "dap_gui_i.h" - -#define DAP_GUI_TICK 250 - -static bool dap_gui_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - DapGuiApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool dap_gui_back_event_callback(void* context) { - furi_assert(context); - DapGuiApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void dap_gui_tick_event_callback(void* context) { - furi_assert(context); - DapGuiApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -DapGuiApp* dap_gui_alloc() { - DapGuiApp* app = malloc(sizeof(DapGuiApp)); - app->gui = furi_record_open(RECORD_GUI); - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&dap_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - - view_dispatcher_set_custom_event_callback(app->view_dispatcher, dap_gui_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, dap_gui_back_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, dap_gui_tick_event_callback, DAP_GUI_TICK); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->notifications = furi_record_open(RECORD_NOTIFICATION); - - app->var_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - DapGuiAppViewVarItemList, - variable_item_list_get_view(app->var_item_list)); - - app->main_view = dap_main_view_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, DapGuiAppViewMainView, dap_main_view_get_view(app->main_view)); - - app->widget = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, DapGuiAppViewWidget, widget_get_view(app->widget)); - - scene_manager_next_scene(app->scene_manager, DapSceneMain); - - return app; -} - -void dap_gui_free(DapGuiApp* app) { - view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewVarItemList); - variable_item_list_free(app->var_item_list); - - view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewMainView); - dap_main_view_free(app->main_view); - - view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewWidget); - widget_free(app->widget); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Close records - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - - free(app); -} - -int32_t dap_gui_thread(void* arg) { - DapGuiApp* app = dap_gui_alloc(); - app->dap_app = arg; - - notification_message_block(app->notifications, &sequence_display_backlight_enforce_on); - view_dispatcher_run(app->view_dispatcher); - notification_message_block(app->notifications, &sequence_display_backlight_enforce_auto); - - dap_gui_free(app); - return 0; -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/dap_gui.h b/applications/external/dap_link/gui/dap_gui.h deleted file mode 100644 index 3d8e6bdf9..000000000 --- a/applications/external/dap_link/gui/dap_gui.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -int32_t dap_gui_thread(void* arg); \ No newline at end of file diff --git a/applications/external/dap_link/gui/dap_gui_custom_event.h b/applications/external/dap_link/gui/dap_gui_custom_event.h deleted file mode 100644 index 8b127c9d4..000000000 --- a/applications/external/dap_link/gui/dap_gui_custom_event.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -typedef enum { - DapAppCustomEventConfig, - DapAppCustomEventHelp, - DapAppCustomEventAbout, -} DapAppCustomEvent; diff --git a/applications/external/dap_link/gui/dap_gui_i.h b/applications/external/dap_link/gui/dap_gui_i.h deleted file mode 100644 index 59411e78c..000000000 --- a/applications/external/dap_link/gui/dap_gui_i.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "dap_gui.h" -#include "../dap_link.h" -#include "scenes/config/dap_scene.h" -#include "dap_gui_custom_event.h" -#include "views/dap_main_view.h" - -typedef struct { - DapApp* dap_app; - - Gui* gui; - NotificationApp* notifications; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - - VariableItemList* var_item_list; - DapMainView* main_view; - Widget* widget; -} DapGuiApp; - -typedef enum { - DapGuiAppViewVarItemList, - DapGuiAppViewMainView, - DapGuiAppViewWidget, -} DapGuiAppView; diff --git a/applications/external/dap_link/gui/scenes/config/dap_scene.c b/applications/external/dap_link/gui/scenes/config/dap_scene.c deleted file mode 100644 index 37e235540..000000000 --- a/applications/external/dap_link/gui/scenes/config/dap_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "dap_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const dap_scene_on_enter_handlers[])(void*) = { -#include "dap_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_event handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, -bool (*const dap_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "dap_scene_config.h" -}; -#undef ADD_SCENE - -// Generate scene on_exit handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, -void (*const dap_scene_on_exit_handlers[])(void* context) = { -#include "dap_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers dap_scene_handlers = { - .on_enter_handlers = dap_scene_on_enter_handlers, - .on_event_handlers = dap_scene_on_event_handlers, - .on_exit_handlers = dap_scene_on_exit_handlers, - .scene_num = DapSceneNum, -}; diff --git a/applications/external/dap_link/gui/scenes/config/dap_scene.h b/applications/external/dap_link/gui/scenes/config/dap_scene.h deleted file mode 100644 index 6fb38da4a..000000000 --- a/applications/external/dap_link/gui/scenes/config/dap_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) DapScene##id, -typedef enum { -#include "dap_scene_config.h" - DapSceneNum, -} DapScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers dap_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "dap_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "dap_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "dap_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/dap_link/gui/scenes/config/dap_scene_config.h b/applications/external/dap_link/gui/scenes/config/dap_scene_config.h deleted file mode 100644 index 8957aca06..000000000 --- a/applications/external/dap_link/gui/scenes/config/dap_scene_config.h +++ /dev/null @@ -1,4 +0,0 @@ -ADD_SCENE(dap, main, Main) -ADD_SCENE(dap, config, Config) -ADD_SCENE(dap, help, Help) -ADD_SCENE(dap, about, About) \ No newline at end of file diff --git a/applications/external/dap_link/gui/scenes/dap_scene_about.c b/applications/external/dap_link/gui/scenes/dap_scene_about.c deleted file mode 100644 index 0974e60a7..000000000 --- a/applications/external/dap_link/gui/scenes/dap_scene_about.c +++ /dev/null @@ -1,68 +0,0 @@ -#include "../dap_gui_i.h" - -#define DAP_VERSION_APP "0.1.0" -#define DAP_DEVELOPED "Dr_Zlo" -#define DAP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" - -void dap_scene_about_on_enter(void* context) { - DapGuiApp* app = context; - - FuriString* temp_str; - temp_str = furi_string_alloc(); - furi_string_printf(temp_str, "\e#%s\n", "Information"); - - furi_string_cat_printf(temp_str, "Version: %s\n", DAP_VERSION_APP); - furi_string_cat_printf(temp_str, "Developed by: %s\n", DAP_DEVELOPED); - furi_string_cat_printf(temp_str, "Github: %s\n\n", DAP_GITHUB); - - furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); - furi_string_cat_printf( - temp_str, "CMSIS-DAP debugger\nbased on Free-DAP\nThanks to Alex Taradov\n\n"); - - furi_string_cat_printf( - temp_str, - "Supported protocols:\n" - "SWD, JTAG, UART\n" - "DAP v1 (cmsis_backend hid), DAP v2 (cmsis_backend usb_bulk), VCP\n"); - - widget_add_text_box_element( - app->widget, - 0, - 0, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! \e!\n", - false); - widget_add_text_box_element( - app->widget, - 0, - 2, - 128, - 14, - AlignCenter, - AlignBottom, - "\e#\e! DAP Link \e!\n", - false); - widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); - furi_string_free(temp_str); - - view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); -} - -bool dap_scene_about_on_event(void* context, SceneManagerEvent event) { - DapGuiApp* app = context; - bool consumed = false; - UNUSED(app); - UNUSED(event); - - return consumed; -} - -void dap_scene_about_on_exit(void* context) { - DapGuiApp* app = context; - - // Clear views - widget_reset(app->widget); -} diff --git a/applications/external/dap_link/gui/scenes/dap_scene_config.c b/applications/external/dap_link/gui/scenes/dap_scene_config.c deleted file mode 100644 index 48d5fedcd..000000000 --- a/applications/external/dap_link/gui/scenes/dap_scene_config.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "../dap_gui_i.h" - -static const char* swd_pins[] = {[DapSwdPinsPA7PA6] = "2,3", [DapSwdPinsPA14PA13] = "10,12"}; -static const char* uart_pins[] = {[DapUartTypeUSART1] = "13,14", [DapUartTypeLPUART1] = "15,16"}; -static const char* uart_swap[] = {[DapUartTXRXNormal] = "No", [DapUartTXRXSwap] = "Yes"}; - -static void swd_pins_cb(VariableItem* item) { - DapGuiApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, swd_pins[index]); - - DapConfig* config = dap_app_get_config(app->dap_app); - config->swd_pins = index; - dap_app_set_config(app->dap_app, config); -} - -static void uart_pins_cb(VariableItem* item) { - DapGuiApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, uart_pins[index]); - - DapConfig* config = dap_app_get_config(app->dap_app); - config->uart_pins = index; - dap_app_set_config(app->dap_app, config); -} - -static void uart_swap_cb(VariableItem* item) { - DapGuiApp* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, uart_swap[index]); - - DapConfig* config = dap_app_get_config(app->dap_app); - config->uart_swap = index; - dap_app_set_config(app->dap_app, config); -} - -static void ok_cb(void* context, uint32_t index) { - DapGuiApp* app = context; - switch(index) { - case 3: - view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventHelp); - break; - case 4: - view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventAbout); - break; - default: - break; - } -} - -void dap_scene_config_on_enter(void* context) { - DapGuiApp* app = context; - VariableItemList* var_item_list = app->var_item_list; - VariableItem* item; - DapConfig* config = dap_app_get_config(app->dap_app); - - item = variable_item_list_add( - var_item_list, "SWC SWD Pins", COUNT_OF(swd_pins), swd_pins_cb, app); - variable_item_set_current_value_index(item, config->swd_pins); - variable_item_set_current_value_text(item, swd_pins[config->swd_pins]); - - item = - variable_item_list_add(var_item_list, "UART Pins", COUNT_OF(uart_pins), uart_pins_cb, app); - variable_item_set_current_value_index(item, config->uart_pins); - variable_item_set_current_value_text(item, uart_pins[config->uart_pins]); - - item = variable_item_list_add( - var_item_list, "Swap TX RX", COUNT_OF(uart_swap), uart_swap_cb, app); - variable_item_set_current_value_index(item, config->uart_swap); - variable_item_set_current_value_text(item, uart_swap[config->uart_swap]); - - variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL); - variable_item_list_add(var_item_list, "About", 0, NULL, NULL); - - variable_item_list_set_selected_item( - var_item_list, scene_manager_get_scene_state(app->scene_manager, DapSceneConfig)); - - variable_item_list_set_enter_callback(var_item_list, ok_cb, app); - - view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewVarItemList); -} - -bool dap_scene_config_on_event(void* context, SceneManagerEvent event) { - DapGuiApp* app = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DapAppCustomEventHelp) { - scene_manager_next_scene(app->scene_manager, DapSceneHelp); - return true; - } else if(event.event == DapAppCustomEventAbout) { - scene_manager_next_scene(app->scene_manager, DapSceneAbout); - return true; - } - } - return false; -} - -void dap_scene_config_on_exit(void* context) { - DapGuiApp* app = context; - scene_manager_set_scene_state( - app->scene_manager, - DapSceneConfig, - variable_item_list_get_selected_item_index(app->var_item_list)); - variable_item_list_reset(app->var_item_list); -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/scenes/dap_scene_help.c b/applications/external/dap_link/gui/scenes/dap_scene_help.c deleted file mode 100644 index d8d70e7ff..000000000 --- a/applications/external/dap_link/gui/scenes/dap_scene_help.c +++ /dev/null @@ -1,102 +0,0 @@ -#include "../dap_gui_i.h" - -void dap_scene_help_on_enter(void* context) { - DapGuiApp* app = context; - DapConfig* config = dap_app_get_config(app->dap_app); - FuriString* string = furi_string_alloc(); - - furi_string_cat(string, "CMSIS DAP/DAP Link v2\r\n"); - furi_string_cat_printf(string, "Serial: %s\r\n", dap_app_get_serial(app->dap_app)); - furi_string_cat( - string, - "Pinout:\r\n" - "\e#SWD:\r\n"); - - switch(config->swd_pins) { - case DapSwdPinsPA7PA6: - furi_string_cat( - string, - " SWC: 2 [A7]\r\n" - " SWD: 3 [A6]\r\n"); - break; - case DapSwdPinsPA14PA13: - furi_string_cat( - string, - " SWC: 10 [SWC]\r\n" - " SWD: 12 [SIO]\r\n"); - break; - default: - break; - } - - furi_string_cat(string, "\e#JTAG:\r\n"); - switch(config->swd_pins) { - case DapSwdPinsPA7PA6: - furi_string_cat( - string, - " TCK: 2 [A7]\r\n" - " TMS: 3 [A6]\r\n" - " RST: 4 [A4]\r\n" - " TDO: 5 [B3]\r\n" - " TDI: 6 [B2]\r\n"); - break; - case DapSwdPinsPA14PA13: - furi_string_cat( - string, - " RST: 4 [A4]\r\n" - " TDO: 5 [B3]\r\n" - " TDI: 6 [B2]\r\n" - " TCK: 10 [SWC]\r\n" - " TMS: 12 [SIO]\r\n"); - break; - default: - break; - } - - furi_string_cat(string, "\e#UART:\r\n"); - switch(config->uart_pins) { - case DapUartTypeUSART1: - if(config->uart_swap == DapUartTXRXNormal) { - furi_string_cat( - string, - " TX: 13 [TX]\r\n" - " RX: 14 [RX]\r\n"); - } else { - furi_string_cat( - string, - " RX: 13 [TX]\r\n" - " TX: 14 [RX]\r\n"); - } - break; - case DapUartTypeLPUART1: - if(config->uart_swap == DapUartTXRXNormal) { - furi_string_cat( - string, - " TX: 15 [C1]\r\n" - " RX: 16 [C0]\r\n"); - } else { - furi_string_cat( - string, - " RX: 15 [C1]\r\n" - " TX: 16 [C0]\r\n"); - } - break; - default: - break; - } - - widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(string)); - furi_string_free(string); - view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); -} - -bool dap_scene_help_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - return false; -} - -void dap_scene_help_on_exit(void* context) { - DapGuiApp* app = context; - widget_reset(app->widget); -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/scenes/dap_scene_main.c b/applications/external/dap_link/gui/scenes/dap_scene_main.c deleted file mode 100644 index 8c19bd6a5..000000000 --- a/applications/external/dap_link/gui/scenes/dap_scene_main.c +++ /dev/null @@ -1,154 +0,0 @@ -#include "../dap_gui_i.h" -#include "../../dap_link.h" - -typedef struct { - DapState dap_state; - bool dap_active; - bool tx_active; - bool rx_active; -} DapSceneMainState; - -static bool process_dap_state(DapGuiApp* app) { - DapSceneMainState* state = - (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); - if(state == NULL) return true; - - DapState* prev_state = &state->dap_state; - DapState next_state; - dap_app_get_state(app->dap_app, &next_state); - bool need_to_update = false; - - if(prev_state->dap_mode != next_state.dap_mode) { - switch(next_state.dap_mode) { - case DapModeDisconnected: - dap_main_view_set_mode(app->main_view, DapMainViewModeDisconnected); - notification_message(app->notifications, &sequence_blink_stop); - break; - case DapModeSWD: - dap_main_view_set_mode(app->main_view, DapMainViewModeSWD); - notification_message(app->notifications, &sequence_blink_start_blue); - break; - case DapModeJTAG: - dap_main_view_set_mode(app->main_view, DapMainViewModeJTAG); - notification_message(app->notifications, &sequence_blink_start_magenta); - break; - } - need_to_update = true; - } - - if(prev_state->dap_version != next_state.dap_version) { - switch(next_state.dap_version) { - case DapVersionUnknown: - dap_main_view_set_version(app->main_view, DapMainViewVersionUnknown); - break; - case DapVersionV1: - dap_main_view_set_version(app->main_view, DapMainViewVersionV1); - break; - case DapVersionV2: - dap_main_view_set_version(app->main_view, DapMainViewVersionV2); - break; - } - need_to_update = true; - } - - if(prev_state->usb_connected != next_state.usb_connected) { - dap_main_view_set_usb_connected(app->main_view, next_state.usb_connected); - need_to_update = true; - } - - if(prev_state->dap_counter != next_state.dap_counter) { - if(!state->dap_active) { - state->dap_active = true; - dap_main_view_set_dap(app->main_view, state->dap_active); - need_to_update = true; - } - } else { - if(state->dap_active) { - state->dap_active = false; - dap_main_view_set_dap(app->main_view, state->dap_active); - need_to_update = true; - } - } - - if(prev_state->cdc_baudrate != next_state.cdc_baudrate) { - dap_main_view_set_baudrate(app->main_view, next_state.cdc_baudrate); - need_to_update = true; - } - - if(prev_state->cdc_tx_counter != next_state.cdc_tx_counter) { - if(!state->tx_active) { - state->tx_active = true; - dap_main_view_set_tx(app->main_view, state->tx_active); - need_to_update = true; - notification_message(app->notifications, &sequence_blink_start_red); - } - } else { - if(state->tx_active) { - state->tx_active = false; - dap_main_view_set_tx(app->main_view, state->tx_active); - need_to_update = true; - notification_message(app->notifications, &sequence_blink_stop); - } - } - - if(prev_state->cdc_rx_counter != next_state.cdc_rx_counter) { - if(!state->rx_active) { - state->rx_active = true; - dap_main_view_set_rx(app->main_view, state->rx_active); - need_to_update = true; - notification_message(app->notifications, &sequence_blink_start_green); - } - } else { - if(state->rx_active) { - state->rx_active = false; - dap_main_view_set_rx(app->main_view, state->rx_active); - need_to_update = true; - notification_message(app->notifications, &sequence_blink_stop); - } - } - - if(need_to_update) { - dap_main_view_update(app->main_view); - } - - *prev_state = next_state; - return true; -} - -static void dap_scene_main_on_left(void* context) { - DapGuiApp* app = (DapGuiApp*)context; - view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventConfig); -} - -void dap_scene_main_on_enter(void* context) { - DapGuiApp* app = context; - DapSceneMainState* state = malloc(sizeof(DapSceneMainState)); - dap_main_view_set_left_callback(app->main_view, dap_scene_main_on_left, app); - view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewMainView); - scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)state); -} - -bool dap_scene_main_on_event(void* context, SceneManagerEvent event) { - DapGuiApp* app = context; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == DapAppCustomEventConfig) { - scene_manager_next_scene(app->scene_manager, DapSceneConfig); - return true; - } - } else if(event.type == SceneManagerEventTypeTick) { - return process_dap_state(app); - } - - return false; -} - -void dap_scene_main_on_exit(void* context) { - DapGuiApp* app = context; - DapSceneMainState* state = - (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); - scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)NULL); - FURI_SW_MEMBARRIER(); - free(state); - notification_message(app->notifications, &sequence_blink_stop); -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/views/dap_main_view.c b/applications/external/dap_link/gui/views/dap_main_view.c deleted file mode 100644 index f54c5e3d5..000000000 --- a/applications/external/dap_link/gui/views/dap_main_view.c +++ /dev/null @@ -1,189 +0,0 @@ -#include "dap_main_view.h" -#include "dap_link_icons.h" -#include - -// extern const Icon I_ArrowDownEmpty_12x18; -// extern const Icon I_ArrowDownFilled_12x18; -// extern const Icon I_ArrowUpEmpty_12x18; -// extern const Icon I_ArrowUpFilled_12x18; - -struct DapMainView { - View* view; - DapMainViewButtonCallback cb_left; - void* cb_context; -}; - -typedef struct { - DapMainViewMode mode; - DapMainViewVersion version; - bool usb_connected; - uint32_t baudrate; - bool dap_active; - bool tx_active; - bool rx_active; -} DapMainViewModel; - -static void dap_main_view_draw_callback(Canvas* canvas, void* _model) { - DapMainViewModel* model = _model; - UNUSED(model); - canvas_clear(canvas); - elements_button_left(canvas, "Config"); - - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, 0, 127, 11); - canvas_set_color(canvas, ColorWhite); - - const char* header_string; - if(model->usb_connected) { - if(model->version == DapMainViewVersionV1) { - header_string = "DAP Link V1 Connected"; - } else if(model->version == DapMainViewVersionV2) { - header_string = "DAP Link V2 Connected"; - } else { - header_string = "DAP Link Connected"; - } - } else { - header_string = "DAP Link"; - } - - canvas_draw_str_aligned(canvas, 64, 9, AlignCenter, AlignBottom, header_string); - - canvas_set_color(canvas, ColorBlack); - if(model->dap_active) { - canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18); - canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpFilled_12x18, IconRotation180); - } else { - canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18); - canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpEmpty_12x18, IconRotation180); - } - - switch(model->mode) { - case DapMainViewModeDisconnected: - canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "----"); - break; - case DapMainViewModeSWD: - canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "SWD"); - break; - case DapMainViewModeJTAG: - canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "JTAG"); - break; - } - - if(model->tx_active) { - canvas_draw_icon(canvas, 87, 16, &I_ArrowUpFilled_12x18); - } else { - canvas_draw_icon(canvas, 87, 16, &I_ArrowUpEmpty_12x18); - } - - if(model->rx_active) { - canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpFilled_12x18, IconRotation180); - } else { - canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpEmpty_12x18, IconRotation180); - } - - canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART"); - - canvas_draw_line(canvas, 44, 52, 123, 52); - if(model->baudrate == 0) { - canvas_draw_str(canvas, 45, 62, "Baud: ????"); - } else { - char baudrate_str[18]; - snprintf(baudrate_str, 18, "Baud: %lu", model->baudrate); - canvas_draw_str(canvas, 45, 62, baudrate_str); - } -} - -static bool dap_main_view_input_callback(InputEvent* event, void* context) { - furi_assert(context); - DapMainView* dap_main_view = context; - bool consumed = false; - - if(event->type == InputTypeShort) { - if(event->key == InputKeyLeft) { - if(dap_main_view->cb_left) { - dap_main_view->cb_left(dap_main_view->cb_context); - } - consumed = true; - } - } - - return consumed; -} - -DapMainView* dap_main_view_alloc() { - DapMainView* dap_main_view = malloc(sizeof(DapMainView)); - - dap_main_view->view = view_alloc(); - view_allocate_model(dap_main_view->view, ViewModelTypeLocking, sizeof(DapMainViewModel)); - view_set_context(dap_main_view->view, dap_main_view); - view_set_draw_callback(dap_main_view->view, dap_main_view_draw_callback); - view_set_input_callback(dap_main_view->view, dap_main_view_input_callback); - return dap_main_view; -} - -void dap_main_view_free(DapMainView* dap_main_view) { - view_free(dap_main_view->view); - free(dap_main_view); -} - -View* dap_main_view_get_view(DapMainView* dap_main_view) { - return dap_main_view->view; -} - -void dap_main_view_set_left_callback( - DapMainView* dap_main_view, - DapMainViewButtonCallback callback, - void* context) { - with_view_model( - dap_main_view->view, - DapMainViewModel * model, - { - UNUSED(model); - dap_main_view->cb_left = callback; - dap_main_view->cb_context = context; - }, - true); -} - -void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->mode = mode; }, false); -} - -void dap_main_view_set_dap(DapMainView* dap_main_view, bool active) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->dap_active = active; }, false); -} - -void dap_main_view_set_tx(DapMainView* dap_main_view, bool active) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->tx_active = active; }, false); -} - -void dap_main_view_set_rx(DapMainView* dap_main_view, bool active) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->rx_active = active; }, false); -} - -void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->baudrate = baudrate; }, false); -} - -void dap_main_view_update(DapMainView* dap_main_view) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { UNUSED(model); }, true); -} - -void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version) { - with_view_model( - dap_main_view->view, DapMainViewModel * model, { model->version = version; }, false); -} - -void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected) { - with_view_model( - dap_main_view->view, - DapMainViewModel * model, - { model->usb_connected = connected; }, - false); -} \ No newline at end of file diff --git a/applications/external/dap_link/gui/views/dap_main_view.h b/applications/external/dap_link/gui/views/dap_main_view.h deleted file mode 100644 index 1fd900452..000000000 --- a/applications/external/dap_link/gui/views/dap_main_view.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include - -typedef struct DapMainView DapMainView; - -typedef void (*DapMainViewButtonCallback)(void* context); - -typedef enum { - DapMainViewVersionUnknown, - DapMainViewVersionV1, - DapMainViewVersionV2, -} DapMainViewVersion; - -typedef enum { - DapMainViewModeDisconnected, - DapMainViewModeSWD, - DapMainViewModeJTAG, -} DapMainViewMode; - -DapMainView* dap_main_view_alloc(); - -void dap_main_view_free(DapMainView* dap_main_view); - -View* dap_main_view_get_view(DapMainView* dap_main_view); - -void dap_main_view_set_left_callback( - DapMainView* dap_main_view, - DapMainViewButtonCallback callback, - void* context); - -void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode); - -void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version); - -void dap_main_view_set_dap(DapMainView* dap_main_view, bool active); - -void dap_main_view_set_tx(DapMainView* dap_main_view, bool active); - -void dap_main_view_set_rx(DapMainView* dap_main_view, bool active); - -void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected); - -void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate); - -void dap_main_view_update(DapMainView* dap_main_view); \ No newline at end of file diff --git a/applications/external/dap_link/icons/ActiveConnection_50x64.png b/applications/external/dap_link/icons/ActiveConnection_50x64.png deleted file mode 100644 index 1d7686dddf8a33b724c7528ed36435514b7518b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3842 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 G5}E*=05`}0 diff --git a/applications/external/dap_link/icons/ArrowUpFilled_12x18.png b/applications/external/dap_link/icons/ArrowUpFilled_12x18.png deleted file mode 100644 index dc481517ebae2d875880d96c3a6863c9ca985690..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^JU}eO!3HGrSK5O(jKx9jP7LeL$-D$|(mh=qLo_Cy zop_M%fC7he^yB~ki^>fomK}Y&?Q6<{^IZXxHgu>k9N2PUPNa~E?*^7bKA+0AH5l)R zS}i4d#BjFRWa(K?7>YJFIonF7y_qz@_V#wWmAe;3-!N4C_Ce~$H&1(?8x60XU0}74 WwRe~lwXYaxC4;A{pUXO@geCw-ib4GV diff --git a/applications/external/dap_link/lib/free-dap b/applications/external/dap_link/lib/free-dap deleted file mode 160000 index e7752beb5..000000000 --- a/applications/external/dap_link/lib/free-dap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e7752beb5e8a69119af67b70b9179cb3c90f3ac5 diff --git a/applications/external/dap_link/usb/dap_v2_usb.c b/applications/external/dap_link/usb/dap_v2_usb.c deleted file mode 100644 index cba786648..000000000 --- a/applications/external/dap_link/usb/dap_v2_usb.c +++ /dev/null @@ -1,977 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "dap_v2_usb.h" - -// #define DAP_USB_LOG - -#define HID_EP_IN 0x80 -#define HID_EP_OUT 0x00 - -#define DAP_HID_EP_SEND 1 -#define DAP_HID_EP_RECV 2 -#define DAP_HID_EP_BULK_RECV 3 -#define DAP_HID_EP_BULK_SEND 4 -#define DAP_CDC_EP_COMM 5 -#define DAP_CDC_EP_SEND 6 -#define DAP_CDC_EP_RECV 7 - -#define DAP_HID_EP_IN (HID_EP_IN | DAP_HID_EP_SEND) -#define DAP_HID_EP_OUT (HID_EP_OUT | DAP_HID_EP_RECV) -#define DAP_HID_EP_BULK_IN (HID_EP_IN | DAP_HID_EP_BULK_SEND) -#define DAP_HID_EP_BULK_OUT (HID_EP_OUT | DAP_HID_EP_BULK_RECV) - -#define DAP_HID_EP_SIZE 64 -#define DAP_CDC_COMM_EP_SIZE 8 -#define DAP_CDC_EP_SIZE 64 - -#define DAP_BULK_INTERVAL 0 -#define DAP_HID_INTERVAL 1 -#define DAP_CDC_INTERVAL 0 -#define DAP_CDC_COMM_INTERVAL 1 - -#define DAP_HID_VID 0x0483 -#define DAP_HID_PID 0x5740 - -#define DAP_USB_EP0_SIZE 8 - -#define EP_CFG_DECONFIGURE 0 -#define EP_CFG_CONFIGURE 1 - -enum { - USB_INTF_HID, - USB_INTF_BULK, - USB_INTF_CDC_COMM, - USB_INTF_CDC_DATA, - USB_INTF_COUNT, -}; - -enum { - USB_STR_ZERO, - USB_STR_MANUFACTURER, - USB_STR_PRODUCT, - USB_STR_SERIAL_NUMBER, - USB_STR_CMSIS_DAP_V1, - USB_STR_CMSIS_DAP_V2, - USB_STR_COM_PORT, - USB_STR_COUNT, -}; - -// static const char* usb_str[] = { -// [USB_STR_MANUFACTURER] = "Flipper Devices Inc.", -// [USB_STR_PRODUCT] = "Combined VCP and CMSIS-DAP Adapter", -// [USB_STR_COM_PORT] = "Virtual COM-Port", -// [USB_STR_CMSIS_DAP_V1] = "CMSIS-DAP v1 Adapter", -// [USB_STR_CMSIS_DAP_V2] = "CMSIS-DAP v2 Adapter", -// [USB_STR_SERIAL_NUMBER] = "01234567890ABCDEF", -// }; - -static const struct usb_string_descriptor dev_manuf_descr = - USB_STRING_DESC("Flipper Devices Inc."); - -static const struct usb_string_descriptor dev_prod_descr = - USB_STRING_DESC("Combined VCP and CMSIS-DAP Adapter"); - -static struct usb_string_descriptor* dev_serial_descr = NULL; - -static const struct usb_string_descriptor dev_dap_v1_descr = - USB_STRING_DESC("CMSIS-DAP v1 Adapter"); - -static const struct usb_string_descriptor dev_dap_v2_descr = - USB_STRING_DESC("CMSIS-DAP v2 Adapter"); - -static const struct usb_string_descriptor dev_com_descr = USB_STRING_DESC("Virtual COM-Port"); - -struct HidConfigDescriptor { - struct usb_config_descriptor configuration; - - // CMSIS-DAP v1 - struct usb_interface_descriptor hid_interface; - struct usb_hid_descriptor hid; - struct usb_endpoint_descriptor hid_ep_in; - struct usb_endpoint_descriptor hid_ep_out; - - // CMSIS-DAP v2 - struct usb_interface_descriptor bulk_interface; - struct usb_endpoint_descriptor bulk_ep_out; - struct usb_endpoint_descriptor bulk_ep_in; - - // CDC - struct usb_iad_descriptor iad; - struct usb_interface_descriptor interface_comm; - struct usb_cdc_header_desc cdc_header; - struct usb_cdc_call_mgmt_desc cdc_acm; - struct usb_cdc_acm_desc cdc_call_mgmt; - struct usb_cdc_union_desc cdc_union; - struct usb_endpoint_descriptor ep_comm; - struct usb_interface_descriptor interface_data; - struct usb_endpoint_descriptor ep_in; - struct usb_endpoint_descriptor ep_out; - -} __attribute__((packed)); - -static const struct usb_device_descriptor hid_device_desc = { - .bLength = sizeof(struct usb_device_descriptor), - .bDescriptorType = USB_DTYPE_DEVICE, - .bcdUSB = VERSION_BCD(2, 1, 0), - .bDeviceClass = USB_CLASS_MISC, - .bDeviceSubClass = USB_SUBCLASS_IAD, - .bDeviceProtocol = USB_PROTO_IAD, - .bMaxPacketSize0 = DAP_USB_EP0_SIZE, - .idVendor = DAP_HID_VID, - .idProduct = DAP_HID_PID, - .bcdDevice = VERSION_BCD(1, 0, 0), - .iManufacturer = USB_STR_MANUFACTURER, - .iProduct = USB_STR_PRODUCT, - .iSerialNumber = USB_STR_SERIAL_NUMBER, - .bNumConfigurations = 1, -}; - -static const uint8_t hid_report_desc[] = { - 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) - 0x09, 0x00, // Usage (Undefined) - 0xa1, 0x01, // Collection (Application) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xff, 0x00, // Logical Maximum (255) - 0x75, 0x08, // Report Size (8) - 0x95, 0x40, // Report Count (64) - 0x09, 0x00, // Usage (Undefined) - 0x81, 0x82, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x75, 0x08, // Report Size (8) - 0x95, 0x40, // Report Count (64) - 0x09, 0x00, // Usage (Undefined) - 0x91, 0x82, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile) - 0xc0, // End Collection -}; - -static const struct HidConfigDescriptor hid_cfg_desc = { - .configuration = - { - .bLength = sizeof(struct usb_config_descriptor), - .bDescriptorType = USB_DTYPE_CONFIGURATION, - .wTotalLength = sizeof(struct HidConfigDescriptor), - .bNumInterfaces = USB_INTF_COUNT, - .bConfigurationValue = 1, - .iConfiguration = NO_DESCRIPTOR, - .bmAttributes = USB_CFG_ATTR_RESERVED, - .bMaxPower = USB_CFG_POWER_MA(500), - }, - - // CMSIS-DAP v1 - .hid_interface = - { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = USB_INTF_HID, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT, - .bInterfaceProtocol = USB_HID_PROTO_NONBOOT, - .iInterface = USB_STR_CMSIS_DAP_V1, - }, - - .hid = - { - .bLength = sizeof(struct usb_hid_descriptor), - .bDescriptorType = USB_DTYPE_HID, - .bcdHID = VERSION_BCD(1, 1, 1), - .bCountryCode = USB_HID_COUNTRY_NONE, - .bNumDescriptors = 1, - .bDescriptorType0 = USB_DTYPE_HID_REPORT, - .wDescriptorLength0 = sizeof(hid_report_desc), - }, - - .hid_ep_in = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = DAP_HID_EP_IN, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = DAP_HID_EP_SIZE, - .bInterval = DAP_HID_INTERVAL, - }, - - .hid_ep_out = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = DAP_HID_EP_OUT, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = DAP_HID_EP_SIZE, - .bInterval = DAP_HID_INTERVAL, - }, - - // CMSIS-DAP v2 - .bulk_interface = - { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = USB_INTF_BULK, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = USB_STR_CMSIS_DAP_V2, - }, - - .bulk_ep_out = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = DAP_HID_EP_BULK_OUT, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = DAP_HID_EP_SIZE, - .bInterval = DAP_BULK_INTERVAL, - }, - - .bulk_ep_in = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = DAP_HID_EP_BULK_IN, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = DAP_HID_EP_SIZE, - .bInterval = DAP_BULK_INTERVAL, - }, - - // CDC - .iad = - { - .bLength = sizeof(struct usb_iad_descriptor), - .bDescriptorType = USB_DTYPE_INTERFASEASSOC, - .bFirstInterface = USB_INTF_CDC_COMM, - .bInterfaceCount = 2, - .bFunctionClass = USB_CLASS_CDC, - .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, - .bFunctionProtocol = USB_PROTO_NONE, - .iFunction = USB_STR_COM_PORT, - }, - .interface_comm = - { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = USB_INTF_CDC_COMM, - .bAlternateSetting = 0, - .bNumEndpoints = 1, - .bInterfaceClass = USB_CLASS_CDC, - .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, - .bInterfaceProtocol = USB_PROTO_NONE, - .iInterface = 0, - }, - - .cdc_header = - { - .bFunctionLength = sizeof(struct usb_cdc_header_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_HEADER, - .bcdCDC = VERSION_BCD(1, 1, 0), - }, - - .cdc_acm = - { - .bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT, - // .bmCapabilities = USB_CDC_CAP_LINE | USB_CDC_CAP_BRK, - .bmCapabilities = 0, - }, - - .cdc_call_mgmt = - { - .bFunctionLength = sizeof(struct usb_cdc_acm_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_ACM, - .bmCapabilities = USB_CDC_CALL_MGMT_CAP_DATA_INTF, - // .bDataInterface = USB_INTF_CDC_DATA, - }, - - .cdc_union = - { - .bFunctionLength = sizeof(struct usb_cdc_union_desc), - .bDescriptorType = USB_DTYPE_CS_INTERFACE, - .bDescriptorSubType = USB_DTYPE_CDC_UNION, - .bMasterInterface0 = USB_INTF_CDC_COMM, - .bSlaveInterface0 = USB_INTF_CDC_DATA, - }, - - .ep_comm = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_COMM, - .bmAttributes = USB_EPTYPE_INTERRUPT, - .wMaxPacketSize = DAP_CDC_COMM_EP_SIZE, - .bInterval = DAP_CDC_COMM_INTERVAL, - }, - - .interface_data = - { - .bLength = sizeof(struct usb_interface_descriptor), - .bDescriptorType = USB_DTYPE_INTERFACE, - .bInterfaceNumber = USB_INTF_CDC_DATA, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_CDC_DATA, - .bInterfaceSubClass = USB_SUBCLASS_NONE, - .bInterfaceProtocol = USB_PROTO_NONE, - .iInterface = NO_DESCRIPTOR, - }, - - .ep_in = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_SEND, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = DAP_CDC_EP_SIZE, - .bInterval = DAP_CDC_INTERVAL, - }, - - .ep_out = - { - .bLength = sizeof(struct usb_endpoint_descriptor), - .bDescriptorType = USB_DTYPE_ENDPOINT, - .bEndpointAddress = HID_EP_OUT | DAP_CDC_EP_RECV, - .bmAttributes = USB_EPTYPE_BULK, - .wMaxPacketSize = DAP_CDC_EP_SIZE, - .bInterval = DAP_CDC_INTERVAL, - }, -}; - -// WinUSB -#include "usb_winusb.h" - -typedef struct USB_PACK { - usb_binary_object_store_descriptor_t bos; - usb_winusb_capability_descriptor_t winusb; -} usb_bos_hierarchy_t; - -typedef struct USB_PACK { - usb_winusb_subset_header_function_t header; - usb_winusb_feature_compatble_id_t comp_id; - usb_winusb_feature_reg_property_guids_t property; -} usb_msos_descriptor_subset_t; - -typedef struct USB_PACK { - usb_winusb_set_header_descriptor_t header; - usb_msos_descriptor_subset_t subset; -} usb_msos_descriptor_set_t; - -#define USB_DTYPE_BINARY_OBJECT_STORE 15 -#define USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR 16 -#define USB_DC_TYPE_PLATFORM 5 - -const usb_bos_hierarchy_t usb_bos_hierarchy = { - .bos = - { - .bLength = sizeof(usb_binary_object_store_descriptor_t), - .bDescriptorType = USB_DTYPE_BINARY_OBJECT_STORE, - .wTotalLength = sizeof(usb_bos_hierarchy_t), - .bNumDeviceCaps = 1, - }, - .winusb = - { - .bLength = sizeof(usb_winusb_capability_descriptor_t), - .bDescriptorType = USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR, - .bDevCapabilityType = USB_DC_TYPE_PLATFORM, - .bReserved = 0, - .PlatformCapabilityUUID = USB_WINUSB_PLATFORM_CAPABILITY_ID, - .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, - .wMSOSDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), - .bMS_VendorCode = USB_WINUSB_VENDOR_CODE, - .bAltEnumCode = 0, - }, -}; - -const usb_msos_descriptor_set_t usb_msos_descriptor_set = { - .header = - { - .wLength = sizeof(usb_winusb_set_header_descriptor_t), - .wDescriptorType = USB_WINUSB_SET_HEADER_DESCRIPTOR, - .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, - .wDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), - }, - - .subset = - { - .header = - { - .wLength = sizeof(usb_winusb_subset_header_function_t), - .wDescriptorType = USB_WINUSB_SUBSET_HEADER_FUNCTION, - .bFirstInterface = USB_INTF_BULK, - .bReserved = 0, - .wSubsetLength = sizeof(usb_msos_descriptor_subset_t), - }, - - .comp_id = - { - .wLength = sizeof(usb_winusb_feature_compatble_id_t), - .wDescriptorType = USB_WINUSB_FEATURE_COMPATBLE_ID, - .CompatibleID = "WINUSB\0\0", - .SubCompatibleID = {0}, - }, - - .property = - { - .wLength = sizeof(usb_winusb_feature_reg_property_guids_t), - .wDescriptorType = USB_WINUSB_FEATURE_REG_PROPERTY, - .wPropertyDataType = USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ, - .wPropertyNameLength = - sizeof(usb_msos_descriptor_set.subset.property.PropertyName), - .PropertyName = {'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 'I', 0, - 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, - 'e', 0, 'G', 0, 'U', 0, 'I', 0, 'D', 0, 's', 0, 0, 0}, - .wPropertyDataLength = - sizeof(usb_msos_descriptor_set.subset.property.PropertyData), - .PropertyData = {'{', 0, 'C', 0, 'D', 0, 'B', 0, '3', 0, 'B', 0, '5', 0, - 'A', 0, 'D', 0, '-', 0, '2', 0, '9', 0, '3', 0, 'B', 0, - '-', 0, '4', 0, '6', 0, '6', 0, '3', 0, '-', 0, 'A', 0, - 'A', 0, '3', 0, '6', 0, '-', 0, '1', 0, 'A', 0, 'A', 0, - 'E', 0, '4', 0, '6', 0, '4', 0, '6', 0, '3', 0, '7', 0, - '7', 0, '6', 0, '}', 0, 0, 0, 0, 0}, - }, - }, -}; - -typedef struct { - FuriSemaphore* semaphore_v1; - FuriSemaphore* semaphore_v2; - FuriSemaphore* semaphore_cdc; - bool connected; - usbd_device* usb_dev; - DapStateCallback state_callback; - DapRxCallback rx_callback_v1; - DapRxCallback rx_callback_v2; - DapRxCallback rx_callback_cdc; - DapCDCControlLineCallback control_line_callback_cdc; - DapCDCConfigCallback config_callback_cdc; - void* context; - void* context_cdc; -} DAPState; - -static DAPState dap_state = { - .semaphore_v1 = NULL, - .semaphore_v2 = NULL, - .semaphore_cdc = NULL, - .connected = false, - .usb_dev = NULL, - .state_callback = NULL, - .rx_callback_v1 = NULL, - .rx_callback_v2 = NULL, - .rx_callback_cdc = NULL, - .control_line_callback_cdc = NULL, - .config_callback_cdc = NULL, - .context = NULL, - .context_cdc = NULL, -}; - -static struct usb_cdc_line_coding cdc_config = {0}; -static uint8_t cdc_ctrl_line_state = 0; - -#ifdef DAP_USB_LOG -void furi_console_log_printf(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); - -void furi_console_log_printf(const char* format, ...) { - char buffer[256]; - va_list args; - va_start(args, format); - vsnprintf(buffer, sizeof(buffer), format, args); - va_end(args); - furi_hal_console_puts(buffer); - furi_hal_console_puts("\r\n"); - UNUSED(format); -} -#else -#define furi_console_log_printf(...) -#endif - -int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size) { - if((dap_state.semaphore_v1 == NULL) || (dap_state.connected == false)) return 0; - - furi_check(furi_semaphore_acquire(dap_state.semaphore_v1, FuriWaitForever) == FuriStatusOk); - - if(dap_state.connected) { - int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_IN, buffer, size); - furi_console_log_printf("v1 tx %ld", len); - return len; - } else { - return 0; - } -} - -int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size) { - if((dap_state.semaphore_v2 == NULL) || (dap_state.connected == false)) return 0; - - furi_check(furi_semaphore_acquire(dap_state.semaphore_v2, FuriWaitForever) == FuriStatusOk); - - if(dap_state.connected) { - int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_BULK_IN, buffer, size); - furi_console_log_printf("v2 tx %ld", len); - return len; - } else { - return 0; - } -} - -int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size) { - if((dap_state.semaphore_cdc == NULL) || (dap_state.connected == false)) return 0; - - furi_check(furi_semaphore_acquire(dap_state.semaphore_cdc, FuriWaitForever) == FuriStatusOk); - - if(dap_state.connected) { - int32_t len = usbd_ep_write(dap_state.usb_dev, HID_EP_IN | DAP_CDC_EP_SEND, buffer, size); - furi_console_log_printf("cdc tx %ld", len); - return len; - } else { - return 0; - } -} - -void dap_v1_usb_set_rx_callback(DapRxCallback callback) { - dap_state.rx_callback_v1 = callback; -} - -void dap_v2_usb_set_rx_callback(DapRxCallback callback) { - dap_state.rx_callback_v2 = callback; -} - -void dap_cdc_usb_set_rx_callback(DapRxCallback callback) { - dap_state.rx_callback_cdc = callback; -} - -void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback) { - dap_state.control_line_callback_cdc = callback; -} - -void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback) { - dap_state.config_callback_cdc = callback; -} - -void dap_cdc_usb_set_context(void* context) { - dap_state.context_cdc = context; -} - -void dap_common_usb_set_context(void* context) { - dap_state.context = context; -} - -void dap_common_usb_set_state_callback(DapStateCallback callback) { - dap_state.state_callback = callback; -} - -static void* dap_usb_alloc_string_descr(const char* str) { - furi_assert(str); - - size_t len = strlen(str); - size_t wlen = (len + 1) * sizeof(uint16_t); - struct usb_string_descriptor* dev_str_desc = malloc(wlen); - dev_str_desc->bLength = wlen; - dev_str_desc->bDescriptorType = USB_DTYPE_STRING; - for(size_t i = 0; i < len; i++) { - dev_str_desc->wString[i] = str[i]; - } - - return dev_str_desc; -} - -void dap_common_usb_alloc_name(const char* name) { - dev_serial_descr = dap_usb_alloc_string_descr(name); -} - -void dap_common_usb_free_name() { - free(dev_serial_descr); -} - -static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); -static void hid_deinit(usbd_device* dev); -static void hid_on_wakeup(usbd_device* dev); -static void hid_on_suspend(usbd_device* dev); - -static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg); -static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); - -FuriHalUsbInterface dap_v2_usb_hid = { - .init = hid_init, - .deinit = hid_deinit, - .wakeup = hid_on_wakeup, - .suspend = hid_on_suspend, - .dev_descr = (struct usb_device_descriptor*)&hid_device_desc, - .cfg_descr = (void*)&hid_cfg_desc, -}; - -static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { - UNUSED(intf); - UNUSED(ctx); - - dap_v2_usb_hid.str_manuf_descr = (void*)&dev_manuf_descr; - dap_v2_usb_hid.str_prod_descr = (void*)&dev_prod_descr; - dap_v2_usb_hid.str_serial_descr = (void*)dev_serial_descr; - - dap_state.usb_dev = dev; - if(dap_state.semaphore_v1 == NULL) dap_state.semaphore_v1 = furi_semaphore_alloc(1, 1); - if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1); - if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1); - - usbd_reg_config(dev, hid_ep_config); - usbd_reg_control(dev, hid_control); - - usbd_connect(dev, true); -} - -static void hid_deinit(usbd_device* dev) { - dap_state.usb_dev = NULL; - - furi_semaphore_free(dap_state.semaphore_v1); - furi_semaphore_free(dap_state.semaphore_v2); - furi_semaphore_free(dap_state.semaphore_cdc); - dap_state.semaphore_v1 = NULL; - dap_state.semaphore_v2 = NULL; - dap_state.semaphore_cdc = NULL; - - usbd_reg_config(dev, NULL); - usbd_reg_control(dev, NULL); -} - -static void hid_on_wakeup(usbd_device* dev) { - UNUSED(dev); - if(!dap_state.connected) { - dap_state.connected = true; - if(dap_state.state_callback != NULL) { - dap_state.state_callback(dap_state.connected, dap_state.context); - } - } -} - -static void hid_on_suspend(usbd_device* dev) { - UNUSED(dev); - if(dap_state.connected) { - dap_state.connected = false; - if(dap_state.state_callback != NULL) { - dap_state.state_callback(dap_state.connected, dap_state.context); - } - } -} - -size_t dap_v1_usb_rx(uint8_t* buffer, size_t size) { - size_t len = 0; - - if(dap_state.connected) { - len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_OUT, buffer, size); - } - - return len; -} - -size_t dap_v2_usb_rx(uint8_t* buffer, size_t size) { - size_t len = 0; - - if(dap_state.connected) { - len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_BULK_OUT, buffer, size); - } - - return len; -} - -size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size) { - size_t len = 0; - - if(dap_state.connected) { - len = usbd_ep_read(dap_state.usb_dev, HID_EP_OUT | DAP_CDC_EP_RECV, buffer, size); - } - - return len; -} - -static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(ep); - - switch(event) { - case usbd_evt_eptx: - furi_semaphore_release(dap_state.semaphore_v1); - furi_console_log_printf("hid tx complete"); - break; - case usbd_evt_eprx: - if(dap_state.rx_callback_v1 != NULL) { - dap_state.rx_callback_v1(dap_state.context); - } - break; - default: - furi_console_log_printf("hid %d, %d", event, ep); - break; - } -} - -static void hid_txrx_ep_bulk_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(ep); - - switch(event) { - case usbd_evt_eptx: - furi_semaphore_release(dap_state.semaphore_v2); - furi_console_log_printf("bulk tx complete"); - break; - case usbd_evt_eprx: - if(dap_state.rx_callback_v2 != NULL) { - dap_state.rx_callback_v2(dap_state.context); - } - break; - default: - furi_console_log_printf("bulk %d, %d", event, ep); - break; - } -} - -static void cdc_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { - UNUSED(dev); - UNUSED(ep); - - switch(event) { - case usbd_evt_eptx: - furi_semaphore_release(dap_state.semaphore_cdc); - furi_console_log_printf("cdc tx complete"); - break; - case usbd_evt_eprx: - if(dap_state.rx_callback_cdc != NULL) { - dap_state.rx_callback_cdc(dap_state.context_cdc); - } - break; - default: - furi_console_log_printf("cdc %d, %d", event, ep); - break; - } -} - -static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { - switch(cfg) { - case EP_CFG_DECONFIGURE: - usbd_ep_deconfig(dev, DAP_HID_EP_OUT); - usbd_ep_deconfig(dev, DAP_HID_EP_IN); - usbd_ep_deconfig(dev, DAP_HID_EP_BULK_IN); - usbd_ep_deconfig(dev, DAP_HID_EP_BULK_OUT); - usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_COMM); - usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_SEND); - usbd_ep_deconfig(dev, HID_EP_OUT | DAP_CDC_EP_RECV); - usbd_reg_endpoint(dev, DAP_HID_EP_OUT, NULL); - usbd_reg_endpoint(dev, DAP_HID_EP_IN, NULL); - usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, NULL); - usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, NULL); - usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, 0); - usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, 0); - return usbd_ack; - case EP_CFG_CONFIGURE: - usbd_ep_config(dev, DAP_HID_EP_IN, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); - usbd_ep_config(dev, DAP_HID_EP_OUT, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); - usbd_ep_config(dev, DAP_HID_EP_BULK_OUT, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); - usbd_ep_config(dev, DAP_HID_EP_BULK_IN, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); - usbd_ep_config(dev, HID_EP_OUT | DAP_CDC_EP_RECV, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); - usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_SEND, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); - usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_COMM, USB_EPTYPE_INTERRUPT, DAP_CDC_EP_SIZE); - usbd_reg_endpoint(dev, DAP_HID_EP_IN, hid_txrx_ep_callback); - usbd_reg_endpoint(dev, DAP_HID_EP_OUT, hid_txrx_ep_callback); - usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, hid_txrx_ep_bulk_callback); - usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, hid_txrx_ep_bulk_callback); - usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, cdc_txrx_ep_callback); - usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, cdc_txrx_ep_callback); - // usbd_ep_write(dev, DAP_HID_EP_IN, NULL, 0); - // usbd_ep_write(dev, DAP_HID_EP_BULK_IN, NULL, 0); - // usbd_ep_write(dev, HID_EP_IN | DAP_CDC_EP_SEND, NULL, 0); - return usbd_ack; - default: - return usbd_fail; - } -} - -#ifdef DAP_USB_LOG -static void dump_request_type(uint8_t type) { - switch(type & USB_REQ_DIRECTION) { - case USB_REQ_HOSTTODEV: - furi_hal_console_puts("host to dev, "); - break; - case USB_REQ_DEVTOHOST: - furi_hal_console_puts("dev to host, "); - break; - } - - switch(type & USB_REQ_TYPE) { - case USB_REQ_STANDARD: - furi_hal_console_puts("standard, "); - break; - case USB_REQ_CLASS: - furi_hal_console_puts("class, "); - break; - case USB_REQ_VENDOR: - furi_hal_console_puts("vendor, "); - break; - } - - switch(type & USB_REQ_RECIPIENT) { - case USB_REQ_DEVICE: - furi_hal_console_puts("device"); - break; - case USB_REQ_INTERFACE: - furi_hal_console_puts("interface"); - break; - case USB_REQ_ENDPOINT: - furi_hal_console_puts("endpoint"); - break; - case USB_REQ_OTHER: - furi_hal_console_puts("other"); - break; - } - - furi_hal_console_puts("\r\n"); -} -#else -#define dump_request_type(...) -#endif - -static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { - UNUSED(callback); - - dump_request_type(req->bmRequestType); - furi_console_log_printf( - "control: RT %02x, R %02x, V %04x, I %04x, L %04x", - req->bmRequestType, - req->bRequest, - req->wValue, - req->wIndex, - req->wLength); - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE | USB_REQ_DIRECTION) & req->bmRequestType) == - (USB_REQ_STANDARD | USB_REQ_VENDOR | USB_REQ_DEVTOHOST)) { - // vendor request, device to host - furi_console_log_printf("vendor request"); - if(USB_WINUSB_VENDOR_CODE == req->bRequest) { - // WINUSB request - if(USB_WINUSB_DESCRIPTOR_INDEX == req->wIndex) { - furi_console_log_printf("WINUSB descriptor"); - uint16_t length = req->wLength; - if(length > sizeof(usb_msos_descriptor_set_t)) { - length = sizeof(usb_msos_descriptor_set_t); - } - - dev->status.data_ptr = (uint8_t*)&usb_msos_descriptor_set; - dev->status.data_count = length; - return usbd_ack; - } - } - } - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_STANDARD | USB_REQ_DEVICE)) { - // device request - if(req->bRequest == USB_STD_GET_DESCRIPTOR) { - const uint8_t dtype = req->wValue >> 8; - const uint8_t dnumber = req->wValue & 0xFF; - // get string descriptor - if(USB_DTYPE_STRING == dtype) { - if(dnumber == USB_STR_CMSIS_DAP_V1) { - furi_console_log_printf("str CMSIS-DAP v1"); - dev->status.data_ptr = (uint8_t*)&dev_dap_v1_descr; - dev->status.data_count = dev_dap_v1_descr.bLength; - return usbd_ack; - } else if(dnumber == USB_STR_CMSIS_DAP_V2) { - furi_console_log_printf("str CMSIS-DAP v2"); - dev->status.data_ptr = (uint8_t*)&dev_dap_v2_descr; - dev->status.data_count = dev_dap_v2_descr.bLength; - return usbd_ack; - } else if(dnumber == USB_STR_COM_PORT) { - furi_console_log_printf("str COM port"); - dev->status.data_ptr = (uint8_t*)&dev_com_descr; - dev->status.data_count = dev_com_descr.bLength; - return usbd_ack; - } - } else if(USB_DTYPE_BINARY_OBJECT_STORE == dtype) { - furi_console_log_printf("BOS descriptor"); - uint16_t length = req->wLength; - if(length > sizeof(usb_bos_hierarchy_t)) { - length = sizeof(usb_bos_hierarchy_t); - } - dev->status.data_ptr = (uint8_t*)&usb_bos_hierarchy; - dev->status.data_count = length; - return usbd_ack; - } - } - } - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_INTERFACE | USB_REQ_CLASS) && - req->wIndex == 0) { - // class request - switch(req->bRequest) { - // get hid descriptor - case USB_HID_GETREPORT: - furi_console_log_printf("get report"); - return usbd_fail; - // set hid idle - case USB_HID_SETIDLE: - furi_console_log_printf("set idle"); - return usbd_ack; - default: - break; - } - } - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_INTERFACE | USB_REQ_CLASS) && - req->wIndex == 2) { - // class request - switch(req->bRequest) { - // control line state - case USB_CDC_SET_CONTROL_LINE_STATE: - furi_console_log_printf("set control line state"); - cdc_ctrl_line_state = req->wValue; - if(dap_state.control_line_callback_cdc != NULL) { - dap_state.control_line_callback_cdc(cdc_ctrl_line_state, dap_state.context_cdc); - } - return usbd_ack; - // set cdc line coding - case USB_CDC_SET_LINE_CODING: - furi_console_log_printf("set line coding"); - memcpy(&cdc_config, req->data, sizeof(cdc_config)); - if(dap_state.config_callback_cdc != NULL) { - dap_state.config_callback_cdc(&cdc_config, dap_state.context_cdc); - } - return usbd_ack; - // get cdc line coding - case USB_CDC_GET_LINE_CODING: - furi_console_log_printf("get line coding"); - dev->status.data_ptr = &cdc_config; - dev->status.data_count = sizeof(cdc_config); - return usbd_ack; - default: - break; - } - } - - if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == - (USB_REQ_INTERFACE | USB_REQ_STANDARD) && - req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) { - // standard request - switch(req->wValue >> 8) { - // get hid descriptor - case USB_DTYPE_HID: - furi_console_log_printf("get hid descriptor"); - dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.hid); - dev->status.data_count = sizeof(hid_cfg_desc.hid); - return usbd_ack; - // get hid report descriptor - case USB_DTYPE_HID_REPORT: - furi_console_log_printf("get hid report descriptor"); - dev->status.data_ptr = (uint8_t*)hid_report_desc; - dev->status.data_count = sizeof(hid_report_desc); - return usbd_ack; - default: - break; - } - } - - return usbd_fail; -} diff --git a/applications/external/dap_link/usb/dap_v2_usb.h b/applications/external/dap_link/usb/dap_v2_usb.h deleted file mode 100644 index 3f1534ffd..000000000 --- a/applications/external/dap_link/usb/dap_v2_usb.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -#include -#include - -extern FuriHalUsbInterface dap_v2_usb_hid; - -// receive callback type -typedef void (*DapRxCallback)(void* context); - -typedef void (*DapStateCallback)(bool state, void* context); - -/************************************ V1 ***************************************/ - -int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size); - -size_t dap_v1_usb_rx(uint8_t* buffer, size_t size); - -void dap_v1_usb_set_rx_callback(DapRxCallback callback); - -/************************************ V2 ***************************************/ - -int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size); - -size_t dap_v2_usb_rx(uint8_t* buffer, size_t size); - -void dap_v2_usb_set_rx_callback(DapRxCallback callback); - -/************************************ CDC **************************************/ - -typedef void (*DapCDCControlLineCallback)(uint8_t state, void* context); -typedef void (*DapCDCConfigCallback)(struct usb_cdc_line_coding* config, void* context); - -int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size); - -size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size); - -void dap_cdc_usb_set_rx_callback(DapRxCallback callback); - -void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback); - -void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback); - -void dap_cdc_usb_set_context(void* context); - -/*********************************** Common ************************************/ - -void dap_common_usb_set_context(void* context); - -void dap_common_usb_set_state_callback(DapStateCallback callback); - -void dap_common_usb_alloc_name(const char* name); - -void dap_common_usb_free_name(); diff --git a/applications/external/dap_link/usb/usb_winusb.h b/applications/external/dap_link/usb/usb_winusb.h deleted file mode 100644 index 9c3a172dc..000000000 --- a/applications/external/dap_link/usb/usb_winusb.h +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once -#include - -/*- Definitions -------------------------------------------------------------*/ - -#define USB_PACK __attribute__((packed)) - -#define USB_WINUSB_VENDOR_CODE 0x20 - -#define USB_WINUSB_WINDOWS_VERSION 0x06030000 // Windows 8.1 - -#define USB_WINUSB_PLATFORM_CAPABILITY_ID \ - { \ - 0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c, 0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, \ - 0x9f \ - } - -enum // WinUSB Microsoft OS 2.0 descriptor request codes -{ - USB_WINUSB_DESCRIPTOR_INDEX = 0x07, - USB_WINUSB_SET_ALT_ENUMERATION = 0x08, -}; - -enum // wDescriptorType -{ - USB_WINUSB_SET_HEADER_DESCRIPTOR = 0x00, - USB_WINUSB_SUBSET_HEADER_CONFIGURATION = 0x01, - USB_WINUSB_SUBSET_HEADER_FUNCTION = 0x02, - USB_WINUSB_FEATURE_COMPATBLE_ID = 0x03, - USB_WINUSB_FEATURE_REG_PROPERTY = 0x04, - USB_WINUSB_FEATURE_MIN_RESUME_TIME = 0x05, - USB_WINUSB_FEATURE_MODEL_ID = 0x06, - USB_WINUSB_FEATURE_CCGP_DEVICE = 0x07, - USB_WINUSB_FEATURE_VENDOR_REVISION = 0x08, -}; - -enum // wPropertyDataType -{ - USB_WINUSB_PROPERTY_DATA_TYPE_SZ = 1, - USB_WINUSB_PROPERTY_DATA_TYPE_EXPAND_SZ = 2, - USB_WINUSB_PROPERTY_DATA_TYPE_BINARY = 3, - USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_LITTLE_ENDIAN = 4, - USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_BIG_ENDIAN = 5, - USB_WINUSB_PROPERTY_DATA_TYPE_LINK = 6, - USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ = 7, -}; - -/*- Types BOS -------------------------------------------------------------------*/ - -typedef struct USB_PACK { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t wTotalLength; - uint8_t bNumDeviceCaps; -} usb_binary_object_store_descriptor_t; - -/*- Types WinUSB -------------------------------------------------------------------*/ - -typedef struct USB_PACK { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bDevCapabilityType; - uint8_t bReserved; - uint8_t PlatformCapabilityUUID[16]; - uint32_t dwWindowsVersion; - uint16_t wMSOSDescriptorSetTotalLength; - uint8_t bMS_VendorCode; - uint8_t bAltEnumCode; -} usb_winusb_capability_descriptor_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint32_t dwWindowsVersion; - uint16_t wDescriptorSetTotalLength; -} usb_winusb_set_header_descriptor_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t bConfigurationValue; - uint8_t bReserved; - uint16_t wTotalLength; -} usb_winusb_subset_header_configuration_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t bFirstInterface; - uint8_t bReserved; - uint16_t wSubsetLength; -} usb_winusb_subset_header_function_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t CompatibleID[8]; - uint8_t SubCompatibleID[8]; -} usb_winusb_feature_compatble_id_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint16_t wPropertyDataType; - //uint16_t wPropertyNameLength; - //uint8_t PropertyName[...]; - //uint16_t wPropertyDataLength - //uint8_t PropertyData[...]; -} usb_winusb_feature_reg_property_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint16_t wPropertyDataType; - uint16_t wPropertyNameLength; - uint8_t PropertyName[42]; - uint16_t wPropertyDataLength; - uint8_t PropertyData[80]; -} usb_winusb_feature_reg_property_guids_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t bResumeRecoveryTime; - uint8_t bResumeSignalingTime; -} usb_winusb_feature_min_resume_time_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint8_t ModelID[16]; -} usb_winusb_feature_model_id_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; -} usb_winusb_feature_ccgp_device_t; - -typedef struct USB_PACK { - uint16_t wLength; - uint16_t wDescriptorType; - uint16_t VendorRevision; -} usb_winusb_feature_vendor_revision_t; \ No newline at end of file diff --git a/applications/external/doom/.gitignore b/applications/external/doom/.gitignore deleted file mode 100644 index e2a15a10a..000000000 --- a/applications/external/doom/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist/* -.vscode -.clang-format -.editorconfig \ No newline at end of file diff --git a/applications/external/doom/README.md b/applications/external/doom/README.md deleted file mode 100644 index 501f644d1..000000000 --- a/applications/external/doom/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Doom Flipper Zero edition -## Will it run Doom? -As tradition goes, Doom is being ported to almost every possible embedded electronic device. Therefore I did an attempt to come up with something close to Doom and still compatible on the Flipper Zero's hardware. This is not the actual Doom game but a port made from yet another Doom port to the Arduino Nano - https://github.com/daveruiz/doom-nano/. This port is basically a raycasting engine, using Doom sprites. -This version is very basic and might be improved over time. - -## Credits -@xMasterX - Porting to latest firmware using new plugins system, fixing many issues, adding sound -@Svaarich - New logo screen and cool icon -@hedger - uFBT fixes and some bugfixes -@p4nic4ttack - First raw implementation based on doom-nano \ No newline at end of file diff --git a/applications/external/doom/application.fam b/applications/external/doom/application.fam deleted file mode 100644 index 5ff6878ee..000000000 --- a/applications/external/doom/application.fam +++ /dev/null @@ -1,18 +0,0 @@ -App( - appid="doom", - name="DOOM", - apptype=FlipperAppType.EXTERNAL, - entry_point="doom_app", - requires=[ - "gui", - "music_player", - ], - stack_size=4 * 1024, - order=75, - fap_icon="doom_10px.png", - fap_category="Games", - fap_icon_assets="assets", - fap_author="@xMasterX & @Svarich & @hedger (original code by @p4nic4ttack)", - fap_version="1.0", - fap_description="Will it run Doom?", -) diff --git a/applications/external/doom/assets.c b/applications/external/doom/assets.c deleted file mode 100644 index 864588581..000000000 --- a/applications/external/doom/assets.c +++ /dev/null @@ -1,331 +0,0 @@ -#include "assets.h" - -const uint8_t space[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -const uint8_t zero[] = {0x00, 0x60, 0x90, 0x90, 0x90, 0x60}; -const uint8_t one[] = {0x00, 0x20, 0x20, 0x20, 0x20, 0x70}; -const uint8_t two[] = {0x00, 0x60, 0x90, 0x20, 0x40, 0xf0}; -const uint8_t three[] = {0x00, 0x60, 0x90, 0x20, 0x90, 0x60}; -const uint8_t four[] = {0x00, 0x90, 0x90, 0xf0, 0x10, 0x10}; -const uint8_t five[] = {0x00, 0xf0, 0x80, 0xe0, 0x10, 0xe0}; -const uint8_t six[] = {0x00, 0x60, 0x80, 0xe0, 0x90, 0x60}; -const uint8_t seven[] = {0x00, 0xf0, 0x10, 0x10, 0x10, 0x10}; -const uint8_t eight[] = {0x00, 0x60, 0x90, 0x60, 0x90, 0x60}; -const uint8_t nine[] = {0x00, 0x60, 0x90, 0x70, 0x10, 0x60}; -const uint8_t A[] = {0x00, 0x60, 0x90, 0xf0, 0x90, 0x90}; -const uint8_t B[] = {0x00, 0xe0, 0x90, 0xe0, 0x90, 0xe0}; -const uint8_t C[] = {0x00, 0x60, 0x90, 0x80, 0x90, 0x60}; -const uint8_t D[] = {0x00, 0xe0, 0x90, 0x90, 0x90, 0xe0}; -const uint8_t E[] = {0x00, 0xf0, 0x80, 0xe0, 0x80, 0xf0}; -const uint8_t F[] = {0x00, 0xf0, 0x80, 0xe0, 0x80, 0x80}; -const uint8_t G[] = {0x00, 0x60, 0x80, 0x80, 0x90, 0x60}; -const uint8_t H[] = {0x00, 0x90, 0x90, 0xf0, 0x90, 0x90}; -const uint8_t I[] = {0x00, 0x20, 0x20, 0x20, 0x20, 0x20}; -const uint8_t J[] = {0x00, 0x10, 0x10, 0x10, 0x90, 0x60}; -const uint8_t K[] = {0x00, 0x90, 0xa0, 0xc0, 0xa0, 0x90}; -const uint8_t L[] = {0x00, 0x80, 0x80, 0x80, 0x80, 0xf0}; -const uint8_t M[] = {0x00, 0x90, 0xf0, 0x90, 0x90, 0x90}; -const uint8_t N[] = {0x00, 0x90, 0xd0, 0xb0, 0x90, 0x90}; -const uint8_t O[] = {0x00, 0x60, 0x90, 0x90, 0x90, 0x60}; -const uint8_t P[] = {0x00, 0xe0, 0x90, 0xe0, 0x80, 0x80}; -const uint8_t Q[] = {0x00, 0x60, 0x90, 0x90, 0xb0, 0x70}; -const uint8_t R[] = {0x00, 0xe0, 0x90, 0xe0, 0x90, 0x90}; -const uint8_t S[] = {0x00, 0x60, 0x80, 0x60, 0x10, 0xe0}; -const uint8_t T[] = {0x00, 0xe0, 0x40, 0x40, 0x40, 0x40}; -const uint8_t U[] = {0x00, 0x90, 0x90, 0x90, 0x90, 0x60}; -const uint8_t V[] = {0x00, 0x90, 0x90, 0x90, 0x60, 0x60}; -const uint8_t W[] = {0x00, 0x90, 0x90, 0x90, 0xf0, 0x90}; -const uint8_t X[] = {0x00, 0x90, 0x90, 0x60, 0x90, 0x90}; -const uint8_t Y[] = {0x00, 0x90, 0x90, 0x60, 0x60, 0x60}; -const uint8_t Z[] = {0x00, 0xf0, 0x10, 0x60, 0x80, 0xf0}; -const uint8_t dot[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x40}; -const uint8_t comma[] = {0x00, 0x00, 0x00, 0x00, 0x20, 0x40}; -const uint8_t dash[] = {0x00, 0x00, 0x00, 0x60, 0x00, 0x00}; -const uint8_t underscore[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0xf0}; -const uint8_t bracket_open[] = {0x00, 0x20, 0x40, 0x40, 0x40, 0x20}; -const uint8_t bracket_close[] = {0x00, 0x40, 0x20, 0x20, 0x20, 0x40}; -const uint8_t cross_left[] = {0x10, 0x10, 0x70, 0x70, 0x10, 0x10}; -const uint8_t cross_right[] = {0x80, 0x80, 0xe0, 0xe0, 0x80, 0x80}; -const uint8_t pacman_left[] = {0x00, 0x30, 0x50, 0x70, 0x70, 0x00}; -const uint8_t pacman_right[] = {0x00, 0xc0, 0x60, 0xe0, 0xe0, 0xe0}; -const uint8_t box[] = {0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00}; -const uint8_t* char_arr[48] = { - space, - zero, - one, - two, - three, - four, - five, - six, - seven, - eight, - nine, - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - dot, - comma, - dash, - underscore, - bracket_open, - bracket_close, - cross_left, - cross_right, - pacman_left, - pacman_right, - box}; -const uint8_t gradient[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00, 0x22, 0x22, - 0x00, 0x00, 0x8a, 0x8a, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0xaa, 0xaa, - 0x10, 0x10, 0xaa, 0xaa, 0x00, 0x00, 0xaa, 0xaa, 0x01, 0x01, 0xaa, 0xaa, - 0x44, 0x44, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x44, 0x44, 0xaa, 0xaa, - 0x15, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xbb, 0xbb, - 0x55, 0x55, 0xaa, 0xea, 0x55, 0x55, 0xbb, 0xbb, 0x55, 0x55, 0xff, 0xff, - 0x55, 0x55, 0xfb, 0xfb, 0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xbb, 0xbf, - 0x57, 0x57, 0xff, 0xff, 0xdd, 0xdd, 0xff, 0xff, 0x77, 0x75, 0xff, 0xff, - 0xdd, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -//const uint8_t gun[] = {0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xfe, 0x3b, 0xff, 0xff, 0xfd, 0xfb, 0xff, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xfd, 0x15, 0xff, 0xff, 0xfb, 0x2e, 0xff, 0xff, 0xf6, 0x77, 0x7f, 0xff, 0xe6, 0xff, 0xff, 0xff, 0xf2, 0x3d, 0x7f, 0xff, 0xd6, 0x7e, 0x3f, 0xff, 0xf4, 0x5d, 0xdf, 0xff, 0xce, 0xbf, 0xbf, 0xff, 0xdc, 0xff, 0x3f, 0xff, 0xec, 0xff, 0xbf, 0xff, 0x8d, 0xfd, 0xff, 0xff, 0xb6, 0xff, 0xbf, 0xfe, 0x1f, 0x57, 0xdf, 0xf8, 0x0e, 0xff, 0xcf, 0xf4, 0x46, 0x1f, 0x17, 0xf8, 0xa3, 0xfc, 0x03, 0xf8, 0x10, 0x00, 0x11, 0xf8, 0x8a, 0x80, 0x2d, 0xe4, 0x44, 0x00, 0x4d, 0xee, 0xa8, 0x82, 0x9b, 0xcd, 0x50, 0x00, 0x17, 0xec, 0xa0, 0x8a, 0x2f, 0xcc, 0x00, 0x04, 0x67, 0xe8, 0x28, 0x1a, 0xff, 0xe4, 0x70, 0x4d, 0xcf, 0xfc, 0x82, 0xa7, 0xef, 0x90, 0x40, 0x13, 0xdf}; -// const uint8_t gun_mask[] = {0xff, 0xff, 0x8f, 0xff, 0xff, 0xfe, 0x03, 0xff, 0xff, 0xfc, 0x01, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xf8, 0x00, 0xff, 0xff, 0xf8, 0x00, 0xff, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0xe0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x0f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0f}; -const uint8_t gun[] = {0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x01, 0xc4, 0x00, - 0x00, 0x02, 0x04, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, 0xea, 0x00, - 0x00, 0x04, 0xd1, 0x00, 0x00, 0x09, 0x88, 0x80, 0x00, 0x19, 0x00, 0x00, - 0x00, 0x0d, 0xc2, 0x80, 0x00, 0x29, 0x81, 0xc0, 0x00, 0x0b, 0xa2, 0x20, - 0x00, 0x31, 0x40, 0x40, 0x00, 0x23, 0x00, 0xc0, 0x00, 0x13, 0x00, 0x40, - 0x00, 0x72, 0x02, 0x00, 0x00, 0x49, 0x00, 0x40, 0x01, 0xe0, 0xa8, 0x20, - 0x07, 0xf1, 0x00, 0x30, 0x0b, 0xb9, 0xe0, 0xe8, 0x07, 0x5c, 0x03, 0xfc, - 0x07, 0xef, 0xff, 0xee, 0x07, 0x75, 0x7f, 0xd2, 0x1b, 0xbb, 0xff, 0xb2, - 0x11, 0x57, 0x7d, 0x64, 0x32, 0xaf, 0xff, 0xe8, 0x13, 0x5f, 0x75, 0xd0, - 0x33, 0xff, 0xfb, 0x98, 0x17, 0xd7, 0xe5, 0x00, 0x1b, 0x8f, 0xb2, 0x30, - 0x03, 0x7d, 0x58, 0x10, 0x6f, 0xbf, 0xec, 0x20}; -const uint8_t gun_mask[] = {0x00, 0x00, 0x70, 0x00, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x03, 0xfe, 0x00, - 0x00, 0x07, 0xfe, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x07, 0xff, 0x00, - 0x00, 0x0f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0x80, - 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xe0, 0x00, 0x7f, 0xff, 0xf0, - 0x00, 0x7f, 0xff, 0xe0, 0x00, 0x7f, 0xff, 0xe0, 0x00, 0x7f, 0xff, 0xe0, - 0x00, 0xff, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xf0, - 0x0f, 0xff, 0xff, 0xf8, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfe, - 0x1f, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0x3f, 0xff, 0xff, 0xff, - 0x3f, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xf8, - 0x7f, 0xff, 0xff, 0xfc, 0x7f, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xff, 0xf8, - 0x7f, 0xff, 0xff, 0xf8, 0xff, 0xff, 0xff, 0xf0}; - -const uint8_t - imp_inv[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, - 0x02, 0x80, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x01, 0x0f, 0xb3, 0x00, 0x00, 0xd0, 0x4e, 0x00, 0x00, 0x79, 0x8c, - 0x00, 0x00, 0x1c, 0x19, 0x00, 0x01, 0x8a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x40, 0x02, 0x08, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x8e, - 0x30, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x06, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x02, 0x20, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x03, 0xe0, - 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0xa1, 0x80, 0x01, 0x80, 0x13, 0x00, - 0x00, 0xf3, 0x8a, 0x00, 0x00, 0x09, 0x94, 0x00, 0x00, 0x88, 0x38, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x23, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x80, - 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0xe2, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x02, 0x20, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x02, 0x20, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, - 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x00, 0x02, 0x2a, 0x80, 0x00, 0x01, 0x05, 0x00, 0x00, 0x01, - 0xae, 0x20, 0x00, 0x01, 0x24, 0x40, 0x00, 0x02, 0xac, 0x80, 0x00, 0x02, 0x86, - 0x00, 0x00, 0x03, 0x20, 0x20, 0x00, 0x04, 0x30, 0x40, 0x00, 0x0c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x1a, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x98, - 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0a, 0x00, - 0x00, 0x00, 0x08, 0x40, 0x00, 0x00, 0x00, 0x80, 0x00, 0x01, 0xd6, 0x80, 0x00, - 0x02, 0xbf, 0x80, 0x00, 0x06, 0x61, 0xa0, 0x00, 0x0c, 0xe8, 0x80, 0x00, 0x0c, - 0x10, 0x00, 0x00, 0x1a, 0x22, 0x00, 0x00, 0x12, 0x40, 0x00, 0x00, 0x06, 0x0c, - 0x00, 0x00, 0x04, 0x0d, 0x00, 0x00, 0x3a, 0x03, 0x00, 0x00, 0x10, 0x02, 0x00, - 0x00, 0x60, 0x0a, 0x00, 0x00, 0x50, 0x04, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x01, - 0x18, 0x00, 0x00, 0x01, 0x41, 0x40, 0x02, 0x33, 0xb6, 0x80, 0x01, 0x9c, 0x04, - 0x00, 0x08, 0xfa, 0x02, 0x08, 0x05, 0x00, 0x01, 0x0c, 0x27, 0x83, 0xa2, 0x2a, - 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00}; //{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x0f, 0xb3, 0x00, 0x00, 0xd0, 0x4e, 0x00, 0x00, 0x79, 0x8c, 0x00, 0x00, 0x1c, 0x19, 0x00, 0x01, 0x8a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x08, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x8e, 0x30, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -const uint8_t imp_mask_inv[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0xc0, 0x00, - 0x00, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x01, 0x07, 0xf1, 0x80, - 0x00, 0xdf, 0xfe, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x00, 0x7f, 0xff, 0x00, 0x01, 0xff, 0xff, 0x80, - 0x00, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0x80, 0x03, 0xcf, 0xf1, 0xc0, 0x01, 0xc7, 0xf1, 0xc0, - 0x01, 0x87, 0xf1, 0xc0, 0x03, 0x0f, 0xf9, 0x80, 0x03, 0x0f, 0xfb, 0x80, 0x01, 0x8f, 0xff, 0x80, - 0x03, 0x9f, 0x79, 0x00, 0x00, 0x1f, 0x7c, 0x00, 0x00, 0x0f, 0x78, 0x00, 0x00, 0x0f, 0x78, 0x00, - 0x00, 0x07, 0x30, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x07, 0x30, 0x00, - 0x00, 0x03, 0x78, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x07, 0xe0, 0x00, - 0x00, 0x07, 0xc0, 0x00, 0x01, 0x07, 0xe1, 0x00, 0x00, 0x8f, 0xfa, 0x00, 0x00, 0xff, 0xfe, 0x00, - 0x00, 0x3f, 0xfe, 0x00, 0x01, 0x7f, 0xff, 0x80, 0x00, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x80, - 0x03, 0xcf, 0xfb, 0xc0, 0x03, 0x87, 0xf1, 0xc0, 0x03, 0xcf, 0xf3, 0xc0, 0x01, 0xcf, 0xf1, 0x80, - 0x00, 0xcf, 0xf1, 0x00, 0x00, 0x0f, 0xfb, 0x80, 0x00, 0x1e, 0x78, 0x00, 0x00, 0x0e, 0x78, 0x00, - 0x00, 0x1e, 0x78, 0x00, 0x00, 0x0f, 0x70, 0x00, 0x00, 0x0f, 0x78, 0x00, 0x00, 0x07, 0x70, 0x00, - 0x00, 0x07, 0x70, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x03, 0x30, 0x00, 0x00, 0x03, 0x20, 0x00, - 0x00, 0x07, 0x30, 0x00, 0x00, 0x05, 0x70, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1f, 0x00, - 0x00, 0x00, 0x1f, 0x00, 0x00, 0x03, 0x3f, 0x80, 0x00, 0x01, 0x3f, 0x00, 0x00, 0x01, 0xff, 0x30, - 0x00, 0x03, 0xff, 0xc0, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x03, 0xff, 0x80, 0x00, 0x07, 0xff, 0xe0, - 0x00, 0x07, 0xff, 0xc0, 0x00, 0x05, 0xff, 0xe0, 0x00, 0x00, 0xfc, 0xe0, 0x00, 0x01, 0xfc, 0xe0, - 0x00, 0x01, 0xfc, 0x70, 0x00, 0x03, 0xfc, 0x38, 0x00, 0x03, 0xfe, 0x70, 0x00, 0x07, 0xfc, 0x00, - 0x00, 0x07, 0x9e, 0x00, 0x00, 0x0f, 0xbc, 0x00, 0x00, 0x0f, 0x3e, 0x00, 0x00, 0x07, 0x9c, 0x00, - 0x00, 0x03, 0x9c, 0x00, 0x00, 0x03, 0xb8, 0x00, 0x00, 0x03, 0x98, 0x00, 0x00, 0x01, 0x98, 0x00, - 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x1f, 0x00, - 0x00, 0x00, 0x1f, 0x40, 0x00, 0x00, 0x3e, 0x80, 0x00, 0x01, 0xff, 0x80, 0x00, 0x03, 0xff, 0x80, - 0x00, 0x07, 0xff, 0xe0, 0x00, 0x0e, 0xff, 0xc0, 0x00, 0x0c, 0xff, 0x80, 0x00, 0x1f, 0xfe, 0x00, - 0x00, 0x13, 0xfc, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x3f, 0x9f, 0x00, - 0x00, 0x3e, 0x0f, 0x00, 0x00, 0x7c, 0x0f, 0x00, 0x00, 0x78, 0x0f, 0x00, 0x00, 0x78, 0x07, 0x80, - 0x00, 0x78, 0x07, 0x40, 0x00, 0x38, 0x07, 0x80, 0x00, 0x30, 0x07, 0x00, 0x00, 0x30, 0x01, 0x00, - 0x01, 0xf0, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1c, 0x00, - 0x00, 0x01, 0x3e, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x0f, 0xff, 0xe0, 0x01, 0x3f, 0xff, 0xc0, - 0x01, 0xff, 0xff, 0xc0, 0x19, 0xff, 0xff, 0xe8, 0x7f, 0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xfe, - 0x1f, 0xc2, 0x07, 0xe0, 0x1f, 0x00, 0x01, 0xe0, 0x0e, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, -}; //{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x01, 0x07, 0xf1, 0x80, 0x00, 0xdf, 0xfe, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x00, 0x7f, 0xff, 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0x80, 0x03, 0xcf, 0xf1, 0xc0, 0x01, 0xc7, 0xf1, 0xc0, 0x01, 0x87, 0xf1, 0xc0, 0x03, 0x0f, 0xf9, 0x80, 0x03, 0x0f, 0xfb, 0x80, 0x01, 0x8f, 0xff, 0x80, 0x03, 0x9f, 0x79, 0x00, 0x00, 0x1f, 0x7c, 0x00, 0x00, 0x0f, 0x78, 0x00, 0x00, 0x0f, 0x78, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x03, 0x78, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00}; -const uint8_t fireball[] = {0x00, 0x00, 0x01, 0x40, 0x0a, 0xb0, 0x0e, 0xd0, 0x00, 0x68, 0x53, - 0xb4, 0x0f, 0x48, 0x27, 0x78, 0x17, 0xa8, 0x27, 0xf0, 0x21, 0xd6, - 0x02, 0xf8, 0x20, 0x48, 0x06, 0x20, 0x01, 0x00, 0x00, 0x00}; -const uint8_t fireball_mask[] = {0x1f, 0x40, 0x0f, 0xf0, 0x3f, 0xf8, 0x1f, 0xfc, 0x7f, 0xfd, 0x7f, - 0xfc, 0x7f, 0xfd, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, - 0xff, 0xfe, 0x3f, 0xfe, 0x17, 0xf8, 0x07, 0xf4, 0x01, 0xe0}; -const uint8_t item[] = {0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0x7f, 0xfe, 0x77, 0xee, 0x3f, - 0xfc, 0x5f, 0xfa, 0x2f, 0xf6, 0x53, 0xcc, 0x3e, 0x7e, 0x5e, 0x7c, - 0x38, 0x1e, 0x58, 0x1c, 0x3e, 0x7e, 0x5e, 0x7e, 0x2e, 0xfc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfc, - 0x17, 0xfc, 0x22, 0x6c, 0x36, 0x44, 0x3f, 0xfc, 0x1f, 0xfc, 0x2b, - 0xfc, 0x05, 0x54, 0x02, 0xa8, 0x00, 0x00, 0x00, 0x00}; -const uint8_t item_mask[] = {0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, - 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, - 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x3f, 0xfc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfc, - 0x1f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, - 0xfc, 0x07, 0xfc, 0x03, 0xf8, 0x00, 0x00, 0x00, 0x00}; - -//const uint8_t door[] = {0xff, 0xff, 0xff, 0xff,0xb2, 0xbd, 0xcd, 0x5b,0x9a, 0xf4, 0x6d, 0x71,0xff, 0xff, 0xff, 0xff,0x00, 0x00, 0x00, 0x00,0xbf, 0xff, 0xff, 0xfd,0x3f, 0x00, 0xfe, 0xfc,0x3e, 0x00, 0xc6, 0xfc,0xbc, 0xaa, 0xfe, 0xbd,0x39, 0x54, 0xc6, 0xbc,0x32, 0x8e, 0xfe, 0xac,0xb5, 0xfe, 0xc6, 0xad,0x3f, 0xe0, 0xfe, 0xac,0x31, 0xe0, 0xc6, 0xac,0xb3, 0xf4, 0xfe, 0xad,0x3f, 0xe8, 0xc6, 0xac,0x3c, 0xf4, 0xd6, 0xac,0xb8, 0xff, 0xfe, 0xad,0x34, 0xc7, 0xfe, 0xfc,0x38, 0xd6, 0x0e, 0x0c,0xb0, 0xd6, 0x4e, 0x0d,0x3f, 0xd6, 0xaf, 0x5c,0x30, 0x47, 0xff, 0xac,0xb7, 0x57, 0xff, 0xfd,0x3f, 0xc6, 0x0e, 0x0c,0x35, 0x56, 0x40, 0x4c,0xb5, 0x46, 0xaa, 0xad,0x35, 0x56, 0x55, 0x4c,0xff, 0xff, 0xff, 0xff,0xb0, 0x1f, 0xf8, 0x0d,0xd9, 0x30, 0x0c, 0x9b,0xff, 0xe0, 0x07, 0xff}; -const uint8_t door[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0xe1, 0x8c, 0x00, 0x04, - 0x00, 0x7c, 0x03, 0x18, 0x60, 0x08, 0x00, 0x3e, 0x0f, 0xf7, 0xdf, 0x00, 0x1f, 0x00, 0xfe, 0x0f, - 0xbe, 0xf8, 0x3e, 0x00, 0x3f, 0x1f, 0xff, 0xdf, 0x00, 0x1f, 0x81, 0xff, 0x0f, 0xff, 0xf8, 0x7e, - 0x00, 0x3f, 0x8f, 0xff, 0xdf, 0x00, 0xff, 0xf9, 0xff, 0x1f, 0xff, 0xf8, 0xff, 0x80, 0x3f, 0xc7, - 0xff, 0xcc, 0x07, 0xff, 0xfc, 0xff, 0x1f, 0xff, 0xe3, 0xff, 0x80, 0x3f, 0xc7, 0xff, 0xc0, 0x07, - 0xff, 0xfc, 0x7f, 0x0f, 0xfe, 0x03, 0xff, 0xc0, 0x3f, 0xc3, 0xf7, 0xc0, 0x07, 0xdf, 0xf8, 0x3e, - 0x0f, 0xbe, 0x01, 0xff, 0x80, 0x1f, 0x80, 0xe3, 0x80, 0x07, 0x8f, 0xf8, 0x1e, 0x07, 0x1c, 0x01, - 0xff, 0x80, 0x3f, 0xc1, 0xff, 0xc0, 0x0f, 0xff, 0xfc, 0x3f, 0x0f, 0xbe, 0x03, 0xff, 0xc0, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0x80, 0x00, - 0x7f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0xe0, 0x00, 0x1f, 0xf0, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xff, - 0xc0, 0x00, 0x00, 0xe0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x01, - 0xe0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x01, 0xf0, 0x00, 0x0f, - 0xf0, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0x81, 0xf0, 0x00, 0x0f, 0xf0, 0xff, 0x00, - 0x07, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xc1, 0xf0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x0f, 0xff, 0xff, - 0xff, 0xe1, 0xff, 0xe1, 0xf0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, 0xff, - 0xc1, 0xf3, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xe0, 0xff, 0x81, 0xff, 0xc0, - 0x0f, 0xf0, 0xff, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0x01, 0xff, 0xc0, 0x0f, 0xf0, 0xff, - 0x00, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xe0, 0x0f, 0xf0, 0xff, 0x01, 0xff, 0xff, - 0xff, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xe0, 0x0f, 0xf0, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xe1, - 0xff, 0xe1, 0xff, 0xe0, 0x0f, 0xf0, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xc1, 0xff, - 0xf0, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x8f, 0xe0, 0xff, 0x81, 0xff, 0xff, 0x0f, 0xf0, - 0xff, 0x1f, 0xff, 0xff, 0xfe, 0x07, 0xe0, 0x7f, 0x81, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, - 0xff, 0xfc, 0x07, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0x8f, 0xfc, 0x03, - 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0x07, 0xfc, 0x07, 0xe0, 0xff, 0xc1, - 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xfe, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, - 0xf0, 0xff, 0x0f, 0x9c, 0x00, 0x3f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x07, - 0xfc, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x07, 0xfc, 0x00, 0x7f, - 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xfc, 0x00, 0x7f, 0xff, 0xe0, 0xff, - 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xfc, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, - 0x8f, 0xf0, 0xff, 0x1f, 0xfe, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, - 0x1f, 0xfc, 0x00, 0x3f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xfc, 0x00, - 0x3f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xf0, 0x00, 0x1f, 0xff, 0xe0, - 0x7f, 0x81, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xf0, 0x00, 0x1f, 0xff, 0xe0, 0xff, 0x81, 0xff, - 0xff, 0x8f, 0xf0, 0xff, 0x07, 0xe0, 0x00, 0x3f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, - 0xff, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x00, 0x1f, - 0x80, 0x3f, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x00, 0x3f, 0xc0, 0x1f, 0xff, - 0xe1, 0xff, 0xe1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x00, 0x7f, 0xc0, 0x0f, 0xff, 0xe1, 0xff, 0xe1, - 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x00, 0xff, 0xc0, 0x07, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xff, 0x8f, - 0xf0, 0xff, 0x01, 0xff, 0xc0, 0x03, 0x8f, 0xc0, 0xc1, 0x81, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x03, - 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x07, 0xff, 0xc0, 0xff, - 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc1, 0xff, 0x80, 0x00, 0x00, - 0x01, 0xf3, 0x8e, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc0, 0x00, 0x00, 0x01, 0xff, 0x9c, - 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc0, 0xff, 0xfc, 0x01, 0xff, 0xfe, 0x0f, 0xf0, 0xff, - 0x0f, 0xff, 0xc3, 0xff, 0xc1, 0xff, 0xfe, 0x01, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x07, 0xff, 0xc3, - 0xff, 0xc3, 0xff, 0xff, 0x01, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, - 0xff, 0x01, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x01, 0xff, - 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x01, 0xff, 0xff, 0x8f, 0xf0, - 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x01, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x0f, 0xff, - 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x00, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, - 0xff, 0xff, 0x00, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x07, 0xff, 0xe3, 0xff, 0xc3, 0xff, 0xff, 0x00, - 0x3f, 0xfe, 0x0f, 0xf0, 0xff, 0x07, 0xff, 0xf3, 0xff, 0xc1, 0xef, 0xfe, 0x00, 0x3f, 0xfe, 0x0f, - 0xf0, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x82, 0x00, 0x00, 0x1f, 0xff, 0x0f, 0xf0, 0xff, 0x1f, - 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, - 0xc0, 0x00, 0x00, 0x00, 0x07, 0xff, 0x0f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, - 0x00, 0x03, 0x8e, 0x0f, 0xf0, 0xff, 0x1f, 0xc1, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x88, - 0x0f, 0xf0, 0xff, 0x0f, 0x80, 0xff, 0xff, 0xc1, 0xff, 0xfc, 0x00, 0xff, 0xfe, 0x0f, 0xf0, 0xff, - 0x06, 0x00, 0x73, 0xff, 0xc3, 0xff, 0xfe, 0x01, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0x00, 0x03, - 0xff, 0xc3, 0xff, 0xff, 0x83, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x07, 0x0c, 0x73, 0xff, 0xc3, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xfe, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, - 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, - 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, - 0xf0, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xf0, 0xfe, 0x1f, - 0xfe, 0xfb, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfc, 0x0f, 0x9e, 0x73, 0xff, - 0x81, 0xf9, 0xf7, 0xe7, 0x9c, 0xff, 0x03, 0xf0, 0xfc, 0x07, 0xfe, 0xfb, 0xc0, 0x00, 0xf0, 0x00, - 0x6f, 0xbe, 0xfe, 0x03, 0xf0, 0x3c, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfe, - 0x03, 0xc0, 0x1c, 0x0f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0x03, 0x80, 0x1e, - 0x0f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x07, 0x80, 0x3f, 0x0f, 0xff, 0xff, - 0xe0, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0x0f, 0xc0, 0x1f, 0x8f, 0xff, 0xff, 0xe7, 0xff, 0xff, - 0xfe, 0x7f, 0xff, 0xff, 0x1f, 0x80, 0x1f, 0xc7, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xfe, 0x3f, 0x80, 0x07, 0xc3, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xfc, 0x3e, 0x00, - 0x07, 0xc1, 0xfe, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xcf, 0xf7, 0xf8, 0x3e, 0x00, 0x01, 0x00, 0xfc, - 0x7e, 0x7f, 0xff, 0xff, 0xff, 0xe7, 0xe3, 0xf0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, - 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xe0, - 0x00, 0x00, 0x00, 0x00}; \ No newline at end of file diff --git a/applications/external/doom/assets.h b/applications/external/doom/assets.h deleted file mode 100644 index 546d7607d..000000000 --- a/applications/external/doom/assets.h +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once -#include - -#ifndef _sprites_h -#define _sprites_h - -#define bmp_font_width 24 // in bytes -#define bmp_font_height 6 -#define bmp_font_width_pxs 192 -#define bmp_font_height_pxs 48 -#define CHAR_MAP " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.,-_(){}[]#" -#define CHAR_WIDTH 4 -#define CHAR_HEIGHT 6 - -#define BMP_GUN_WIDTH 32 -#define BMP_GUN_HEIGHT 32 - -#define BMP_FIRE_WIDTH 24 -#define BMP_FIRE_HEIGHT 20 - -#define BMP_IMP_WIDTH 32 -#define BMP_IMP_HEIGHT 32 -#define BMP_IMP_COUNT 5 - -#define BMP_FIREBALL_WIDTH 16 -#define BMP_FIREBALL_HEIGHT 16 - -#define BMP_DOOR_WIDTH 100 -#define BMP_DOOR_HEIGHT 100 - -#define BMP_ITEMS_WIDTH 16 -#define BMP_ITEMS_HEIGHT 16 -#define BMP_ITEMS_COUNT 2 - -#define BMP_LOGO_WIDTH 128 -#define BMP_LOGO_HEIGHT 64 - -#define GRADIENT_WIDTH 2 -#define GRADIENT_HEIGHT 8 -#define GRADIENT_COUNT 8 -#define GRADIENT_WHITE 7 -#define GRADIENT_BLACK 0 - -// Fonts -extern const uint8_t zero[]; -extern const uint8_t one[]; -extern const uint8_t two[]; -extern const uint8_t three[]; -extern const uint8_t four[]; -extern const uint8_t five[]; -extern const uint8_t six[]; -extern const uint8_t seven[]; -extern const uint8_t eight[]; -extern const uint8_t nine[]; -extern const uint8_t A[]; -extern const uint8_t B[]; -extern const uint8_t C[]; -extern const uint8_t D[]; -extern const uint8_t E[]; -extern const uint8_t F[]; -extern const uint8_t G[]; -extern const uint8_t H[]; -extern const uint8_t I[]; -extern const uint8_t J[]; -extern const uint8_t K[]; -extern const uint8_t L[]; -extern const uint8_t M[]; -extern const uint8_t N[]; -extern const uint8_t O[]; -extern const uint8_t P[]; -extern const uint8_t Q[]; -extern const uint8_t R[]; -extern const uint8_t S[]; -extern const uint8_t T[]; -extern const uint8_t U[]; -extern const uint8_t V[]; -extern const uint8_t W[]; -extern const uint8_t X[]; -extern const uint8_t Y[]; -extern const uint8_t Z[]; -extern const uint8_t dot[]; -extern const uint8_t comma[]; -extern const uint8_t dash[]; -extern const uint8_t underscore[]; -extern const uint8_t bracket_open[]; -extern const uint8_t bracket_close[]; -extern const uint8_t cross_left[]; -extern const uint8_t cross_right[]; -extern const uint8_t pacman_left[]; -extern const uint8_t pacman_right[]; -extern const uint8_t box[]; -extern const uint8_t* char_arr[48]; -extern const uint8_t gradient[]; -//extern const uint8_t gun[] -//extern const uint8_t gun_mask[] -extern const uint8_t gun[]; -extern const uint8_t gun_mask[]; - -extern const uint8_t imp_inv[]; -extern const uint8_t imp_mask_inv[]; -extern const uint8_t fireball[]; -extern const uint8_t fireball_mask[]; -extern const uint8_t item[]; -extern const uint8_t item_mask[]; - -extern const uint8_t door[]; - -#endif diff --git a/applications/external/doom/assets/door2.png b/applications/external/doom/assets/door2.png deleted file mode 100644 index b4b4f03995d0a9da6374bbd471e2a068db3f9865..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31942 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 VrOV0%#})tp002ovPDHLkV1nA-0CWHV diff --git a/applications/external/doom/assets/fireball_inv.png b/applications/external/doom/assets/fireball_inv.png deleted file mode 100644 index b046288f8c413683b93d1bc87b189ead18d3ca61..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 418 zcmV;T0bTxyP)Px$TuDShR5(vvQwx$qAq;|e|05fAAZp)ir&H^X06{7rfCKPx$K1oDDR5(vzv)oMR{m9$RNiQOO*jUe+8Igc>{b3fOcn$K4ciEAxcMX140 zU9ncU-o_?x1F`~{rar@ZO6$11r*P1%9=-xZRcZO7lEgVQf2B0@$^Dhs2v)(n#@*ib zUs2|K*VKBotG4%7_E=J;8j;VvHkE-BGS6r{f}its!oN1R6P3|UFTZi*h=mcg#k=-+ zwTW#Y*)p?XX+0|XF+}Si(W>5Ks`D0xq7!zeAJ!U3llxt0bwPx&$4Nv%R9HuaSKG3LAPl4U|DUX!Nz+R}caATsD3GQ}%i6*<))>Qv@h2`k;Ic8+ z>L05Q_?8REylp-vV#4AYaX*&DgvA9g2Pmw!_mN&UF=7~#iO#>s1!`cM+Kccmo(bny z2>Ya9Ap_&iJzqok;6Y&twMyd_!oH2LSwvcLT0Gc#7T7yDW7?EN#U}~gN$gdUERY@= z$#9r_q147&&#Jk|b!65#pYhBzgZ%05lj?UGmEwv_G?BOQYup~>*oa$a4?BaNP|KJ$ z$HPyV|3D@PJ_TKgQQ|baNRTHYuUa_}VCS`-lJ1;DN-myZ&stFTzgLCsDr~gj#x*X) zs7NMae#%}p=UC4}(=8hYB#_WsNUed*oG*;+-vxFr?1-3>-Cw`S!8FebvyeG;Vs6>% zs6L0tTv+Z-#Nsc3fJ*Zim2E|Y@kFc$y3A05@BypTt`U8r4tzzj>WTvrM-YhQy^r7h zz0|~rM@*cHbP?TCfv~+of;jAaKWoe@V^2gjZXgRz(j(%9XC-l|aatJiW&8{akwCyNlTMZ5J~x#aqMz}6QS7^IyhMlM1Y+YiLvWqSlpEu z9Bjp+E!@T9@qU1^oQtyTesF-DD=g;%utTAzN4m9v$SdR=Rz2mNlSNG8g&-__{^3UD z1jIh;rW?V&zbpeO0;agaQo^Z3SU_C(RR@kq?cS&dpGD+z66=NQBE+=S0~Y*IZu6Hw zbkm*HZlhj6*#)Bs4?p|pg0`OdB<9C>f7Mj1%Da5kbXJ5uh*fgYeL>vEGK)Z`1Ns%zNqr=L?7J41$AemSHl_d2HF*nB>2XM4y?jewx(B$t-4V zC)Hdan?_p}v0gO=@4aH>NVwLsz?fp&5**HsaX!_vlc>_Yz)*IztAa@GYilLPxksXpvnOWd0LwB(Tq(QJp-O!U$}%2|=iF7s0UA-^4V zl*Zr1Q+r_I%1byu6DpOcDR;((hvY{^MPLxNQ6Z0;=fK=sOc~00000NkvXX Hu0mjfATIzz diff --git a/applications/external/doom/assets/gun_inv.png b/applications/external/doom/assets/gun_inv.png deleted file mode 100644 index e2ec052952905c92e9d11e523da71d6fa0dc96d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1050 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 diff --git a/applications/external/doom/assets/imp_mask_inv.png b/applications/external/doom/assets/imp_mask_inv.png deleted file mode 100644 index 70e99127021de388624f3dd9b187cf77ba77a4a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 958 zcmV;v13~Px&cu7P-R9HuSSKF53FbK2L|NrEgIpQjCI(gWgx-JM1f^3l^vi6$Kbt1T-|E#s= zw>aPSwSN_V>i_M7U-Prw{gVZ@*3i|RH{*eE;8Cx#fscIvoCv-u=z8-l%ATYBJZs^n z8nP@klaG7TY<275N|vlGI{B7j#TL`iK;_n0Ohzdhj$vMm&j7m|mpFt^-qz2mq`ljv>nrqz|V&93t^n)nb(a z+FKUr)m;v<8vxuI^3^$~&Lh?8I;cU(3L4FbBMWSj`f31}j$6M}y))qxNF!xcM&!8V z7s;O_ObkIX?s+OT^t6dz<@ zYO;YCUErv66p#R+)q>Gu61H;WJr|p>;pf$DMOyv?Pj996g?P^#cv@FqHSZ)r*CQsL?Mo#nk9GyG58R8j8*r9HzRvjJJ(O^^UR3o>%Z;4i z01;_)ML{Y+POCOqjR!xJ&>|=U0F{6!5G*2bZn(VfUA`Onm}WW5XeUjH2x#nI<^ zDsW;qlP$_)bHIr9-{U*seP#4S&6=SDh|u^_?T9m@(wmjgnx=$7vVpgCMu>~8VJ|d7@tjmrY)BFcv*N^%jeAI z9(S$Jl!rv444)jA*QOscC?(g{cE#!Y$O})RM%=p=6K_T~>9BzKmP6~sEBUCby$$ot zrSXaSHhV+?gcK;@xJ9FDZrnVJ(}};D4IQ3UAWT7pn3nRj`qyeU=Ko7P%#D|&v?neu zw;$1aL69zX#?rMK?QF4c|GE!&(V9wK^EsKV^&e9FtL>_2zgmKa(e(LKz9w1frKYzX gF`dV0wzJ*-13?RLuaP50!vFvP07*qoM6N<$g7sC)xc~qF diff --git a/applications/external/doom/assets/item_inv.png b/applications/external/doom/assets/item_inv.png deleted file mode 100644 index 1d32dbcd8e523fdde813aa45d9f0c408716c3a55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 417 zcmV;S0bc%zP)Px$TS-JgR5(vvlR=ilFbG4zzWn?i9i9J84VPV1IB-K`K_yat)(O-QlmOga zJcUeW-+4eq^5!_L3I>co#H6D@-Fq@Gn5U|$pc$D=iWXwP#RgXbmN z!Ol3RV=c+~qxeIAHU&iYiIqDJY#W456*H4TJdPM$`C1G1+>=xN(V)8`bA>J?7zJLT zwCR$dSMW1lFoQ4y55-(At3JAWMw35?8yG-sfrL{nA=Q6mV9y*JI@WsJc>0K$oQMH7 zs~g7w=ADoIMiY-^qDp-jqy%@=lKjIo1Au(bcOqG3xw1N zLS}{AKCpdZ#b&&bYh`@{<9jwE5urerI%u&>ZD_JupU=lOe&F^G&Qc%EoG8hD00000 LNkvXXu0mjfdm*rZ diff --git a/applications/external/doom/assets/item_mask_inv.png b/applications/external/doom/assets/item_mask_inv.png deleted file mode 100644 index a0bde9c76401a4cf074f8a59a18b41eafddfeaed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 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 -#include -#include "constants.h" -#include -#include "assets.h" - -#define CHECK_BIT(var, pos) ((var) & (1 << (pos))) - -static const uint8_t bit_mask[8] = {128, 64, 32, 16, 8, 4, 2, 1}; - -#define pgm_read_byte(addr) (*(const unsigned char*)(addr)) -#define read_bit(b, n) b& pgm_read_byte(bit_mask + n) ? 1 : 0 -//#define read_bit(byte, index) (((unsigned)(byte) >> (index)) & 1) - -void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity, Canvas* const canvas); -void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport, Canvas* const canvas); -void drawSprite( - int8_t x, - int8_t y, - const uint8_t* bitmap, - const uint8_t* bitmap_mask, - int16_t w, - int16_t h, - uint8_t sprite, - double distance, - Canvas* const canvas); -void drawBitmap( - int16_t x, - int16_t y, - const Icon* i, - int16_t w, - int16_t h, - uint16_t color, - Canvas* const canvas); -void drawTextSpace(int8_t x, int8_t y, char* txt, uint8_t space, Canvas* const canvas); -void drawChar(int8_t x, int8_t y, char ch, Canvas* const canvas); -void clearRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas); -void drawGun( - int16_t x, - int16_t y, - const uint8_t* bitmap, - int16_t w, - int16_t h, - uint16_t color, - Canvas* const canvas); -void drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas); -void drawText(uint8_t x, uint8_t y, uint8_t num, Canvas* const canvas); -void fadeScreen(uint8_t intensity, bool color, Canvas* const canvas); -bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i); -double getActualFps(); -void fps(); -uint8_t reverse_bits(uint8_t num); - -// FPS control -double delta = 1; -uint32_t lastFrameTime = 0; -uint8_t zbuffer[128]; /// 128 = screen width & REMOVE WHEN DISPLAY.H IMPLEMENTED - -void drawGun( - int16_t x, - int16_t y, - const uint8_t* bitmap, - int16_t w, - int16_t h, - uint16_t color, - Canvas* const canvas) { - int16_t byteWidth = (w + 7) / 8; - uint8_t byte = 0; - for(int16_t j = 0; j < h; j++, y++) { - for(int16_t i = 0; i < w; i++) { - if(i & 7) - byte <<= 1; - else - byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); - if(byte & 0x80) drawPixel(x + i, y, color, false, canvas); - } - } -} - -void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity, Canvas* const canvas) { - UNUSED(intensity); - uint8_t dots = end_y - start_y; - for(int i = 0; i < dots; i++) { - canvas_draw_dot(canvas, x, start_y + i); - } -} - -void drawBitmap( - int16_t x, - int16_t y, - const Icon* i, - int16_t w, - int16_t h, - uint16_t color, - Canvas* const canvas) { - UNUSED(w); - UNUSED(h); - if(!color) { - canvas_invert_color(canvas); - } - canvas_draw_icon(canvas, x, y, i); - if(!color) { - canvas_invert_color(canvas); - } -} - -void drawText(uint8_t x, uint8_t y, uint8_t num, Canvas* const canvas) { - char buf[4]; - snprintf(buf, 4, "%d", num); - drawTextSpace(x, y, buf, 1, canvas); -} - -void drawTextSpace(int8_t x, int8_t y, char* txt, uint8_t space, Canvas* const canvas) { - uint8_t pos = x; - uint8_t i = 0; - char ch; - while((ch = txt[i]) != '\0') { - drawChar(pos, y, ch, canvas); - i++; - pos += CHAR_WIDTH + space; - - // shortcut on end of screen - if(pos > SCREEN_WIDTH) return; - } -} - -// Custom drawBitmap method with scale support, mask, zindex and pattern filling -void drawSprite( - int8_t x, - int8_t y, - const uint8_t* bitmap, - const uint8_t* bitmap_mask, - int16_t w, - int16_t h, - uint8_t sprite, - double distance, - Canvas* const canvas) { - uint8_t tw = (double)w / distance; - uint8_t th = (double)h / distance; - uint8_t byte_width = w / 8; - uint8_t pixel_size = fmax(1, (double)1.0 / (double)distance); - uint16_t sprite_offset = byte_width * h * sprite; - - bool pixel; - bool maskPixel; - - // Don't draw the whole sprite if the anchor is hidden by z buffer - // Not checked per pixel for performance reasons - if(zbuffer[(int)(fmin(fmax(x, 0), ZBUFFER_SIZE - 1) / Z_RES_DIVIDER)] < - distance * DISTANCE_MULTIPLIER) { - return; - } - - for(uint8_t ty = 0; ty < th; ty += pixel_size) { - // Don't draw out of screen - if(y + ty < 0 || y + ty >= RENDER_HEIGHT) { - continue; - } - - uint8_t sy = ty * distance; // The y from the sprite - - for(uint8_t tx = 0; tx < tw; tx += pixel_size) { - uint8_t sx = tx * distance; // The x from the sprite - uint16_t byte_offset = sprite_offset + sy * byte_width + sx / 8; - - // Don't draw out of screen - if(x + tx < 0 || x + tx >= SCREEN_WIDTH) { - continue; - } - - maskPixel = read_bit(pgm_read_byte(bitmap_mask + byte_offset), sx % 8); - - if(maskPixel) { - pixel = read_bit(pgm_read_byte(bitmap + byte_offset), sx % 8); - for(uint8_t ox = 0; ox < pixel_size; ox++) { - for(uint8_t oy = 0; oy < pixel_size; oy++) { - if(bitmap == imp_inv) - drawPixel(x + tx + ox, y + ty + oy, 1, true, canvas); - else - drawPixel(x + tx + ox, y + ty + oy, pixel, true, canvas); - } - } - } - } - } -} - -void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport, Canvas* const canvas) { - if(x < 0 || x >= SCREEN_WIDTH || y < 0 || - y >= (raycasterViewport ? RENDER_HEIGHT : SCREEN_HEIGHT)) { - return; - } - if(color) - canvas_draw_dot(canvas, x, y); - else { - canvas_invert_color(canvas); - canvas_draw_dot(canvas, x, y); - canvas_invert_color(canvas); - } -} - -void drawChar(int8_t x, int8_t y, char ch, Canvas* const canvas) { - uint8_t lsb; - uint8_t c = 0; - while(CHAR_MAP[c] != ch && CHAR_MAP[c] != '\0') c++; - for(uint8_t i = 0; i < 6; i++) { - //lsb = (char_arr[c][i] >> 4); - lsb = reverse_bits(char_arr[c][i]); - for(uint8_t n = 0; n < 4; n++) { - if(CHECK_BIT(lsb, n)) { - drawPixel(x + n, y + i, true, false, canvas); - } - } - } -} - -void clearRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas) { - canvas_invert_color(canvas); - - for(int i = 0; i < w; i++) { - for(int j = 0; j < h; j++) { - canvas_draw_dot(canvas, x + i, y + j); - } - } - - canvas_invert_color(canvas); -} - -void drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas) { - for(int i = 0; i < w; i++) { - for(int j = 0; j < h; j++) { - canvas_draw_dot(canvas, x + i, y + j); - } - } -} - -bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i) { - if(i == 0) return 0; - if(i >= GRADIENT_COUNT - 1) return 1; - - uint8_t index = - fmax(0, fmin(GRADIENT_COUNT - 1, i)) * GRADIENT_WIDTH * GRADIENT_HEIGHT // gradient index - + y * GRADIENT_WIDTH % (GRADIENT_WIDTH * GRADIENT_HEIGHT) // y byte offset - + x / GRADIENT_HEIGHT % GRADIENT_WIDTH; // x byte offset - //uint8_t *gradient_data = NULL; - //furi_hal_compress_icon_decode(icon_get_data(&I_gradient_inv), &gradient_data); - // return the bit based on x - return read_bit(pgm_read_byte(gradient + index), x % 8); -} - -void fadeScreen(uint8_t intensity, bool color, Canvas* const canvas) { - for(uint8_t x = 0; x < SCREEN_WIDTH; x++) { - for(uint8_t y = 0; y < SCREEN_HEIGHT; y++) { - if(getGradientPixel(x, y, intensity)) drawPixel(x, y, color, false, canvas); - } - } -} - -// Adds a delay to limit play to specified fps -// Calculates also delta to keep movement consistent in lower framerates -void fps() { - while(furi_get_tick() - lastFrameTime < FRAME_TIME) - ; - delta = (double)(furi_get_tick() - lastFrameTime) / (double)FRAME_TIME; - lastFrameTime = furi_get_tick(); -} - -double getActualFps() { - return 1000 / ((double)FRAME_TIME * (double)delta); -} - -uint8_t reverse_bits(uint8_t num) { - unsigned int NO_OF_BITS = sizeof(num) * 8; - uint8_t reverse_num = 0; - uint8_t i; - for(i = 0; i < NO_OF_BITS; i++) { - if((num & (1 << i))) reverse_num |= 1 << ((NO_OF_BITS - 1) - i); - } - return reverse_num; -} \ No newline at end of file diff --git a/applications/external/doom/doom.c b/applications/external/doom/doom.c deleted file mode 100644 index c2d9a6bf0..000000000 --- a/applications/external/doom/doom.c +++ /dev/null @@ -1,1104 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "sound.h" -#include "display.h" -#include "assets.h" -#include "constants.h" -#include "entities.h" -#include "types.h" -#include "level.h" -#include -#include -#include - -#define SOUND - -// Useful macros -#define swap(a, b) \ - do { \ - typeof(a) temp = a; \ - a = b; \ - b = temp; \ - } while(0) -#define sign(a, b) (double)(a > b ? 1 : (b > a ? -1 : 0)) -#define pgm_read_byte(addr) (*(const unsigned char*)(addr)) - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType type; - InputEvent input; -} PluginEvent; - -typedef struct { - FuriMutex* mutex; - Player player; - Entity entity[MAX_ENTITIES]; - StaticEntity static_entity[MAX_STATIC_ENTITIES]; - uint8_t num_entities; - uint8_t num_static_entities; - - uint8_t scene; - uint8_t gun_pos; - double jogging; - double view_height; - bool init; - - bool up; - bool down; - bool left; - bool right; - bool fired; - bool gun_fired; - - double rot_speed; - double old_dir_x; - double old_plane_x; - NotificationApp* notify; -#ifdef SOUND - MusicPlayer* music_instance; - bool intro_sound; -#endif -} PluginState; - -static const NotificationSequence sequence_short_sound = { - &message_note_c5, - &message_delay_50, - &message_sound_off, - NULL, -}; -static const NotificationSequence sequence_long_sound = { - &message_note_c3, - &message_delay_100, - &message_sound_off, - NULL, -}; - -Coords translateIntoView(Coords* pos, PluginState* const plugin_state); -void updateHud(Canvas* const canvas, PluginState* const plugin_state); -// general - -bool invert_screen = false; -uint8_t flash_screen = 0; - -// game -// player and entities - -uint8_t getBlockAt(const uint8_t level[], uint8_t x, uint8_t y) { - if(x >= LEVEL_WIDTH || y >= LEVEL_HEIGHT) { - return E_FLOOR; - } - - // y is read in inverse order - return pgm_read_byte(level + (((LEVEL_HEIGHT - 1 - y) * LEVEL_WIDTH + x) / 2)) >> - (!(x % 2) * 4) // displace part of wanted bits - & 0b1111; // mask wanted bits -} - -// Finds the player in the map -void initializeLevel(const uint8_t level[], PluginState* const plugin_state) { - for(uint8_t y = LEVEL_HEIGHT - 1; y > 0; y--) { - for(uint8_t x = 0; x < LEVEL_WIDTH; x++) { - uint8_t block = getBlockAt(level, x, y); - - if(block == E_PLAYER) { - plugin_state->player = create_player(x, y); - return; - } - - // todo create other static entities - } - } -} - -bool isSpawned(UID uid, PluginState* const plugin_state) { - for(uint8_t i = 0; i < plugin_state->num_entities; i++) { - if(plugin_state->entity[i].uid == uid) return true; - } - - return false; -} - -bool isStatic(UID uid, PluginState* const plugin_state) { - for(uint8_t i = 0; i < plugin_state->num_static_entities; i++) { - if(plugin_state->static_entity[i].uid == uid) return true; - } - - return false; -} - -void spawnEntity(uint8_t type, uint8_t x, uint8_t y, PluginState* const plugin_state) { - // Limit the number of spawned entities - if(plugin_state->num_entities >= MAX_ENTITIES) { - return; - } - - // todo: read static entity status - - switch(type) { - case E_ENEMY: - plugin_state->entity[plugin_state->num_entities] = create_enemy(x, y); - plugin_state->num_entities++; - break; - - case E_KEY: - plugin_state->entity[plugin_state->num_entities] = create_key(x, y); - plugin_state->num_entities++; - break; - - case E_MEDIKIT: - plugin_state->entity[plugin_state->num_entities] = create_medikit(x, y); - plugin_state->num_entities++; - break; - } -} - -void spawnFireball(double x, double y, PluginState* const plugin_state) { - // Limit the number of spawned entities - if(plugin_state->num_entities >= MAX_ENTITIES) { - return; - } - - UID uid = create_uid(E_FIREBALL, x, y); - // Remove if already exists, don't throw anything. Not the best, but shouldn't happen too often - if(isSpawned(uid, plugin_state)) return; - - // Calculate direction. 32 angles - int16_t dir = - FIREBALL_ANGLES + atan2(y - plugin_state->player.pos.y, x - plugin_state->player.pos.x) / - (double)PI * FIREBALL_ANGLES; - if(dir < 0) dir += FIREBALL_ANGLES * 2; - plugin_state->entity[plugin_state->num_entities] = create_fireball(x, y, dir); - plugin_state->num_entities++; -} - -void removeEntity(UID uid, PluginState* const plugin_state) { - uint8_t i = 0; - bool found = false; - - while(i < plugin_state->num_entities) { - if(!found && plugin_state->entity[i].uid == uid) { - // todo: doze it - found = true; - plugin_state->num_entities--; - } - - // displace entities - if(found) { - plugin_state->entity[i] = plugin_state->entity[i + 1]; - } - - i++; - } -} - -void removeStaticEntity(UID uid, PluginState* const plugin_state) { - uint8_t i = 0; - bool found = false; - - while(i < plugin_state->num_static_entities) { - if(!found && plugin_state->static_entity[i].uid == uid) { - found = true; - plugin_state->num_static_entities--; - } - - // displace entities - if(found) { - plugin_state->static_entity[i] = plugin_state->static_entity[i + 1]; - } - - i++; - } -} - -UID detectCollision( - const uint8_t level[], - Coords* pos, - double relative_x, - double relative_y, - bool only_walls, - PluginState* const plugin_state) { - // Wall collision - uint8_t round_x = (int)(pos->x + relative_x); - uint8_t round_y = (int)(pos->y + relative_y); - uint8_t block = getBlockAt(level, round_x, round_y); - - if(block == E_WALL) { - // playSound(hit_wall_snd, HIT_WALL_SND_LEN); - return create_uid(block, round_x, round_y); - } - - if(only_walls) { - return UID_null; - } - - // Entity collision - for(uint8_t i = 0; i < plugin_state->num_entities; i++) { - // Don't collide with itself - if(&(plugin_state->entity[i].pos) == pos) { - continue; - } - - uint8_t type = uid_get_type(plugin_state->entity[i].uid); - - // Only ALIVE enemy collision - if(type != E_ENEMY || plugin_state->entity[i].state == S_DEAD || - plugin_state->entity[i].state == S_HIDDEN) { - continue; - } - - Coords new_coords = { - plugin_state->entity[i].pos.x - relative_x, - plugin_state->entity[i].pos.y - relative_y}; - uint8_t distance = coords_distance(pos, &new_coords); - - // Check distance and if it's getting closer - if(distance < ENEMY_COLLIDER_DIST && distance < plugin_state->entity[i].distance) { - return plugin_state->entity[i].uid; - } - } - - return UID_null; -} - -// Shoot -void fire(PluginState* const plugin_state) { - //playSound(shoot_snd, SHOOT_SND_LEN); - - for(uint8_t i = 0; i < plugin_state->num_entities; i++) { - // Shoot only ALIVE enemies - if(uid_get_type(plugin_state->entity[i].uid) != E_ENEMY || - plugin_state->entity[i].state == S_DEAD || plugin_state->entity[i].state == S_HIDDEN) { - continue; - } - - Coords transform = translateIntoView(&(plugin_state->entity[i].pos), plugin_state); - if(fabs(transform.x) < 20 && transform.y > 0) { - uint8_t damage = (double)fmin( - GUN_MAX_DAMAGE, - GUN_MAX_DAMAGE / (fabs(transform.x) * plugin_state->entity[i].distance) / 5); - if(damage > 0) { - plugin_state->entity[i].health = fmax(0, plugin_state->entity[i].health - damage); - plugin_state->entity[i].state = S_HIT; - plugin_state->entity[i].timer = 4; - } - } - } -} - -UID updatePosition( - const uint8_t level[], - Coords* pos, - double relative_x, - double relative_y, - bool only_walls, - PluginState* const plugin_state) { - UID collide_x = detectCollision(level, pos, relative_x, 0, only_walls, plugin_state); - UID collide_y = detectCollision(level, pos, 0, relative_y, only_walls, plugin_state); - - if(!collide_x) pos->x += relative_x; - if(!collide_y) pos->y += relative_y; - - return collide_x || collide_y || UID_null; -} - -void updateEntities(const uint8_t level[], Canvas* const canvas, PluginState* const plugin_state) { - uint8_t i = 0; - while(i < plugin_state->num_entities) { - // update distance - plugin_state->entity[i].distance = - coords_distance(&(plugin_state->player.pos), &(plugin_state->entity[i].pos)); - - // Run the timer. Works with actual frames. - // Todo: use delta here. But needs double type and more memory - if(plugin_state->entity[i].timer > 0) plugin_state->entity[i].timer--; - - // too far away. put it in doze mode - if(plugin_state->entity[i].distance > MAX_ENTITY_DISTANCE) { - removeEntity(plugin_state->entity[i].uid, plugin_state); - // don't increase 'i', since current one has been removed - continue; - } - - // bypass render if hidden - if(plugin_state->entity[i].state == S_HIDDEN) { - i++; - continue; - } - - uint8_t type = uid_get_type(plugin_state->entity[i].uid); - - switch(type) { - case E_ENEMY: { - // Enemy "IA" - if(plugin_state->entity[i].health == 0) { - if(plugin_state->entity[i].state != S_DEAD) { - plugin_state->entity[i].state = S_DEAD; - plugin_state->entity[i].timer = 6; - } - } else if(plugin_state->entity[i].state == S_HIT) { - if(plugin_state->entity[i].timer == 0) { - // Back to alert state - plugin_state->entity[i].state = S_ALERT; - plugin_state->entity[i].timer = 40; // delay next fireball thrown - } - } else if(plugin_state->entity[i].state == S_FIRING) { - if(plugin_state->entity[i].timer == 0) { - // Back to alert state - plugin_state->entity[i].state = S_ALERT; - plugin_state->entity[i].timer = 40; // delay next fireball throwm - } - } else { - // ALERT STATE - if(plugin_state->entity[i].distance > ENEMY_MELEE_DIST && - plugin_state->entity[i].distance < MAX_ENEMY_VIEW) { - if(plugin_state->entity[i].state != S_ALERT) { - plugin_state->entity[i].state = S_ALERT; - plugin_state->entity[i].timer = 20; // used to throw fireballs - } else { - if(plugin_state->entity[i].timer == 0) { - // Throw a fireball - spawnFireball( - plugin_state->entity[i].pos.x, - plugin_state->entity[i].pos.y, - plugin_state); - plugin_state->entity[i].state = S_FIRING; - plugin_state->entity[i].timer = 6; - } else { - // move towards to the player. - updatePosition( - level, - &(plugin_state->entity[i].pos), - sign(plugin_state->player.pos.x, plugin_state->entity[i].pos.x) * - (double)ENEMY_SPEED * 1, // NOT SURE (delta) - sign(plugin_state->player.pos.y, plugin_state->entity[i].pos.y) * - (double)ENEMY_SPEED * 1, // NOT SURE (delta) - true, - plugin_state); - } - } - } else if(plugin_state->entity[i].distance <= ENEMY_MELEE_DIST) { - if(plugin_state->entity[i].state != S_MELEE) { - // Preparing the melee attack - plugin_state->entity[i].state = S_MELEE; - plugin_state->entity[i].timer = 10; - } else if(plugin_state->entity[i].timer == 0) { - // Melee attack - plugin_state->player.health = - fmax(0, plugin_state->player.health - ENEMY_MELEE_DAMAGE); - plugin_state->entity[i].timer = 14; - flash_screen = 1; - updateHud(canvas, plugin_state); - } - } else { - // stand - plugin_state->entity[i].state = S_STAND; - } - } - break; - } - - case E_FIREBALL: { - if(plugin_state->entity[i].distance < FIREBALL_COLLIDER_DIST) { - // Hit the player and disappear - plugin_state->player.health = - fmax(0, plugin_state->player.health - ENEMY_FIREBALL_DAMAGE); - flash_screen = 1; - updateHud(canvas, plugin_state); - removeEntity(plugin_state->entity[i].uid, plugin_state); - continue; // continue in the loop - } else { - // Move. Only collide with walls. - // Note: using health to store the angle of the movement - UID collided = updatePosition( - level, - &(plugin_state->entity[i].pos), - cos((double)plugin_state->entity[i].health / FIREBALL_ANGLES * (double)PI) * - (double)FIREBALL_SPEED, - sin((double)plugin_state->entity[i].health / FIREBALL_ANGLES * (double)PI) * - (double)FIREBALL_SPEED, - true, - plugin_state); - - if(collided) { - removeEntity(plugin_state->entity[i].uid, plugin_state); - continue; // continue in the entity check loop - } - } - break; - } - - case E_MEDIKIT: { - if(plugin_state->entity[i].distance < ITEM_COLLIDER_DIST) { - // pickup - notification_message(plugin_state->notify, &sequence_long_sound); - //playSound(medkit_snd, MEDKIT_SND_LEN); - plugin_state->entity[i].state = S_HIDDEN; - plugin_state->player.health = fmin(100, plugin_state->player.health + 50); - updateHud(canvas, plugin_state); - flash_screen = 1; - } - break; - } - - case E_KEY: { - if(plugin_state->entity[i].distance < ITEM_COLLIDER_DIST) { - // pickup - notification_message(plugin_state->notify, &sequence_long_sound); - //playSound(get_key_snd, GET_KEY_SND_LEN); - plugin_state->entity[i].state = S_HIDDEN; - plugin_state->player.keys++; - updateHud(canvas, plugin_state); - flash_screen = 1; - } - break; - } - } - - i++; - } -} - -// The map raycaster. Based on https://lodev.org/cgtutor/raycasting.html -void renderMap( - const uint8_t level[], - double view_height, - Canvas* const canvas, - PluginState* const plugin_state) { - UID last_uid = 0; // NOT SURE ? - - for(uint8_t x = 0; x < SCREEN_WIDTH; x += RES_DIVIDER) { - double camera_x = 2 * (double)x / SCREEN_WIDTH - 1; - double ray_x = plugin_state->player.dir.x + plugin_state->player.plane.x * camera_x; - double ray_y = plugin_state->player.dir.y + plugin_state->player.plane.y * camera_x; - uint8_t map_x = (uint8_t)plugin_state->player.pos.x; - uint8_t map_y = (uint8_t)plugin_state->player.pos.y; - Coords map_coords = {plugin_state->player.pos.x, plugin_state->player.pos.y}; - double delta_x = fabs(1 / ray_x); - double delta_y = fabs(1 / ray_y); - - int8_t step_x; - int8_t step_y; - double side_x; - double side_y; - - if(ray_x < 0) { - step_x = -1; - side_x = (plugin_state->player.pos.x - map_x) * delta_x; - } else { - step_x = 1; - side_x = (map_x + (double)1.0 - plugin_state->player.pos.x) * delta_x; - } - - if(ray_y < 0) { - step_y = -1; - side_y = (plugin_state->player.pos.y - map_y) * delta_y; - } else { - step_y = 1; - side_y = (map_y + (double)1.0 - plugin_state->player.pos.y) * delta_y; - } - - // Wall detection - uint8_t depth = 0; - bool hit = 0; - bool side; - while(!hit && depth < MAX_RENDER_DEPTH) { - if(side_x < side_y) { - side_x += delta_x; - map_x += step_x; - side = 0; - } else { - side_y += delta_y; - map_y += step_y; - side = 1; - } - - uint8_t block = getBlockAt(level, map_x, map_y); - - if(block == E_WALL) { - hit = 1; - } else { - // Spawning entities here, as soon they are visible for the - // player. Not the best place, but would be a very performance - // cost scan for them in another loop - if(block == E_ENEMY || (block & 0b00001000) /* all collectable items */) { - // Check that it's close to the player - if(coords_distance(&(plugin_state->player.pos), &map_coords) < - MAX_ENTITY_DISTANCE) { - UID uid = create_uid(block, map_x, map_y); - if(last_uid != uid && !isSpawned(uid, plugin_state)) { - spawnEntity(block, map_x, map_y, plugin_state); - last_uid = uid; - } - } - } - } - - depth++; - } - - if(hit) { - double distance; - - if(side == 0) { - distance = - fmax(1, (map_x - plugin_state->player.pos.x + (1 - step_x) / 2) / ray_x); - } else { - distance = - fmax(1, (map_y - plugin_state->player.pos.y + (1 - step_y) / 2) / ray_y); - } - - // store zbuffer value for the column - zbuffer[x / Z_RES_DIVIDER] = fmin(distance * DISTANCE_MULTIPLIER, 255); - - // rendered line height - uint8_t line_height = RENDER_HEIGHT / distance; - - drawVLine( - x, - view_height / distance - line_height / 2 + RENDER_HEIGHT / 2, - view_height / distance + line_height / 2 + RENDER_HEIGHT / 2, - GRADIENT_COUNT - (int)distance / MAX_RENDER_DEPTH * GRADIENT_COUNT - side * 2, - canvas); - } - } -} - -// Sort entities from far to close -uint8_t sortEntities(PluginState* const plugin_state) { - uint8_t gap = plugin_state->num_entities; - bool swapped = false; - while(gap > 1 || swapped) { - //shrink factor 1.3 - gap = (gap * 10) / 13; - if(gap == 9 || gap == 10) gap = 11; - if(gap < 1) gap = 1; - swapped = false; - for(uint8_t i = 0; i < plugin_state->num_entities - gap; i++) { - uint8_t j = i + gap; - if(plugin_state->entity[i].distance < plugin_state->entity[j].distance) { - swap(plugin_state->entity[i], plugin_state->entity[j]); - swapped = true; - } - } - } - return swapped; -} - -Coords translateIntoView(Coords* pos, PluginState* const plugin_state) { - //translate sprite position to relative to camera - double sprite_x = pos->x - plugin_state->player.pos.x; - double sprite_y = pos->y - plugin_state->player.pos.y; - - //required for correct matrix multiplication - double inv_det = - ((double)1.0 / - ((double)plugin_state->player.plane.x * (double)plugin_state->player.dir.y - - (double)plugin_state->player.dir.x * (double)plugin_state->player.plane.y)); - double transform_x = - inv_det * (plugin_state->player.dir.y * sprite_x - plugin_state->player.dir.x * sprite_y); - double transform_y = inv_det * (-plugin_state->player.plane.y * sprite_x + - plugin_state->player.plane.x * sprite_y); // Z in screen - Coords res = {transform_x, transform_y}; - return res; -} - -void renderEntities(double view_height, Canvas* const canvas, PluginState* const plugin_state) { - sortEntities(plugin_state); - - for(uint8_t i = 0; i < plugin_state->num_entities; i++) { - if(plugin_state->entity[i].state == S_HIDDEN) continue; - - Coords transform = translateIntoView(&(plugin_state->entity[i].pos), plugin_state); - - // don´t render if behind the player or too far away - if(transform.y <= (double)0.1 || transform.y > MAX_SPRITE_DEPTH) { - continue; - } - - int16_t sprite_screen_x = HALF_WIDTH * ((double)1.0 + transform.x / transform.y); - int8_t sprite_screen_y = RENDER_HEIGHT / 2 + view_height / transform.y; - uint8_t type = uid_get_type(plugin_state->entity[i].uid); - - // don´t try to render if outside of screen - // doing this pre-shortcut due int16 -> int8 conversion makes out-of-screen - // values fit into the screen space - if(sprite_screen_x < -HALF_WIDTH || sprite_screen_x > SCREEN_WIDTH + HALF_WIDTH) { - continue; - } - - switch(type) { - case E_ENEMY: { - uint8_t sprite; - if(plugin_state->entity[i].state == S_ALERT) { - // walking - sprite = ((int)furi_get_tick() / 500) % 2; - } else if(plugin_state->entity[i].state == S_FIRING) { - // fireball - sprite = 2; - } else if(plugin_state->entity[i].state == S_HIT) { - // hit - sprite = 3; - } else if(plugin_state->entity[i].state == S_MELEE) { - // melee atack - sprite = plugin_state->entity[i].timer > 10 ? 2 : 1; - } else if(plugin_state->entity[i].state == S_DEAD) { - // dying - sprite = plugin_state->entity[i].timer > 0 ? 3 : 4; - } else { - // stand - sprite = 0; - } - - drawSprite( - sprite_screen_x - BMP_IMP_WIDTH * (double).5 / transform.y, - sprite_screen_y - 8 / transform.y, - imp_inv, - imp_mask_inv, - BMP_IMP_WIDTH, - BMP_IMP_HEIGHT, - sprite, - transform.y, - canvas); - break; - } - - case E_FIREBALL: { - drawSprite( - sprite_screen_x - BMP_FIREBALL_WIDTH / 2 / transform.y, - sprite_screen_y - BMP_FIREBALL_HEIGHT / 2 / transform.y, - fireball, - fireball_mask, - BMP_FIREBALL_WIDTH, - BMP_FIREBALL_HEIGHT, - 0, - transform.y, - canvas); - break; - } - - case E_MEDIKIT: { - drawSprite( - sprite_screen_x - BMP_ITEMS_WIDTH / 2 / transform.y, - sprite_screen_y + 5 / transform.y, - item, - item_mask, - BMP_ITEMS_WIDTH, - BMP_ITEMS_HEIGHT, - 0, - transform.y, - canvas); - break; - } - - case E_KEY: { - drawSprite( - sprite_screen_x - BMP_ITEMS_WIDTH / 2 / transform.y, - sprite_screen_y + 5 / transform.y, - item, - item_mask, - BMP_ITEMS_WIDTH, - BMP_ITEMS_HEIGHT, - 1, - transform.y, - canvas); - break; - } - } - } -} - -void renderGun(uint8_t gun_pos, double amount_jogging, Canvas* const canvas) { - // jogging - char x = 48 + sin((double)furi_get_tick() * (double)JOGGING_SPEED) * 10 * amount_jogging; - char y = RENDER_HEIGHT - gun_pos + - fabs(cos((double)furi_get_tick() * (double)JOGGING_SPEED)) * 8 * amount_jogging; - - if(gun_pos > GUN_SHOT_POS - 2) { - // Gun fire - drawBitmap(x + 6, y - 11, &I_fire_inv, BMP_FIRE_WIDTH, BMP_FIRE_HEIGHT, 1, canvas); - } - - // Don't draw over the hud! - uint8_t clip_height = fmax(0, fmin(y + BMP_GUN_HEIGHT, RENDER_HEIGHT) - y); - - // Draw the gun (black mask + actual sprite). - drawBitmap(x, y, &I_gun_mask_inv, BMP_GUN_WIDTH, clip_height, 0, canvas); - drawBitmap(x, y, &I_gun_inv, BMP_GUN_WIDTH, clip_height, 1, canvas); - //drawGun(x,y,gun_mask, BMP_GUN_WIDTH, clip_height, 0, canvas); - //drawGun(x,y,gun, BMP_GUN_WIDTH, clip_height, 1, canvas); -} - -// Only needed first time -void renderHud(Canvas* const canvas, PluginState* plugin_state) { - drawTextSpace(2, 58, "{}", 0, canvas); // Health symbol - drawTextSpace(40, 58, "[]", 0, canvas); // Keys symbol - updateHud(canvas, plugin_state); -} - -// Render values for the HUD -void updateHud(Canvas* const canvas, PluginState* plugin_state) { - clearRect(12, 58, 15, 6, canvas); - clearRect(50, 58, 15, 6, canvas); - drawText(12, 58, plugin_state->player.health, canvas); - drawText(50, 58, plugin_state->player.keys, canvas); -} - -// Debug stats -void renderStats(Canvas* const canvas, PluginState* plugin_state) { - clearRect(58, 58, 70, 6, canvas); - drawText(114, 58, (int)getActualFps(), canvas); - drawText(82, 58, plugin_state->num_entities, canvas); - // drawText(94, 58, freeMemory()); -} - -// Intro screen -void loopIntro(Canvas* const canvas) { - canvas_draw_icon(canvas, 0, 0, &I_logo_inv); - //drawTextSpace(SCREEN_WIDTH / 2 - 25, SCREEN_HEIGHT * .8, "PRESS FIRE", 1, canvas); -} - -static void render_callback(Canvas* const canvas, void* ctx) { - furi_assert(ctx); - PluginState* plugin_state = ctx; - furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); - - canvas_set_font(canvas, FontPrimary); - - switch(plugin_state->scene) { - case INTRO: { - loopIntro(canvas); - break; - } - case GAME_PLAY: { - updateEntities(sto_level_1, canvas, plugin_state); - - renderGun(plugin_state->gun_pos, plugin_state->jogging, canvas); - renderMap(sto_level_1, plugin_state->view_height, canvas, plugin_state); - - renderEntities(plugin_state->view_height, canvas, plugin_state); - - renderHud(canvas, plugin_state); - updateHud(canvas, plugin_state); - renderStats(canvas, plugin_state); - break; - } - } - furi_mutex_release(plugin_state->mutex); -} - -static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - PluginEvent event = {.type = EventTypeKey, .input = *input_event}; - furi_message_queue_put(event_queue, &event, 0); -} - -static void doom_state_init(PluginState* const plugin_state) { - plugin_state->notify = furi_record_open(RECORD_NOTIFICATION); - plugin_state->num_entities = 0; - plugin_state->num_static_entities = 0; - - plugin_state->scene = INTRO; - plugin_state->gun_pos = 0; - plugin_state->view_height = 0; - plugin_state->init = true; - - plugin_state->up = false; - plugin_state->down = false; - plugin_state->left = false; - plugin_state->right = false; - plugin_state->fired = false; - plugin_state->gun_fired = false; -#ifdef SOUND - - plugin_state->music_instance = malloc(sizeof(MusicPlayer)); - plugin_state->music_instance->model = malloc(sizeof(MusicPlayerModel)); - memset( - plugin_state->music_instance->model->duration_history, - 0xff, - MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); - memset( - plugin_state->music_instance->model->semitone_history, - 0xff, - MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); - plugin_state->music_instance->model->volume = 2; - - plugin_state->music_instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); - //plugin_state->music_instance->view_port = view_port_alloc(); - - plugin_state->music_instance->worker = music_player_worker_alloc(); - //music_player_worker_set_volume(plugin_state->music_instance->worker, 0.75); - music_player_worker_set_volume( - plugin_state->music_instance->worker, - MUSIC_PLAYER_VOLUMES[plugin_state->music_instance->model->volume]); - plugin_state->intro_sound = true; - //init_sound(plugin_state->music_instance); -#endif -} - -static void doom_game_update_timer_callback(FuriMessageQueue* event_queue) { - furi_assert(event_queue); - - PluginEvent event = {.type = EventTypeTick}; - furi_message_queue_put(event_queue, &event, 0); -} - -static void doom_game_tick(PluginState* const plugin_state) { - if(plugin_state->scene == GAME_PLAY) { - //fps(); - //player is alive - if(plugin_state->player.health > 0) { - if(plugin_state->up) { - plugin_state->player.velocity += - ((double)MOV_SPEED - plugin_state->player.velocity) * (double).4; - plugin_state->jogging = fabs(plugin_state->player.velocity) * MOV_SPEED_INV; - //plugin_state->up = false; - } else if(plugin_state->down) { - plugin_state->player.velocity += - (-(double)MOV_SPEED - plugin_state->player.velocity) * (double).4; - plugin_state->jogging = fabs(plugin_state->player.velocity) * MOV_SPEED_INV; - //plugin_state->down = false; - } else { - plugin_state->player.velocity *= (double).5; - plugin_state->jogging = fabs(plugin_state->player.velocity) * MOV_SPEED_INV; - } - - if(plugin_state->right) { - plugin_state->rot_speed = (double)ROT_SPEED * delta; - plugin_state->old_dir_x = plugin_state->player.dir.x; - plugin_state->player.dir.x = - plugin_state->player.dir.x * cos(-(plugin_state->rot_speed)) - - plugin_state->player.dir.y * sin(-(plugin_state->rot_speed)); - plugin_state->player.dir.y = - plugin_state->old_dir_x * sin(-(plugin_state->rot_speed)) + - plugin_state->player.dir.y * cos(-(plugin_state->rot_speed)); - plugin_state->old_plane_x = plugin_state->player.plane.x; - plugin_state->player.plane.x = - plugin_state->player.plane.x * cos(-(plugin_state->rot_speed)) - - plugin_state->player.plane.y * sin(-(plugin_state->rot_speed)); - plugin_state->player.plane.y = - plugin_state->old_plane_x * sin(-(plugin_state->rot_speed)) + - plugin_state->player.plane.y * cos(-(plugin_state->rot_speed)); - - //plugin_state->right = false; - } else if(plugin_state->left) { - plugin_state->rot_speed = (double)ROT_SPEED * delta; - plugin_state->old_dir_x = plugin_state->player.dir.x; - plugin_state->player.dir.x = - plugin_state->player.dir.x * cos(plugin_state->rot_speed) - - plugin_state->player.dir.y * sin(plugin_state->rot_speed); - plugin_state->player.dir.y = - plugin_state->old_dir_x * sin(plugin_state->rot_speed) + - plugin_state->player.dir.y * cos(plugin_state->rot_speed); - plugin_state->old_plane_x = plugin_state->player.plane.x; - plugin_state->player.plane.x = - plugin_state->player.plane.x * cos(plugin_state->rot_speed) - - plugin_state->player.plane.y * sin(plugin_state->rot_speed); - plugin_state->player.plane.y = - plugin_state->old_plane_x * sin(plugin_state->rot_speed) + - plugin_state->player.plane.y * cos(plugin_state->rot_speed); - //plugin_state->left = false; - } - plugin_state->view_height = - fabs(sin((double)furi_get_tick() * (double)JOGGING_SPEED)) * 6 * - plugin_state->jogging; - - if(plugin_state->gun_pos > GUN_TARGET_POS) { - // Right after fire - plugin_state->gun_pos -= 1; - } else if(plugin_state->gun_pos < GUN_TARGET_POS) { - plugin_state->gun_pos += 2; - } else if(!plugin_state->gun_fired && plugin_state->fired) { - //furi_hal_speaker_start(20480 / 10, 0.45f); - /*#ifdef SOUND - music_player_worker_start(plugin_state->music_instance->worker); -#endif*/ - plugin_state->gun_pos = GUN_SHOT_POS; - plugin_state->gun_fired = true; - plugin_state->fired = false; - fire(plugin_state); - - } else if(plugin_state->gun_fired && !plugin_state->fired) { - //furi_hal_speaker_stop(); - plugin_state->gun_fired = false; - - notification_message(plugin_state->notify, &sequence_short_sound); - - /*#ifdef SOUND - music_player_worker_stop(plugin_state->music_instance->worker); -#endif*/ - } - } else { - // Player is dead - if(plugin_state->view_height > -10) plugin_state->view_height--; - if(plugin_state->gun_pos > 1) plugin_state->gun_pos -= 2; - } - - if(fabs(plugin_state->player.velocity) > (double)0.003) { - updatePosition( - sto_level_1, - &(plugin_state->player.pos), - plugin_state->player.dir.x * plugin_state->player.velocity * delta, - plugin_state->player.dir.y * plugin_state->player.velocity * delta, - false, - plugin_state); - } else { - plugin_state->player.velocity = 0; - } - } -} - -int32_t doom_app() { - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); - PluginState* plugin_state = malloc(sizeof(PluginState)); - doom_state_init(plugin_state); - plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - if(!plugin_state->mutex) { - FURI_LOG_E("Doom_game", "cannot create mutex\r\n"); - furi_record_close(RECORD_NOTIFICATION); - furi_message_queue_free(event_queue); - free(plugin_state); - return 255; - } - FuriTimer* timer = - furi_timer_alloc(doom_game_update_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 12); - // Set system callbacks - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, render_callback, plugin_state); - view_port_input_callback_set(view_port, input_callback, event_queue); - - // Open GUI and register view_port - Gui* gui = furi_record_open(RECORD_GUI); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - ////////////////////////////////// - plugin_state->init = false; - - PluginEvent event; -#ifdef SOUND - music_player_worker_load_rtttl_from_string(plugin_state->music_instance->worker, dsintro); - music_player_worker_start(plugin_state->music_instance->worker); -#endif - // Call dolphin deed on game start - dolphin_deed(DolphinDeedPluginGameStart); - - for(bool processing = true; processing;) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - furi_mutex_acquire(plugin_state->mutex, FuriWaitForever); -#ifdef SOUND - furi_check( - furi_mutex_acquire(plugin_state->music_instance->model_mutex, FuriWaitForever) == - FuriStatusOk); -#endif - if(event_status == FuriStatusOk) { - // press events - if(event.type == EventTypeKey) { - if(event.input.key == InputKeyBack) { - processing = false; -#ifdef SOUND - if(plugin_state->intro_sound) { - furi_mutex_release(plugin_state->music_instance->model_mutex); - music_player_worker_stop(plugin_state->music_instance->worker); - } -#endif - } - - if(event.input.type == InputTypePress) { - if(plugin_state->scene == INTRO && event.input.key == InputKeyOk) { - plugin_state->scene = GAME_PLAY; - initializeLevel(sto_level_1, plugin_state); -#ifdef SOUND - furi_mutex_release(plugin_state->music_instance->model_mutex); - music_player_worker_stop(plugin_state->music_instance->worker); - plugin_state->intro_sound = false; -#endif - goto skipintro; - } - - //While playing game - if(plugin_state->scene == GAME_PLAY) { - // If the player is alive - if(plugin_state->player.health > 0) { - //Player speed - if(event.input.key == InputKeyUp) { - plugin_state->up = true; - } else if(event.input.key == InputKeyDown) { - plugin_state->down = true; - } - // Player rotation - if(event.input.key == InputKeyRight) { - plugin_state->right = true; - } else if(event.input.key == InputKeyLeft) { - plugin_state->left = true; - } - if(event.input.key == InputKeyOk) { - /*#ifdef SOUND - music_player_worker_load_rtttl_from_string(plugin_state->music_instance->worker, dspistol); -#endif*/ - if(plugin_state->fired) { - plugin_state->fired = false; - } else { - plugin_state->fired = true; - } - } - } else { - // Player is dead - if(event.input.key == InputKeyOk) plugin_state->scene = INTRO; - } - } - } - if(event.input.type == InputTypeRelease) { - if(plugin_state->player.health > 0) { - //Player speed - if(event.input.key == InputKeyUp) { - plugin_state->up = false; - } else if(event.input.key == InputKeyDown) { - plugin_state->down = false; - } - // Player rotation - if(event.input.key == InputKeyRight) { - plugin_state->right = false; - } else if(event.input.key == InputKeyLeft) { - plugin_state->left = false; - } - } - } - } - - skipintro: - if(event.type == EventTypeTick) { - doom_game_tick(plugin_state); - } - } -#ifdef SOUND - furi_mutex_release(plugin_state->music_instance->model_mutex); -#endif - view_port_update(view_port); - furi_mutex_release(plugin_state->mutex); - } -#ifdef SOUND - music_player_worker_free(plugin_state->music_instance->worker); - furi_mutex_free(plugin_state->music_instance->model_mutex); - free(plugin_state->music_instance->model); - free(plugin_state->music_instance); -#endif - furi_record_close(RECORD_NOTIFICATION); - furi_timer_free(timer); - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close(RECORD_GUI); - view_port_free(view_port); - furi_mutex_free(plugin_state->mutex); - furi_message_queue_free(event_queue); - free(plugin_state); - return 0; -} diff --git a/applications/external/doom/doom_10px.png b/applications/external/doom/doom_10px.png deleted file mode 100644 index 17fe22c8763a9e3b86fcd6116a4a51d3a1b820a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1772 zcmcIl&2QX99CaJ1Dv`n?><-!3Z4ycFTxWJA703jj71#v(sGrRV-pf=>fO1mC=-@Nzc{eGVLbbs&d zYwItrYnpa#cV}=QuWG!0^|JiEynpzMygXO#+^sdu?5Xdi`TdP!O}lb88y+@?@w*Wf zxk;EvxH->DiPp3m8}pLTBi`r$m^z?{^ z%-Fc8->)H1`+}4lcFJWP}IGSVZduzWo46zLRX9=5!0q`NUO^=kvTKk$JtZTyO_@Fj~;FGdcC#R|CEEX2WQ`Zy!OqNSDbkK&ih}WOVj&5eDvV# ngXbU8vG&vNKmRy>X-#|ikLy>z`b>X8okzRddxI};efY`WEeb3q diff --git a/applications/external/doom/doom_music_player_worker.c b/applications/external/doom/doom_music_player_worker.c deleted file mode 100644 index e81549625..000000000 --- a/applications/external/doom/doom_music_player_worker.c +++ /dev/null @@ -1,504 +0,0 @@ -#include "doom_music_player_worker.h" - -#include -#include - -#include -#include - -#include - -#define TAG "MusicPlayerWorker" - -#define MUSIC_PLAYER_FILETYPE "Flipper Music Format" -#define MUSIC_PLAYER_VERSION 0 - -#define SEMITONE_PAUSE 0xFF - -#define NOTE_C4 261.63f -#define NOTE_C4_SEMITONE (4.0f * 12.0f) -#define TWO_POW_TWELTH_ROOT 1.059463094359f - -typedef struct { - uint8_t semitone; - uint8_t duration; - uint8_t dots; -} NoteBlock; - -ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST); - -struct MusicPlayerWorker { - FuriThread* thread; - bool should_work; - - MusicPlayerWorkerCallback callback; - void* callback_context; - - float volume; - uint32_t bpm; - uint32_t duration; - uint32_t octave; - NoteBlockArray_t notes; -}; - -static int32_t music_player_worker_thread_callback(void* context) { - furi_assert(context); - MusicPlayerWorker* instance = context; - - NoteBlockArray_it_t it; - NoteBlockArray_it(it, instance->notes); - if(furi_hal_speaker_acquire(1000)) { - while(instance->should_work) { - if(NoteBlockArray_end_p(it)) { - NoteBlockArray_it(it, instance->notes); - furi_delay_ms(10); - } else { - NoteBlock* note_block = NoteBlockArray_ref(it); - - float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE; - float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4); - float duration = 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm / - note_block->duration; - uint32_t dots = note_block->dots; - while(dots > 0) { - duration += duration / 2; - dots--; - } - uint32_t next_tick = furi_get_tick() + duration; - float volume = instance->volume; - - if(instance->callback) { - instance->callback( - note_block->semitone, - note_block->dots, - note_block->duration, - 0.0, - instance->callback_context); - } - - furi_hal_speaker_stop(); - furi_hal_speaker_start(frequency, volume); - while(instance->should_work && furi_get_tick() < next_tick) { - volume *= 0.9945679; - furi_hal_speaker_set_volume(volume); - furi_delay_ms(2); - } - NoteBlockArray_next(it); - } - } - - furi_hal_speaker_stop(); - furi_hal_speaker_release(); - } else { - FURI_LOG_E(TAG, "Speaker system is busy with another process."); - } - - return 0; -} - -MusicPlayerWorker* music_player_worker_alloc() { - MusicPlayerWorker* instance = malloc(sizeof(MusicPlayerWorker)); - - NoteBlockArray_init(instance->notes); - - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "MusicPlayerWorker"); - furi_thread_set_stack_size(instance->thread, 1024); - furi_thread_set_context(instance->thread, instance); - furi_thread_set_callback(instance->thread, music_player_worker_thread_callback); - - instance->volume = 1.0f; - - return instance; -} - -void music_player_worker_free(MusicPlayerWorker* instance) { - furi_assert(instance); - furi_thread_free(instance->thread); - NoteBlockArray_clear(instance->notes); - free(instance); -} - -static bool is_digit(const char c) { - return isdigit(c) != 0; -} - -static bool is_letter(const char c) { - return islower(c) != 0 || isupper(c) != 0; -} - -static bool is_space(const char c) { - return c == ' ' || c == '\t'; -} - -static size_t extract_number(const char* string, uint32_t* number) { - size_t ret = 0; - while(is_digit(*string)) { - *number *= 10; - *number += (*string - '0'); - string++; - ret++; - } - return ret; -} - -static size_t extract_dots(const char* string, uint32_t* number) { - size_t ret = 0; - while(*string == '.') { - *number += 1; - string++; - ret++; - } - return ret; -} - -static size_t extract_char(const char* string, char* symbol) { - if(is_letter(*string)) { - *symbol = *string; - return 1; - } else { - return 0; - } -} - -static size_t extract_sharp(const char* string, char* symbol) { - if(*string == '#' || *string == '_') { - *symbol = '#'; - return 1; - } else { - return 0; - } -} - -static size_t skip_till(const char* string, const char symbol) { - size_t ret = 0; - while(*string != '\0' && *string != symbol) { - string++; - ret++; - } - if(*string != symbol) { - ret = 0; - } - return ret; -} - -static bool music_player_worker_add_note( - MusicPlayerWorker* instance, - uint8_t semitone, - uint8_t duration, - uint8_t dots) { - NoteBlock note_block; - - note_block.semitone = semitone; - note_block.duration = duration; - note_block.dots = dots; - - NoteBlockArray_push_back(instance->notes, note_block); - - return true; -} - -static int8_t note_to_semitone(const char note) { - switch(note) { - case 'C': - return 0; - // C# - case 'D': - return 2; - // D# - case 'E': - return 4; - case 'F': - return 5; - // F# - case 'G': - return 7; - // G# - case 'A': - return 9; - // A# - case 'B': - return 11; - default: - return 0; - } -} - -static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const char* string) { - const char* cursor = string; - bool result = true; - - while(*cursor != '\0') { - if(!is_space(*cursor)) { - uint32_t duration = 0; - char note_char = '\0'; - char sharp_char = '\0'; - uint32_t octave = 0; - uint32_t dots = 0; - - // Parsing - cursor += extract_number(cursor, &duration); - cursor += extract_char(cursor, ¬e_char); - cursor += extract_sharp(cursor, &sharp_char); - cursor += extract_number(cursor, &octave); - cursor += extract_dots(cursor, &dots); - - // Post processing - note_char = toupper(note_char); - if(!duration) { - duration = instance->duration; - } - if(!octave) { - octave = instance->octave; - } - - // Validation - bool is_valid = true; - is_valid &= (duration >= 1 && duration <= 128); - is_valid &= ((note_char >= 'A' && note_char <= 'G') || note_char == 'P'); - is_valid &= (sharp_char == '#' || sharp_char == '\0'); - is_valid &= (octave <= 16); - is_valid &= (dots <= 16); - if(!is_valid) { - FURI_LOG_E( - TAG, - "Invalid note: %lu%c%c%lu.%lu", - duration, - note_char == '\0' ? '_' : note_char, - sharp_char == '\0' ? '_' : sharp_char, - octave, - dots); - result = false; - break; - } - - // Note to semitones - uint8_t semitone = 0; - if(note_char == 'P') { - semitone = SEMITONE_PAUSE; - } else { - semitone += octave * 12; - semitone += note_to_semitone(note_char); - semitone += sharp_char == '#' ? 1 : 0; - } - - if(music_player_worker_add_note(instance, semitone, duration, dots)) { - FURI_LOG_D( - TAG, - "Added note: %c%c%lu.%lu = %u %lu", - note_char == '\0' ? '_' : note_char, - sharp_char == '\0' ? '_' : sharp_char, - octave, - dots, - semitone, - duration); - } else { - FURI_LOG_E( - TAG, - "Invalid note: %c%c%lu.%lu = %u %lu", - note_char == '\0' ? '_' : note_char, - sharp_char == '\0' ? '_' : sharp_char, - octave, - dots, - semitone, - duration); - } - cursor += skip_till(cursor, ','); - } - - if(*cursor != '\0') cursor++; - } - - return result; -} - -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - bool ret = false; - if(strcasestr(file_path, ".fmf")) { - ret = music_player_worker_load_fmf_from_file(instance, file_path); - } else { - ret = music_player_worker_load_rtttl_from_file(instance, file_path); - } - return ret; -} - -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - bool result = false; - FuriString* temp_str; - temp_str = furi_string_alloc(); - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* file = flipper_format_file_alloc(storage); - - do { - if(!flipper_format_file_open_existing(file, file_path)) break; - - uint32_t version = 0; - if(!flipper_format_read_header(file, temp_str, &version)) break; - if(furi_string_cmp_str(temp_str, MUSIC_PLAYER_FILETYPE) || - (version != MUSIC_PLAYER_VERSION)) { - FURI_LOG_E(TAG, "Incorrect file format or version"); - break; - } - - if(!flipper_format_read_uint32(file, "BPM", &instance->bpm, 1)) { - FURI_LOG_E(TAG, "BPM is missing"); - break; - } - if(!flipper_format_read_uint32(file, "Duration", &instance->duration, 1)) { - FURI_LOG_E(TAG, "Duration is missing"); - break; - } - if(!flipper_format_read_uint32(file, "Octave", &instance->octave, 1)) { - FURI_LOG_E(TAG, "Octave is missing"); - break; - } - - if(!flipper_format_read_string(file, "Notes", temp_str)) { - FURI_LOG_E(TAG, "Notes is missing"); - break; - } - - if(!music_player_worker_parse_notes(instance, furi_string_get_cstr(temp_str))) { - break; - } - - result = true; - } while(false); - - furi_record_close(RECORD_STORAGE); - flipper_format_free(file); - furi_string_free(temp_str); - - return result; -} - -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path) { - furi_assert(instance); - furi_assert(file_path); - - bool result = false; - FuriString* content; - content = furi_string_alloc(); - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); - - do { - if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { - FURI_LOG_E(TAG, "Unable to open file"); - break; - }; - - uint16_t ret = 0; - do { - uint8_t buffer[65] = {0}; - ret = storage_file_read(file, buffer, sizeof(buffer) - 1); - for(size_t i = 0; i < ret; i++) { - furi_string_push_back(content, buffer[i]); - } - } while(ret > 0); - - furi_string_trim(content); - if(!furi_string_size(content)) { - FURI_LOG_E(TAG, "Empty file"); - break; - } - - if(!music_player_worker_load_rtttl_from_string(instance, furi_string_get_cstr(content))) { - FURI_LOG_E(TAG, "Invalid file content"); - break; - } - - result = true; - } while(0); - - storage_file_free(file); - furi_record_close(RECORD_STORAGE); - furi_string_free(content); - - return result; -} - -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string) { - furi_assert(instance); - - const char* cursor = string; - - // Skip name - cursor += skip_till(cursor, ':'); - if(*cursor != ':') { - return false; - } - - // Duration - cursor += skip_till(cursor, '='); - if(*cursor != '=') { - return false; - } - cursor++; - cursor += extract_number(cursor, &instance->duration); - - // Octave - cursor += skip_till(cursor, '='); - if(*cursor != '=') { - return false; - } - cursor++; - cursor += extract_number(cursor, &instance->octave); - - // BPM - cursor += skip_till(cursor, '='); - if(*cursor != '=') { - return false; - } - cursor++; - cursor += extract_number(cursor, &instance->bpm); - - // Notes - cursor += skip_till(cursor, ':'); - if(*cursor != ':') { - return false; - } - cursor++; - if(!music_player_worker_parse_notes(instance, cursor)) { - return false; - } - - return true; -} - -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context) { - furi_assert(instance); - instance->callback = callback; - instance->callback_context = context; -} - -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume) { - furi_assert(instance); - instance->volume = volume; -} - -void music_player_worker_start(MusicPlayerWorker* instance) { - furi_assert(instance); - furi_assert(instance->should_work == false); - - instance->should_work = true; - furi_thread_start(instance->thread); -} - -void music_player_worker_stop(MusicPlayerWorker* instance) { - furi_assert(instance); - furi_assert(instance->should_work == true); - - instance->should_work = false; - furi_thread_join(instance->thread); -} diff --git a/applications/external/doom/doom_music_player_worker.h b/applications/external/doom/doom_music_player_worker.h deleted file mode 100644 index 9958a9273..000000000 --- a/applications/external/doom/doom_music_player_worker.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*MusicPlayerWorkerCallback)( - uint8_t semitone, - uint8_t dots, - uint8_t duration, - float position, - void* context); - -typedef struct MusicPlayerWorker MusicPlayerWorker; - -MusicPlayerWorker* music_player_worker_alloc(); - -void music_player_worker_free(MusicPlayerWorker* instance); - -bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path); - -bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string); - -void music_player_worker_set_callback( - MusicPlayerWorker* instance, - MusicPlayerWorkerCallback callback, - void* context); - -void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume); - -void music_player_worker_start(MusicPlayerWorker* instance); - -void music_player_worker_stop(MusicPlayerWorker* instance); - -#ifdef __cplusplus -} -#endif diff --git a/applications/external/doom/entities.c b/applications/external/doom/entities.c deleted file mode 100644 index 86c7f6ae1..000000000 --- a/applications/external/doom/entities.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "entities.h" - -//extern "C" -/*Player create_player(double x, double y){ - return {create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100, 0}; -}*/ - -Player create_player(double x, double y) { - Player p; - p.pos = create_coords((double)x + (double)0.5, (double)y + (double)0.5); - p.dir = create_coords(1, 0); - p.plane = create_coords(0, -0.66); - p.velocity = 0; - p.health = 100; - p.keys = 0; - return p; //{create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100, 0}; -} - -//extern "C" -Entity - create_entity(uint8_t type, uint8_t x, uint8_t y, uint8_t initialState, uint8_t initialHealth) { - UID uid = create_uid(type, x, y); - Coords pos = create_coords((double)x + (double).5, (double)y + (double).5); - Entity new_entity; // = { uid, pos, initialState, initialHealth, 0, 0 }; - new_entity.uid = uid; - new_entity.pos = pos; - new_entity.state = initialState; - new_entity.health = initialHealth; - new_entity.distance = 0; - new_entity.timer = 0; - return new_entity; -} - -//extern "C" -StaticEntity crate_static_entity(UID uid, uint8_t x, uint8_t y, bool active) { - StaticEntity ent; - ent.uid = uid; - ent.x = x; - ent.y = y; - ent.active = active; - return ent; -} \ No newline at end of file diff --git a/applications/external/doom/entities.h b/applications/external/doom/entities.h deleted file mode 100644 index ef5eb1a78..000000000 --- a/applications/external/doom/entities.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef _entities_h -#define _entities_h -#include -#include -#include "types.h" - -// Shortcuts -//#define create_player(x, y) {create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100} - -#define create_enemy(x, y) create_entity(E_ENEMY, x, y, S_STAND, 50) -#define create_medikit(x, y) create_entity(E_MEDIKIT, x, y, S_STAND, 0) -#define create_key(x, y) create_entity(E_KEY, x, y, S_STAND, 0) -#define create_fireball(x, y, dir) create_entity(E_FIREBALL, x, y, S_STAND, dir) -#define create_door(x, y) create_entity(E_DOOR, x, y, S_STAND, 0) - -// entity statuses -#define S_STAND 0 -#define S_ALERT 1 -#define S_FIRING 2 -#define S_MELEE 3 -#define S_HIT 4 -#define S_DEAD 5 -#define S_HIDDEN 6 -#define S_OPEN 7 -#define S_CLOSE 8 - -typedef struct Player { - Coords pos; - Coords dir; - Coords plane; - double velocity; - uint8_t health; - uint8_t keys; -} Player; - -typedef struct Entity { - UID uid; - Coords pos; - uint8_t state; - uint8_t health; // angle for fireballs - uint8_t distance; - uint8_t timer; -} Entity; - -typedef struct StaticEntity { - UID uid; - uint8_t x; - uint8_t y; - bool active; -} StaticEntity; - -Entity - create_entity(uint8_t type, uint8_t x, uint8_t y, uint8_t initialState, uint8_t initialHealth); -StaticEntity create_static_entity(UID uid, uint8_t x, uint8_t y, bool active); -Player create_player(double x, double y); -#endif diff --git a/applications/external/doom/img/1.png b/applications/external/doom/img/1.png deleted file mode 100644 index 1ecb073a69f3692705a6b5a76947d7ceb6dfab3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1487 zcmeAS@N?(olHy`uVBq!ia0y~yU;;8388|?c*QZDWAjMhW5n0T@z;^_M8K-LVNi#68 zHhQ`^hE&XXdpEZ4vK2!^pjDFPnts;}cGsHUNv|pqJ$dkF@&4j@XufgnYUZ5(uw}k>|NTEd%o!%JFerF2Ff1~=&hTLF*2b+r>I@HYK!kuYr=%Ey&zRmn z{V}~wkilUJ1A_}EkTPZPOU(Nf(Wjnnz(ht!Zjxu1erwnIzxR%Z2mY>#>_##fY5|mb z&G19ReQx=Cn_8|OpcA1kC7yc2e!%_aF8BX8`ww^FG$4lkz|ySecdze@yOzYlAfU>? z5XeLdm1@u6_i*30<3%=>%}72mWmq<2_P*(l&+{PSK_i=iXXRPb`oDkvet#^w|C{K2 z^f0gYIp5S&EI>K^{-jhoIo;n z-hrAa6MueRTm1k04fY#V4E|{d8=hJ*OfxUCpRa3toqf*7njMpn3?=mO6 z-~W1_!y4oSRmz}~`QG-I`c|}@-=njfUINCNluyfcg6EL4^Lj#zL0|OZYpoX@tf8A{+~Jj;ojfI{#nPj%N-Dw zK*UZYGed_FA|_D_msz)jD_+mjU8l=XVTbV3ij}4f+Hd#P|2p%_B{BE$y6SiGUuX3c z|C#^z)c@mc_s%P>XF5;>P3vvf7`~kozi;~7!f3zQ=IgIt{oZlzO}({o`OlTdKcAHO zUmE*!FH%B^dS=DYw{@%E=JLJl7T2a`o4FwpwR1Lua&eIIdG>@mH?LIpi1*e%!5?luH~HN2J?DHr z=X-9+r_mf|+I|`UI7dZ>odmG6zEZ$(8;-8V`B}j!Co)b5;PUJCL%HVb@(O@`d{o#+ zpNXoLrg>RY5niFAK?`}!%J<&T4HcbZd-?2n>2TnD8DFF>OiLIz@h;PoQ4_fNd`+iK zg7r-iMuk~q54?U9p!WX~?YO!Cae*xUUT}L8;DjBx8L%1NCP0UW@AYEoXJa)P~Q7phEag={jZfCBb`0W+=8F1uTS0ken2%i&!e9Gs`Y0Yt7OB4!#Zeuwn& zF0~8elq;?IeCg6N!HUMBh_z|*;_sMpC<4PVmM25y9-;Q1Tw}I$+crGWm&oFjvh@WT zZnFB!jU~3jf3;#=K#KUrj%?*%J~Z;hth!dE_VXQcw>{>=Tf$q;{w>{{Et+`iJKwF0 z(?k(-d@%k&VUx5!9zbp4sOb@E`O{NRD`nh70N<;guLst{h_CljNUY7aBN zCv)zv2R`WijCKtw=`?v}Fno@P@ zyjOwIVnasN304<#Q_4`ad2E=v9AN6oz%})@2Sjl+^&zGW?h}Uzmdu-EzI38EC7h$_ z#N|TqqWQpm!F8dbJtpQ$qUE_DxE|1=cjGY16rq z89qAv|Dg-@N@}r(~GK;}F|lEj6~VwKR_T{r4j+V+hiB=7pi;XwHa-#kc^T>@e&^j z_9G$qLrJaQlxV!dERY^o4oDp}x{>06uaN3fN*WBkxO43${i$Bl@1x_@`f6Np5G&$I zE$a{Q?6O53+hBI$wyg;Uh<4H{bPzZ`R>eQ7p;qZeDk=z}9?89XtC8DCjcYH;P4YcM zlx+B5jj0!F%F#YaEo&-p6*+)cUes&pa#8f->~(Xot~*GH2UB7zcXwgW$&o8eC*JwT Q)_)H~g-3_UKTbaPH)Y0Q@&Et; diff --git a/applications/external/doom/img/3.png b/applications/external/doom/img/3.png deleted file mode 100644 index b5aec03fdccb77f978d836ea80ecb1ee898154a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1629 zcmc&#drVVj6hDR04n$z0ggRHr%z_#6kY>CD#x`a}7la8+6bc!5%Vw1d_@cIedqf6V z!{!vSLZf>`2cw`t9gnskI>4?SnXz;XI!C37rMFN@OK%^$uFHD;Z~ytn&AB;W&iS45 zJHPXN^;wyW)qd;!0AO{-j`Vi{yzpNVJpaV(T+ENbjW1_MekDM_@RN`9RYbsx0OY`o z^fz)&2K#iSkW)pGW7UP^=bavvck}+Pru)+Ft(X;L+c_Mu0$hk3AP2y zF&_=p%uYT)!CL?qpM$WS&(zGB=O`fiiOF}`EYcdO)L}74(ELFa47Hm{a3dZ zd^e*eGQyqE9atchxFAp{wyGJo#9 z<4Gl~Qkv`t;%g)xwx?uS*$3lh_&$bVzyD-9mY??Rp{YHw6`D~hW<`9Hzx3qGd7o^Q zSAJ}$M#fw915x`iJzX@T<@;ayo7{xgui9N4V z*d^FH%`S!N8k5^OlVFJ`xZ^3}!%-?EkB$5>!gTe@LnVl(-yW@hG1{ijwNN@L1Ve={ zsl^&>Jw3c^AMYGNZsw9}DVJa1D!r1paU3lsw%V2Yk$1|p`}9+q_`qWF*EMwUi5Mu@ z?baR+Cu6YsrOqEP-#lr^dx^2wf^g4MsQGEe@p}VdEKP8inI~K|{OZ#U=~GsfAGOhyCe?+LaJAc`_OVu&O;@@ta?#e|So>S^skYHfqskCwDM-yKkR# z>b6d5wi0T(K$T59{%T*|z*ts|iEdSEP2~c8VQ+|1w^F2{i(5U4yc>&?(xpl~d@%Fs z@#&jFdsN5c7L|gaq%v~MmGIXVQPC(f+QzXN5!iVhN!9VRDUbHERE=F5ZR%;A oeZ;b47yXP5B_qyl1Pz$ulXAWbLp3j}@%I8U-p)+#e6#rU-{AE`xBvhE diff --git a/applications/external/doom/level.h b/applications/external/doom/level.h deleted file mode 100644 index 4d92a1cc6..000000000 --- a/applications/external/doom/level.h +++ /dev/null @@ -1,188 +0,0 @@ -#ifndef _level_h -#define _level_h - -#include "constants.h" - -/* - Based on E1M1 from Wolfenstein 3D - - ################################################################ - #############################...........######################## - ######....###################........E..######################## - ######....########..........#...........#...#################### - ######.....#######..........L.....E.......M.#################### - ######.....#######..........#...........#...#################### - ##################...########...........######################## - ######.........###...########...........######################## - ######.........###...#############D############################# - ######.........#......E##########...############################ - ######....E....D...E...##########...############################ - ######.........#.......##########...############################ - ######....E....##################...############################ - #...##.........##################...############################ - #.K.######D######################...############################ - #...#####...###############...#E.....K########################## - ##D######...###############..####...############################ - #...#####...###############..####...############################ - #...#...#...###############..####...############################ - #...D...#...#####################...############################ - #...#...#...#####################...############################ - #...######D#######################L############################# - #.E.##.........#################.....#################........## - #...##.........############...............############........## - #...##...E.....############...............############........## - #....#.........############...E.......E....#.........#........## - #....L....K....############................D....E....D....E...## - #....#.........############................#.........#........## - #...##.....E...############...............####....####........## - #...##.........############...............#####..#####.....M..## - #...##.........#################.....##########..#####........## - #...######L#######################D############..############### - #...#####...#####################...###########..############### - #E.E#####...#####################...###########..############### - #...#...#...#####################.E.###########..############### - #...D.M.#...#####################...###########..############### - #...#...#...#####################...###########..###.#.#.#.##### - #...#####...#####################...###########...#.........#### - #...#####...#####################...###########...D....E..K.#### - #................##......########...###########...#.........#### - #....E........E...L...E...X######...################.#.#.#.##### - #................##......########...############################ - #################################...############################ - #############..#..#..#############L############################# - ###########....#..#.########....#...#....####################### - #############.....##########.P..D...D....####################### - ############################....#...#....####################### - ##############..#################...############################ - ##############..############....#...#....####################### - ############################....D...D....####################### - ############################....#...#....####################### - #################################...############################ - ############################.............####################### - ############################..........EK.####################### - ############################.............####################### - ################################################################ -*/ - -/* - Same map above built from some regexp replacements using the legend above. - Using this way lets me use only 4 bit to store each block -*/ -const uint8_t sto_level_1[LEVEL_SIZE] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, - 0x00, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, - 0x00, 0x20, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x02, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x90, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF2, - 0x00, 0x00, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x4F, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0x40, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xF0, 0x00, 0xFF, 0x00, 0x02, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xF0, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x20, - 0x00, 0x00, 0x00, 0x20, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xF0, 0x00, 0x05, 0x00, 0x00, 0x90, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0xFF, - 0xF0, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x08, 0x00, 0xFF, - 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF2, 0x02, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0x40, 0x80, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x40, 0x00, 0x02, 0x00, 0x90, 0xFF, 0xFF, - 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, - 0xF0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, - 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x0F, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0xF0, 0x00, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, - 0x40, 0x00, 0x40, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0xF0, 0x00, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0xF0, 0x00, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0x40, 0x00, 0x40, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0xF0, 0x00, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x29, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -}; - -#endif diff --git a/applications/external/doom/sound.h b/applications/external/doom/sound.h deleted file mode 100644 index 514381334..000000000 --- a/applications/external/doom/sound.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef sound_h -#define sound_h -#include -#include -#include -#include "doom_music_player_worker.h" - -//static const char dspistol[] = "AnyConv:d=,o=,b=120:408,40p,40p,40p,40p,405,40p,40p,40p,405,30p.,30p.,30p.,13p"; -static const char dsintro[] = - "Doom:d=32,o=4,b=56:f,f,f5,f,f,d#5,f,f,c#5,f,f,b,f,f,c5,c#5,f,f,f5,f,f,d#5,f,f,c#5,f,f,8b.,f,f,f5,f,f,d#5,f,f,c#5,f,f,b,f,f,c5,c#5,f,f,f5,f,f,d#5,f,f,c#5,f,f,8b.,a#,a#,a#5,a#,a#,g#5,a#,a#,f#5,a#,a#,e5,a#,a#,f5,f#5,a#,a#,a#5,a#,a#,g#5,a#,a#,f#5,a#,a#,8e5"; -//static const char dsgetpow[] = "dsgetpow:d=,o=,b=120:407,40p,30.6,407,40p,406,40p,407,40p,40p,407,30p.,407"; -//static const char dsnoway[] = "dsnoway:d=,o=,b=120:407,30.4"; - -#define MUSIC_PLAYER_SEMITONE_HISTORY_SIZE 4 -static const float MUSIC_PLAYER_VOLUMES[] = {0, .25, .5, .75, 1}; - -typedef struct { - uint8_t semitone_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE]; - uint8_t duration_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE]; - - uint8_t volume; - uint8_t semitone; - uint8_t dots; - uint8_t duration; - float position; -} MusicPlayerModel; - -typedef struct { - MusicPlayerModel* model; - MusicPlayerWorker* worker; - FuriMutex** model_mutex; -} MusicPlayer; - -#endif \ No newline at end of file diff --git a/applications/external/doom/types.c b/applications/external/doom/types.c deleted file mode 100644 index 6b55d56a7..000000000 --- a/applications/external/doom/types.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "types.h" - -/*template -inline T sq(T value) { - return value * value; -}*/ - -double sq(double val) { - return val * val; -} - -//extern "C" -Coords create_coords(double x, double y) { - Coords cord; - cord.x = x; - cord.y = y; - return cord; -} - -//extern "C" -uint8_t coords_distance(Coords* a, Coords* b) { - return sqrt(sq(a->x - b->x) + sq(a->y - b->y)) * 20; -} - -//extern "C" -UID create_uid(uint8_t type, uint8_t x, uint8_t y) { - return ((y << 6) | x) << 4 | type; -} - -//extern "C" -uint8_t uid_get_type(UID uid) { - return uid & 0x0F; -} diff --git a/applications/external/doom/types.h b/applications/external/doom/types.h deleted file mode 100644 index b8579f645..000000000 --- a/applications/external/doom/types.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef _types_h -#define _types_h - -#include -#include -//#include "constants.h" - -#define UID_null 0 - -// Entity types (legend applies to level.h) -#define E_FLOOR 0x0 // . (also null) -#define E_WALL 0xF // # -#define E_PLAYER 0x1 // P -#define E_ENEMY 0x2 // E -#define E_DOOR 0x4 // D -#define E_LOCKEDDOOR 0x5 // L -#define E_EXIT 0x7 // X -// collectable entities >= 0x8 -#define E_MEDIKIT 0x8 // M -#define E_KEY 0x9 // K -#define E_FIREBALL 0xA // not in map - -typedef uint16_t UID; -typedef uint8_t EType; - -typedef struct Coords { - double x; - double y; -} Coords; - -UID create_uid(EType type, uint8_t x, uint8_t y); -EType uid_get_type(UID uid); -Coords create_coords(double x, double y); -uint8_t coords_distance(Coords* a, Coords* b); - -#endif diff --git a/applications/external/dtmf_dolphin/LICENSE b/applications/external/dtmf_dolphin/LICENSE deleted file mode 100644 index f288702d2..000000000 --- a/applications/external/dtmf_dolphin/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/applications/external/dtmf_dolphin/README.md b/applications/external/dtmf_dolphin/README.md deleted file mode 100644 index 9a65a382a..000000000 --- a/applications/external/dtmf_dolphin/README.md +++ /dev/null @@ -1,18 +0,0 @@ -![Image](pics/dialer.jpg) - -[Original Link](https://github.com/litui/dtmf_dolphin) - -## DTMF Dolphin - -DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox. - -Now in a release-ready state for both Dialer, Bluebox, and Redbox (US/UK) functionality! - -Please note that using the current tone output method, the 2600 tone is scaled about 33 Hz higher than it should be. This is a limitation of the current sample rate. - -### Educational Links: - -* http://www.phrack.org/issues/25/7.html#article -* https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling -* https://en.wikipedia.org/wiki/Blue_box -* https://en.wikipedia.org/wiki/Red_box_(phreaking) diff --git a/applications/external/dtmf_dolphin/application.fam b/applications/external/dtmf_dolphin/application.fam deleted file mode 100644 index 5f01bc9a0..000000000 --- a/applications/external/dtmf_dolphin/application.fam +++ /dev/null @@ -1,18 +0,0 @@ -App( - appid="dtmf_dolphin", - name="DTMF Dolphin", - apptype=FlipperAppType.EXTERNAL, - entry_point="dtmf_dolphin_app", - requires=[ - "storage", - "gui", - "dialogs", - ], - fap_icon="phone.png", - stack_size=8 * 1024, - order=20, - fap_category="Tools", - fap_author="@litui & @xMasterX", - fap_version="1.0", - fap_description="DTMF (Dual-Tone Multi-Frequency) dialer, Bluebox, and Redbox.", -) diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin.c b/applications/external/dtmf_dolphin/dtmf_dolphin.c deleted file mode 100644 index c1b10defa..000000000 --- a/applications/external/dtmf_dolphin/dtmf_dolphin.c +++ /dev/null @@ -1,89 +0,0 @@ -#include "dtmf_dolphin_i.h" - -#include -#include - -static bool dtmf_dolphin_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - DTMFDolphinApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool dtmf_dolphin_app_back_event_callback(void* context) { - furi_assert(context); - DTMFDolphinApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void dtmf_dolphin_app_tick_event_callback(void* context) { - furi_assert(context); - DTMFDolphinApp* app = context; - - scene_manager_handle_tick_event(app->scene_manager); -} - -static DTMFDolphinApp* app_alloc() { - DTMFDolphinApp* app = malloc(sizeof(DTMFDolphinApp)); - - app->gui = furi_record_open(RECORD_GUI); - app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&dtmf_dolphin_scene_handlers, app); - view_dispatcher_enable_queue(app->view_dispatcher); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, dtmf_dolphin_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, dtmf_dolphin_app_back_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, dtmf_dolphin_app_tick_event_callback, 100); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - app->main_menu_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - DTMFDolphinViewMainMenu, - variable_item_list_get_view(app->main_menu_list)); - - app->dtmf_dolphin_dialer = dtmf_dolphin_dialer_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - DTMFDolphinViewDialer, - dtmf_dolphin_dialer_get_view(app->dtmf_dolphin_dialer)); - - app->notification = furi_record_open(RECORD_NOTIFICATION); - notification_message(app->notification, &sequence_display_backlight_enforce_on); - - scene_manager_next_scene(app->scene_manager, DTMFDolphinSceneStart); - - return app; -} - -static void app_free(DTMFDolphinApp* app) { - furi_assert(app); - view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewMainMenu); - view_dispatcher_remove_view(app->view_dispatcher, DTMFDolphinViewDialer); - variable_item_list_free(app->main_menu_list); - - dtmf_dolphin_dialer_free(app->dtmf_dolphin_dialer); - - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - notification_message(app->notification, &sequence_display_backlight_enforce_auto); - - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - free(app); -} - -int32_t dtmf_dolphin_app(void* p) { - UNUSED(p); - DTMFDolphinApp* app = app_alloc(); - - view_dispatcher_run(app->view_dispatcher); - - app_free(app); - return 0; -} \ No newline at end of file diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin_audio.c b/applications/external/dtmf_dolphin/dtmf_dolphin_audio.c deleted file mode 100644 index 66d17bc2e..000000000 --- a/applications/external/dtmf_dolphin/dtmf_dolphin_audio.c +++ /dev/null @@ -1,270 +0,0 @@ -#include "dtmf_dolphin_audio.h" - -DTMFDolphinAudio* current_player; - -static void dtmf_dolphin_audio_dma_isr(void* ctx) { - FuriMessageQueue* event_queue = ctx; - - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); - - DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAHalfTransfer}; - furi_message_queue_put(event_queue, &event, 0); - } - - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); - - DTMFDolphinCustomEvent event = {.type = DTMFDolphinEventDMAFullTransfer}; - furi_message_queue_put(event_queue, &event, 0); - } -} - -void dtmf_dolphin_audio_clear_samples(DTMFDolphinAudio* player) { - for(size_t i = 0; i < player->buffer_length; i++) { - player->sample_buffer[i] = 0; - } -} - -DTMFDolphinOsc* dtmf_dolphin_osc_alloc() { - DTMFDolphinOsc* osc = malloc(sizeof(DTMFDolphinOsc)); - osc->cached_freq = 0; - osc->offset = 0; - osc->period = 0; - osc->lookup_table = NULL; - return osc; -} - -DTMFDolphinPulseFilter* dtmf_dolphin_pulse_filter_alloc() { - DTMFDolphinPulseFilter* pf = malloc(sizeof(DTMFDolphinPulseFilter)); - pf->duration = 0; - pf->period = 0; - pf->offset = 0; - pf->lookup_table = NULL; - return pf; -} - -DTMFDolphinAudio* dtmf_dolphin_audio_alloc() { - DTMFDolphinAudio* player = malloc(sizeof(DTMFDolphinAudio)); - player->buffer_length = SAMPLE_BUFFER_LENGTH; - player->half_buffer_length = SAMPLE_BUFFER_LENGTH / 2; - player->sample_buffer = malloc(sizeof(uint16_t) * player->buffer_length); - player->osc1 = dtmf_dolphin_osc_alloc(); - player->osc2 = dtmf_dolphin_osc_alloc(); - player->volume = 1.0f; - player->queue = furi_message_queue_alloc(10, sizeof(DTMFDolphinCustomEvent)); - player->filter = dtmf_dolphin_pulse_filter_alloc(); - player->playing = false; - dtmf_dolphin_audio_clear_samples(player); - - return player; -} - -size_t calc_waveform_period(float freq) { - if(!freq) { - return 0; - } - // DMA Rate calculation, thanks to Dr_Zlo - float dma_rate = CPU_CLOCK_FREQ / 2 / DTMF_DOLPHIN_HAL_DMA_PRESCALER / - (DTMF_DOLPHIN_HAL_DMA_AUTORELOAD + 1); - - // Using a constant scaling modifier, which likely represents - // the combined system overhead and isr latency. - return (uint16_t)dma_rate * 2 / freq * 0.801923; -} - -void osc_generate_lookup_table(DTMFDolphinOsc* osc, float freq) { - if(osc->lookup_table != NULL) { - free(osc->lookup_table); - } - osc->offset = 0; - osc->cached_freq = freq; - osc->period = calc_waveform_period(freq); - if(!osc->period) { - osc->lookup_table = NULL; - return; - } - osc->lookup_table = malloc(sizeof(float) * osc->period); - - for(size_t i = 0; i < osc->period; i++) { - osc->lookup_table[i] = sin(i * PERIOD_2_PI / osc->period) + 1; - } -} - -void filter_generate_lookup_table( - DTMFDolphinPulseFilter* pf, - uint16_t pulses, - uint16_t pulse_ms, - uint16_t gap_ms) { - if(pf->lookup_table != NULL) { - free(pf->lookup_table); - } - pf->offset = 0; - - uint16_t gap_period = calc_waveform_period(1000 / (float)gap_ms); - uint16_t pulse_period = calc_waveform_period(1000 / (float)pulse_ms); - pf->period = pulse_period + gap_period; - - if(!pf->period) { - pf->lookup_table = NULL; - return; - } - pf->duration = pf->period * pulses; - pf->lookup_table = malloc(sizeof(bool) * pf->duration); - - for(size_t i = 0; i < pf->duration; i++) { - pf->lookup_table[i] = i % pf->period < pulse_period; - } -} - -float sample_frame(DTMFDolphinOsc* osc) { - float frame = 0.0; - - if(osc->period) { - frame = osc->lookup_table[osc->offset]; - osc->offset = (osc->offset + 1) % osc->period; - } - - return frame; -} - -bool sample_filter(DTMFDolphinPulseFilter* pf) { - bool frame = true; - - if(pf->duration) { - if(pf->offset < pf->duration) { - frame = pf->lookup_table[pf->offset]; - pf->offset = pf->offset + 1; - } else { - frame = false; - } - } - - return frame; -} - -void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc) { - if(osc->lookup_table != NULL) { - free(osc->lookup_table); - } - free(osc); -} - -void dtmf_dolphin_filter_free(DTMFDolphinPulseFilter* pf) { - if(pf->lookup_table != NULL) { - free(pf->lookup_table); - } - free(pf); -} - -void dtmf_dolphin_audio_free(DTMFDolphinAudio* player) { - furi_message_queue_free(player->queue); - dtmf_dolphin_osc_free(player->osc1); - dtmf_dolphin_osc_free(player->osc2); - dtmf_dolphin_filter_free(player->filter); - free(player->sample_buffer); - free(player); - current_player = NULL; -} - -bool generate_waveform(DTMFDolphinAudio* player, uint16_t buffer_index) { - uint16_t* sample_buffer_start = &player->sample_buffer[buffer_index]; - - for(size_t i = 0; i < player->half_buffer_length; i++) { - float data = 0; - if(player->osc2->period) { - data = (sample_frame(player->osc1) / 2) + (sample_frame(player->osc2) / 2); - } else { - data = (sample_frame(player->osc1)); - } - data *= sample_filter(player->filter) ? player->volume : 0.0; - data *= UINT8_MAX / 2; // scale -128..127 - data += UINT8_MAX / 2; // to unsigned - - if(data < 0) { - data = 0; - } - - if(data > 255) { - data = 255; - } - - sample_buffer_start[i] = data; - } - - return true; -} - -bool dtmf_dolphin_audio_play_tones( - float freq1, - float freq2, - uint16_t pulses, - uint16_t pulse_ms, - uint16_t gap_ms) { - if(current_player != NULL && current_player->playing) { - // Cannot start playing while still playing something else - return false; - } - current_player = dtmf_dolphin_audio_alloc(); - - osc_generate_lookup_table(current_player->osc1, freq1); - osc_generate_lookup_table(current_player->osc2, freq2); - filter_generate_lookup_table(current_player->filter, pulses, pulse_ms, gap_ms); - - generate_waveform(current_player, 0); - generate_waveform(current_player, current_player->half_buffer_length); - - dtmf_dolphin_dma_init((uint32_t)current_player->sample_buffer, current_player->buffer_length); - - furi_hal_interrupt_set_isr( - FuriHalInterruptIdDma1Ch1, dtmf_dolphin_audio_dma_isr, current_player->queue); - if(furi_hal_speaker_acquire(1000)) { - dtmf_dolphin_speaker_init(); - dtmf_dolphin_dma_start(); - dtmf_dolphin_speaker_start(); - current_player->playing = true; - return true; - } else { - current_player->playing = false; - return false; - } -} - -bool dtmf_dolphin_audio_stop_tones() { - if(current_player != NULL && !current_player->playing) { - // Can't stop a player that isn't playing. - return false; - } - while(current_player->filter->offset > 0 && - current_player->filter->offset < current_player->filter->duration) { - // run remaining ticks if needed to complete filter sequence - dtmf_dolphin_audio_handle_tick(); - } - dtmf_dolphin_speaker_stop(); - dtmf_dolphin_dma_stop(); - furi_hal_speaker_release(); - - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - - dtmf_dolphin_audio_free(current_player); - - return true; -} - -bool dtmf_dolphin_audio_handle_tick() { - bool handled = false; - - if(current_player) { - DTMFDolphinCustomEvent event; - if(furi_message_queue_get(current_player->queue, &event, 250) == FuriStatusOk) { - if(event.type == DTMFDolphinEventDMAHalfTransfer) { - generate_waveform(current_player, 0); - handled = true; - } else if(event.type == DTMFDolphinEventDMAFullTransfer) { - generate_waveform(current_player, current_player->half_buffer_length); - handled = true; - } - } - } - return handled; -} \ No newline at end of file diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin_audio.h b/applications/external/dtmf_dolphin/dtmf_dolphin_audio.h deleted file mode 100644 index 2dd1d6eb6..000000000 --- a/applications/external/dtmf_dolphin/dtmf_dolphin_audio.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -// #include "dtmf_dolphin_i.h" -#include "dtmf_dolphin_event.h" -#include "dtmf_dolphin_hal.h" - -#define SAMPLE_BUFFER_LENGTH 8192 -#define PERIOD_2_PI 6.2832 -#define CPU_CLOCK_FREQ 64000000 - -typedef struct { - float cached_freq; - size_t period; - float* lookup_table; - uint16_t offset; -} DTMFDolphinOsc; - -typedef struct { - float duration; - size_t period; - bool* lookup_table; - uint16_t offset; -} DTMFDolphinPulseFilter; - -typedef struct { - size_t buffer_length; - size_t half_buffer_length; - uint8_t* buffer_buffer; - uint16_t* sample_buffer; - float volume; - FuriMessageQueue* queue; - DTMFDolphinOsc* osc1; - DTMFDolphinOsc* osc2; - DTMFDolphinPulseFilter* filter; - bool playing; -} DTMFDolphinAudio; - -DTMFDolphinOsc* dtmf_dolphin_osc_alloc(); - -DTMFDolphinAudio* dtmf_dolphin_audio_alloc(); - -void dtmf_dolphin_audio_free(DTMFDolphinAudio* player); - -void dtmf_dolphin_osc_free(DTMFDolphinOsc* osc); - -bool dtmf_dolphin_audio_play_tones( - float freq1, - float freq2, - uint16_t pulses, - uint16_t pulse_ms, - uint16_t gap_ms); - -bool dtmf_dolphin_audio_stop_tones(); - -bool dtmf_dolphin_audio_handle_tick(); diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin_data.c b/applications/external/dtmf_dolphin/dtmf_dolphin_data.c deleted file mode 100644 index 72386b83d..000000000 --- a/applications/external/dtmf_dolphin/dtmf_dolphin_data.c +++ /dev/null @@ -1,220 +0,0 @@ -#include "dtmf_dolphin_data.h" - -typedef struct { - const uint8_t row; - const uint8_t col; - const uint8_t span; -} DTMFDolphinTonePos; - -typedef struct { - const char* name; - const float frequency_1; - const float frequency_2; - const DTMFDolphinTonePos pos; - const uint16_t pulses; // for Redbox - const uint16_t pulse_ms; // for Redbox - const uint16_t gap_duration; // for Redbox -} DTMFDolphinTones; - -typedef struct { - const char* name; - DTMFDolphinToneSection block; - uint8_t tone_count; - DTMFDolphinTones tones[DTMF_DOLPHIN_MAX_TONE_COUNT]; -} DTMFDolphinSceneData; - -DTMFDolphinSceneData DTMFDolphinSceneDataDialer = { - .name = "Dialer", - .block = DTMF_DOLPHIN_TONE_BLOCK_DIALER, - .tone_count = 16, - .tones = { - {"1", 697.0, 1209.0, {0, 0, 1}, 0, 0, 0}, - {"2", 697.0, 1336.0, {0, 1, 1}, 0, 0, 0}, - {"3", 697.0, 1477.0, {0, 2, 1}, 0, 0, 0}, - {"A", 697.0, 1633.0, {0, 3, 1}, 0, 0, 0}, - {"4", 770.0, 1209.0, {1, 0, 1}, 0, 0, 0}, - {"5", 770.0, 1336.0, {1, 1, 1}, 0, 0, 0}, - {"6", 770.0, 1477.0, {1, 2, 1}, 0, 0, 0}, - {"B", 770.0, 1633.0, {1, 3, 1}, 0, 0, 0}, - {"7", 852.0, 1209.0, {2, 0, 1}, 0, 0, 0}, - {"8", 852.0, 1336.0, {2, 1, 1}, 0, 0, 0}, - {"9", 852.0, 1477.0, {2, 2, 1}, 0, 0, 0}, - {"C", 852.0, 1633.0, {2, 3, 1}, 0, 0, 0}, - {"*", 941.0, 1209.0, {3, 0, 1}, 0, 0, 0}, - {"0", 941.0, 1336.0, {3, 1, 1}, 0, 0, 0}, - {"#", 941.0, 1477.0, {3, 2, 1}, 0, 0, 0}, - {"D", 941.0, 1633.0, {3, 3, 1}, 0, 0, 0}, - }}; - -DTMFDolphinSceneData DTMFDolphinSceneDataBluebox = { - .name = "Bluebox", - .block = DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX, - .tone_count = 13, - .tones = { - {"1", 700.0, 900.0, {0, 0, 1}, 0, 0, 0}, - {"2", 700.0, 1100.0, {0, 1, 1}, 0, 0, 0}, - {"3", 900.0, 1100.0, {0, 2, 1}, 0, 0, 0}, - {"4", 700.0, 1300.0, {1, 0, 1}, 0, 0, 0}, - {"5", 900.0, 1300.0, {1, 1, 1}, 0, 0, 0}, - {"6", 1100.0, 1300.0, {1, 2, 1}, 0, 0, 0}, - {"7", 700.0, 1500.0, {2, 0, 1}, 0, 0, 0}, - {"8", 900.0, 1500.0, {2, 1, 1}, 0, 0, 0}, - {"9", 1100.0, 1500.0, {2, 2, 1}, 0, 0, 0}, - {"0", 1300.0, 1500.0, {3, 1, 1}, 0, 0, 0}, - {"KP", 1100.0, 1700.0, {0, 3, 2}, 0, 0, 0}, - {"ST", 1500.0, 1700.0, {1, 3, 2}, 0, 0, 0}, - {"2600", 2600.0, 0.0, {3, 2, 3}, 0, 0, 0}, - }}; - -DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUS = { - .name = "Redbox (US)", - .block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US, - .tone_count = 4, - .tones = { - {"Nickel", 1700.0, 2200.0, {0, 0, 5}, 1, 66, 0}, - {"Dime", 1700.0, 2200.0, {1, 0, 5}, 2, 66, 66}, - {"Quarter", 1700.0, 2200.0, {2, 0, 5}, 5, 33, 33}, - {"Dollar", 1700.0, 2200.0, {3, 0, 5}, 1, 650, 0}, - }}; - -DTMFDolphinSceneData DTMFDolphinSceneDataRedboxCA = { - .name = "Redbox (CA)", - .block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA, - .tone_count = 3, - .tones = { - {"Nickel", 2200.0, 0.0, {0, 0, 5}, 1, 66, 0}, - {"Dime", 2200.0, 0.0, {1, 0, 5}, 2, 66, 66}, - {"Quarter", 2200.0, 0.0, {2, 0, 5}, 5, 33, 33}, - }}; - -DTMFDolphinSceneData DTMFDolphinSceneDataRedboxUK = { - .name = "Redbox (UK)", - .block = DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK, - .tone_count = 2, - .tones = { - {"10p", 1000.0, 0.0, {0, 0, 5}, 1, 200, 0}, - {"50p", 1000.0, 0.0, {1, 0, 5}, 1, 350, 0}, - }}; - -DTMFDolphinSceneData DTMFDolphinSceneDataMisc = { - .name = "Misc", - .block = DTMF_DOLPHIN_TONE_BLOCK_MISC, - .tone_count = 3, - .tones = { - {"CCITT 11", 700.0, 1700.0, {0, 0, 5}, 0, 0, 0}, - {"CCITT 12", 900.0, 1700.0, {1, 0, 5}, 0, 0, 0}, - {"CCITT KP2", 1300.0, 1700.0, {2, 0, 5}, 0, 0, 0}, - }}; - -DTMFDolphinToneSection current_section; -DTMFDolphinSceneData* current_scene_data; - -void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section) { - current_section = section; - - switch(current_section) { - case DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX: - current_scene_data = &DTMFDolphinSceneDataBluebox; - break; - case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US: - current_scene_data = &DTMFDolphinSceneDataRedboxUS; - break; - case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA: - current_scene_data = &DTMFDolphinSceneDataRedboxCA; - break; - case DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK: - current_scene_data = &DTMFDolphinSceneDataRedboxUK; - break; - case DTMF_DOLPHIN_TONE_BLOCK_MISC: - current_scene_data = &DTMFDolphinSceneDataMisc; - break; - default: // DTMF_DOLPHIN_TONE_BLOCK_DIALER: - current_scene_data = &DTMFDolphinSceneDataDialer; - break; - } -} - -DTMFDolphinToneSection dtmf_dolphin_data_get_current_section() { - return current_section; -} - -DTMFDolphinSceneData* dtmf_dolphin_data_get_current_scene_data() { - return current_scene_data; -} - -bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col) { - for(size_t i = 0; i < current_scene_data->tone_count; i++) { - DTMFDolphinTones tones = current_scene_data->tones[i]; - if(tones.pos.row == row && tones.pos.col == col) { - freq1[0] = tones.frequency_1; - freq2[0] = tones.frequency_2; - return true; - } - } - return false; -} - -bool dtmf_dolphin_data_get_filter_data( - uint16_t* pulses, - uint16_t* pulse_ms, - uint16_t* gap_ms, - uint8_t row, - uint8_t col) { - for(size_t i = 0; i < current_scene_data->tone_count; i++) { - DTMFDolphinTones tones = current_scene_data->tones[i]; - if(tones.pos.row == row && tones.pos.col == col) { - pulses[0] = tones.pulses; - pulse_ms[0] = tones.pulse_ms; - gap_ms[0] = tones.gap_duration; - return true; - } - } - return false; -} - -const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col) { - for(size_t i = 0; i < current_scene_data->tone_count; i++) { - DTMFDolphinTones tones = current_scene_data->tones[i]; - if(tones.pos.row == row && tones.pos.col == col) { - return tones.name; - } - } - return NULL; -} - -const char* dtmf_dolphin_data_get_current_section_name() { - if(current_scene_data) { - return current_scene_data->name; - } - return NULL; -} - -void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span) { - max_rows[0] = 0; - max_cols[0] = 0; - max_span[0] = 0; - uint8_t tmp_rowspan[5] = {0, 0, 0, 0, 0}; - for(size_t i = 0; i < current_scene_data->tone_count; i++) { - DTMFDolphinTones tones = current_scene_data->tones[i]; - if(tones.pos.row > max_rows[0]) { - max_rows[0] = tones.pos.row; - } - if(tones.pos.col > max_cols[0]) { - max_cols[0] = tones.pos.col; - } - tmp_rowspan[tones.pos.row] += tones.pos.span; - if(tmp_rowspan[tones.pos.row] > max_span[0]) max_span[0] = tmp_rowspan[tones.pos.row]; - } - max_rows[0]++; - max_cols[0]++; -} - -uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col) { - for(size_t i = 0; i < current_scene_data->tone_count; i++) { - DTMFDolphinTones tones = current_scene_data->tones[i]; - if(tones.pos.row == row && tones.pos.col == col) { - return tones.pos.span; - } - } - return 0; -} diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin_data.h b/applications/external/dtmf_dolphin/dtmf_dolphin_data.h deleted file mode 100644 index 56ceaf03d..000000000 --- a/applications/external/dtmf_dolphin/dtmf_dolphin_data.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include -#include -#include - -#define DTMF_DOLPHIN_MAX_TONE_COUNT 16 - -typedef enum { - DTMF_DOLPHIN_TONE_BLOCK_DIALER, - DTMF_DOLPHIN_TONE_BLOCK_BLUEBOX, - DTMF_DOLPHIN_TONE_BLOCK_REDBOX_US, - DTMF_DOLPHIN_TONE_BLOCK_REDBOX_UK, - DTMF_DOLPHIN_TONE_BLOCK_REDBOX_CA, - DTMF_DOLPHIN_TONE_BLOCK_MISC, -} DTMFDolphinToneSection; - -void dtmf_dolphin_data_set_current_section(DTMFDolphinToneSection section); - -DTMFDolphinToneSection dtmf_dolphin_data_get_current_section(); - -bool dtmf_dolphin_data_get_tone_frequencies(float* freq1, float* freq2, uint8_t row, uint8_t col); - -bool dtmf_dolphin_data_get_filter_data( - uint16_t* pulses, - uint16_t* pulse_ms, - uint16_t* gap_ms, - uint8_t row, - uint8_t col); - -const char* dtmf_dolphin_data_get_tone_name(uint8_t row, uint8_t col); - -const char* dtmf_dolphin_data_get_current_section_name(); - -void dtmf_dolphin_tone_get_max_pos(uint8_t* max_rows, uint8_t* max_cols, uint8_t* max_span); - -uint8_t dtmf_dolphin_get_tone_span(uint8_t row, uint8_t col); \ No newline at end of file diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin_event.h b/applications/external/dtmf_dolphin/dtmf_dolphin_event.h deleted file mode 100644 index 525d0eb04..000000000 --- a/applications/external/dtmf_dolphin/dtmf_dolphin_event.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -typedef enum { - DTMFDolphinEventVolumeUp = 0, - DTMFDolphinEventVolumeDown, - DTMFDolphinDialerOkCB, - DTMFDolphinEventStartDialer, - DTMFDolphinEventStartBluebox, - DTMFDolphinEventStartRedboxUS, - DTMFDolphinEventStartRedboxUK, - DTMFDolphinEventStartRedboxCA, - DTMFDolphinEventStartMisc, - DTMFDolphinEventPlayTones, - DTMFDolphinEventStopTones, - DTMFDolphinEventDMAHalfTransfer, - DTMFDolphinEventDMAFullTransfer, -} DTMFDolphinEvent; - -typedef struct { - DTMFDolphinEvent type; -} DTMFDolphinCustomEvent; \ No newline at end of file diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin_hal.c b/applications/external/dtmf_dolphin/dtmf_dolphin_hal.c deleted file mode 100644 index b556c7145..000000000 --- a/applications/external/dtmf_dolphin/dtmf_dolphin_hal.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "dtmf_dolphin_hal.h" - -void dtmf_dolphin_speaker_init() { - LL_TIM_InitTypeDef TIM_InitStruct = {0}; - TIM_InitStruct.Prescaler = DTMF_DOLPHIN_HAL_DMA_PRESCALER; - TIM_InitStruct.Autoreload = DTMF_DOLPHIN_HAL_DMA_AUTORELOAD; - LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct); - - LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0}; - TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1; - TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE; - TIM_OC_InitStruct.CompareValue = 127; - LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct); -} - -void dtmf_dolphin_speaker_start() { - LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER); - LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER); -} - -void dtmf_dolphin_speaker_stop() { - LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER); - LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER); -} - -void dtmf_dolphin_dma_init(uint32_t address, size_t size) { - uint32_t dma_dst = (uint32_t) & (FURI_HAL_SPEAKER_TIMER->CCR1); - - LL_DMA_ConfigAddresses(DMA_INSTANCE, address, dma_dst, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); - LL_DMA_SetDataLength(DMA_INSTANCE, size); - - LL_DMA_SetPeriphRequest(DMA_INSTANCE, LL_DMAMUX_REQ_TIM16_UP); - LL_DMA_SetDataTransferDirection(DMA_INSTANCE, LL_DMA_DIRECTION_MEMORY_TO_PERIPH); - LL_DMA_SetChannelPriorityLevel(DMA_INSTANCE, LL_DMA_PRIORITY_VERYHIGH); - LL_DMA_SetMode(DMA_INSTANCE, LL_DMA_MODE_CIRCULAR); - LL_DMA_SetPeriphIncMode(DMA_INSTANCE, LL_DMA_PERIPH_NOINCREMENT); - LL_DMA_SetMemoryIncMode(DMA_INSTANCE, LL_DMA_MEMORY_INCREMENT); - LL_DMA_SetPeriphSize(DMA_INSTANCE, LL_DMA_PDATAALIGN_HALFWORD); - LL_DMA_SetMemorySize(DMA_INSTANCE, LL_DMA_MDATAALIGN_HALFWORD); - - LL_DMA_EnableIT_TC(DMA_INSTANCE); - LL_DMA_EnableIT_HT(DMA_INSTANCE); -} - -void dtmf_dolphin_dma_start() { - LL_DMA_EnableChannel(DMA_INSTANCE); - LL_TIM_EnableDMAReq_UPDATE(FURI_HAL_SPEAKER_TIMER); -} - -void dtmf_dolphin_dma_stop() { - LL_DMA_DisableChannel(DMA_INSTANCE); -} diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin_hal.h b/applications/external/dtmf_dolphin/dtmf_dolphin_hal.h deleted file mode 100644 index 5b426f6a0..000000000 --- a/applications/external/dtmf_dolphin/dtmf_dolphin_hal.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -#define FURI_HAL_SPEAKER_TIMER TIM16 -#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1 -#define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1 - -#define DTMF_DOLPHIN_HAL_DMA_PRESCALER 4 -#define DTMF_DOLPHIN_HAL_DMA_AUTORELOAD 255 - -#ifdef __cplusplus -extern "C" { -#endif - -void dtmf_dolphin_speaker_init(); - -void dtmf_dolphin_speaker_start(); - -void dtmf_dolphin_speaker_stop(); - -void dtmf_dolphin_dma_init(uint32_t address, size_t size); - -void dtmf_dolphin_dma_start(); - -void dtmf_dolphin_dma_stop(); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/external/dtmf_dolphin/dtmf_dolphin_i.h b/applications/external/dtmf_dolphin/dtmf_dolphin_i.h deleted file mode 100644 index f8ae1530f..000000000 --- a/applications/external/dtmf_dolphin/dtmf_dolphin_i.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "scenes/dtmf_dolphin_scene.h" - -#include -#include -#include -// #include -// #include -#include -#include -#include - -#include "dtmf_dolphin_event.h" - -#include "views/dtmf_dolphin_dialer.h" - -#define TAG "DTMFDolphin" - -enum DTMFDolphinSceneState { - DTMFDolphinSceneStateDialer, - DTMFDolphinSceneStateBluebox, - DTMFDolphinSceneStateRedboxUS, - DTMFDolphinSceneStateRedboxUK, - DTMFDolphinSceneStateRedboxCA, - DTMFDolphinSceneStateMisc, -}; - -typedef struct { - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - VariableItemList* main_menu_list; - DTMFDolphinDialer* dtmf_dolphin_dialer; - - Gui* gui; - // ButtonPanel* dialer_button_panel; - // ButtonPanel* bluebox_button_panel; - // ButtonPanel* redbox_button_panel; - NotificationApp* notification; -} DTMFDolphinApp; - -typedef enum { DTMFDolphinViewMainMenu, DTMFDolphinViewDialer } DTMFDolphinView; diff --git a/applications/external/dtmf_dolphin/phone.png b/applications/external/dtmf_dolphin/phone.png deleted file mode 100644 index 443f847c3cd72b263c4d8623101af5acd4d77439..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 306 zcmex=8Ac{%7FISvA$DN~4iQmBB~Ed}|F;-8fUaY(XGouZ zBf~@1e5J|3&)YmgL!L~TeDjMMUzhGd<;|fRWzCJ-RyAxjN^^T|$l$=2EUj{mQ=4m( zRoB#2OP30+`cfM*&o1&H-&RM~9SN344yc_Ibxtw=JgJNKKZ9T5H<5qtNlE+Gw9S3n jvwM5BaK@lmUQY?UoK#&APL?Tr{kdhz}LJ<)K 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�j7 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 JsUcvo4VzQn6OKr!9th_=;EZ{-@rUj1x}u$; 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�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 z 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

%N=vm=ta(4njEHP|w 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 MDnl1 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.me3-kn$<=aY_jSJHB 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

    jLEy){kWlpY)z$uN;dZjg=?y97JYOmcNcl@ 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%0D~$T%{m2ZmB&nc>65u8r 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
    m;M(gJrn6Gy-^kzC1 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 z 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�X#B9u2r`xG??HPvEGPG_Qu`;;h!}czqk$-O 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

    xovf7*Oufq$ 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

    9Sh+XR$UvWcV$n7p@WlI&1yqXw1 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 z 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

    -!Z*GAUD~iT6KjvwblfIa$cqCi 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∋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

    4cgm&~2}DS$FJH`C9m# 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 z z+GHcnT?8~a&sq>H@EW7=y4nlLs1OSuR10Bl`2DHs zs?VH`YcZJlEQunkQ<^0G=kfwGP%-GxzO~qk$@aYt72G(v>L~xEtZ40eyY{~hm&b&h zHqhwy4<{>kB(nS04oX+e4Q-3}k-D%@&bwapy~#;6_1WL>~YV+)G(dN3Dt z1{;czmvx!0e*l3bh5AEYG~F77DT~YJHcyy#Ajn&PI}J2_xCAK>ybdwFT`znePjdMW z6yRsl7hVn3V(Re5Zxp#2KXZ`()2C{>IC>BvzxndEWLm%{Zqa$k8JJ_aRI>5zL@4R2N4>{QteX+P~LTGgB zs?gmeFjo^Qt6usV=8Ngw9|sJL|C*hPE>5o=e)%#WQiTXeg3EYYxqmc5WH%3XrKQD( z+j$m@eoISkNsmvJ8&w{ED6KO#YFt}9rB=;GMGukswi``lJ zkns0>P{e5-YPnstinj4iq$RP zFCuSRRksH(QjXjy*XNU_B^o;W0Ss>G+XQEyysKL9<&nxGG92CV@9*K#+mb+ za8GWh6VztE5XUo@=ClSZ&;(I2_4 z*3C)XddEu})cGVjJc1Ftdr2mB6(-%*mXYo}UBa{%rt4!I*xE_<=*Dj0HT3FbgMKMS2HRBO)J)9@ z@aW}x&_5XNCkuAtin09oS`>3AZF=J$Io{j5?>0b-oq%U3#p+7h{G|1x3l7P!ia$y7 z7Ir#Wrb3;s5REEH=CPO+lgg&Urn!${WFKRvhzR#K_*fYxR+F1$lB|-fzq?zS{O$8X zSh7kjcnDyo&lk!88d+*8Aonv$FksMZGr^L5_Pd& z>Gib_m=`R-tR~3Cp}}}HxPt=boe$?jnGTpN-Uqb3yoza;Uw&RJMVuf`0p)^+gHx2q zX^u@jEh z%vfmb&1NdI^cnBjdN(|g=}J3HsS;;jaW3l@DOMR>X>$_wf?gV?*HZTvie6pS_+##x z1t;olY=f6E!acQajYeWDQa-T8umy7Q1*cQ3yjaNuB20Zk^?jFdc8n&U8WDYb`jyN>l^ z&m1}u;9Z5e+^8i>C|qxg?tm`P_pNJXY_Cs+04&(Kt0ew*y=uvn3=*eF&r>JC{RbtC z-J`A^mY45MVE3lBIwhk}0$G$LBQ2^m(2+X?O1Ymhd6O(B!%r?)0&;-KmciO|`u&*D z2!$4-;?u0Q;U%I_L*fEHRpI0KvoX{8CW>CpCJ9HSncq~%NVN0Lyr8u_eh`_V{i)fx z2P!S2#DXqSA0!NRcCoq2qWn?T3VEqk^p0^aAKyspKq3a6IpcOpK#xJV+LDwJDo&;X z?p2edr#kr$%wwg+8kj_#?Xo}ty#oQhl(eK4V)|&ljeLK)+PS3qc5K};CjRt`R26!w zo&|mfF*BPF30cvo-1pn{jktc>EnmM@-8Hp#Oy|&*@eb&1&1cT{AAI+dsDSQULjG=Y zOnjQ5dvQQuQI7W%Oj89pMwd<*c1lFSw{+hT7ByzzQj?F`3@R|ns%m5t*<6oE(GKeh z3f?s)7;yixTo~>B29}bP^J?JLev}P23|{_Z> zBC~1!r14>k@O|d#nJ6H(?tGd@jG>kmB95vh_Jv?0>~$|tih-3mM-Uu|P9sFPT}{1> zx1M`>Zax84tISC!ZcE+}*~)vW~jg>iOq4sKfld0$t` zNbHM^SF%iDKQ7~CjLL{^ZOP?ybMKuSj+rvgfL1}IUp=PHS-n?^MI_3f)5mB6fuqh_ z@w9uY6Jz@zQeKhq;*IT9bEmsr#;6*Q6bRhAw7T^hn&)~sr?Bf-B4< zf$uAS=6x!7khFYb?#1`E1)JzTV{A8T^UiS=fm-!XlHZt9#odVPo9)x zM;5bw(=kp_tQ!N#>vhx~L!s%u0 zYJ0yDKaY#d+Ng+R0n(ggEHJFKuD#XuF!{I9jq|b!%Aw99h`E8!89pEOye}iQR>FR5 z_xQBizI=Fb-q%=b8x(NiUC-j>6?N0{ z1^r`o%Un|{pyqC#fSsR=OM*86G3r)pF6v;(*5=0KTDjP}_s^3mTCFo)^(Ce_9JkuUd%^5^p|w% zhwmxtQW=$0F_jy(%3(fsH#pBvIDMBgIW=91w%L07=e__(WNX)u>wKRvoQ@jC{k}M< z{@_-oCMbrQN6YK?v$^&JV8bTaehYB_dVlT)NCk{U_UqM^U6#&q$XYlN?4rT*iOv6k z9xVsnT$Qo=^2CXHsyfsCAQs4o^2qyI`}0y;+vkgl!HVzHQkIETlbZJ5USh-UG;rxW z$5`@@vM>d(lCDhIjZ*T$6ijGwaP0h)xRHOHe)6-=^+<~J_L0YJ_jAub4~FOL9JF=C zvuxJ8%TE9JbM2g?zSOI`+A3!!K@KKJHF*YB9r4N-ldE_6|1OYq#|D(q zp!J7-$>eCJLK05pVLkpp>P5o?8y-`qgh*=V<@~g?m}vOMKT89xiUfhHjZ?dEGMFTf zyTfubZAR#(O<86!B6_s^n{dOIw(a@RU?ny7_D@+a4sItL=}M$^gV6n3xOtBxJmD`N z%K6iAlzobnUn<-3$wX<@yMd~r*0D&ZCdM!KzB=&`Wf`=&fG)4dDdG zp+AFX7t$>yP-`AWRwjCP_3nhi-wRGa)NJS(=#@gJNw)bMGo``R+B0odEZJs#bXYR% zqG3eu&Euc3cp#8LN+j59qW7%{*4d8CXYIWsD*Dm5%!6w6d3y0b zBNzG}e^${sWqyAeGxaO~EQkAan?2&<4zKVO)S~xJmX3-LD0er7tv@4z0B!WJWGWF&)buF^r~R8aQ|!2EQ}u258je}eCrT;u_(E{FQyQ_9vH5t^ zt%EJaXm7JYCBTkdPPNO9?SngNvx2Ytn>T%v}9C%bw^cRs$Nx=t^{C1^vda9jy8IQ zxAK7u&Ep>fCOR^HlfPvj&k*Cws()f%H=Ujw>NJa z=Z+B$Nyu!?AyjA+Glo_v9LZKxbH{q5!Ap+tRqr5V=-XRxV*T`!EMsS(V3@D|vyTSC z3SUk9e*9NhD2XxLHmXom6t|bLTdIPBR_#u<(GSX>{qm^(G7oU$&eMjU3nL^|0{bQf zvYfl6e&823zbH1LT^e354;i%aYd*`_!>-j?+T#_sXYQN{)7nHdWb5nQc=$AblcJtt z(0b$Hk0yB)H!rlyU94VirE}bk4+J@tIMOo{dRg?-|H(h?Jv)A4(0Fq z%955maz2GQOf1}vwZ6|ei!>LBkEKh)`TF7VqK@8Ig?M?bBmmJ~){1j)9@@g5>k0Gc zA6BkuwX{C3w6$4y(gOrNG&GE}c{MyX>mQ`oIiDg%khnhvmb$;oo>U6wo>=}Y{jMqS z_8!-PXtV|_2IrCWjGb?IjHgUs9V=!QCDR<4K;Y%(!@8QGnY!;&S%=8>AahiQA@Qppcq#xs3p|@U_oyTi5q{U?y z)&Fpo%;)4)HEt%UWs%g1m+iI_U$hi@B5meuXh7yfEjuiw5^xQ8Y#R4iWpC?0U%JCI zr92~1ulNf9FC*-HOX;DoC~RQqT@OBiGZ8h8)*lL$H67PxZS+~*>UMk{2o3|^BKoS& zcw>NMM9wn=?w{>6sY(~k_jQP;Ik{iDU5){SUW_sV-m(zO#t_E5-UW6&XNe_SxnfOx zY#|dmA0~6=Q8+1t{ty=@h3HS$c63Jb9ujq7WMTgLbZvxu$(P9&L)`&)BUs!fKDPrS zI=#=4Hw=_%fzhivB`KOuy?fO4#O+zaXu+-noInFX{vEho=wF!Z66COPk6-T7r=Q_a zqbeKZHNn~9SziGj%M{7TC7g~f1edM&>Fzx}Q-=5RK`|n<*--^2L;?qioL!+*vcn0B)#BECa7C{wWm7*461J5U|XSN%u+2fdabTJ`iBvnxVv~)Va3rotn?Cbg(*LcpsTL= zxO|LoXL&ZH^rvU8qNPTi;!O<{&C89Lm>BKcIfY-|u1{I;r(c`ioMabCH`x}{SvQBY zcQJt^l>M; zu=R$?n**nHT<_M-D`_@u{}0qlf-HE*NT7&Wj1e&-KF^(M`iLDXWR>A`)7!Nf zT+aWaE^b!@^GhNxr}Un9&cxR9E49aIg+6hqgWpc2S3cq)2_w$LT1IPDc8Z&>DJpwc z@E3D=z-mIPc$x>BA!~>~42O5e7x=6{B-fPVIUlsTy^^(c{cQ>*^LU-rL8=79lquT9 zg?V$trXQi&5zkBKc)#lrU9O(RKIzxdyxc`m>pTxbv~iPNr8Y^2ACtVwf|_)>%PC5X z8=sVzhNe<&aD8R|lZ)G*Gqgw7^0apDJ=Xmq>$@Fu6^+e?_LHmVCVqZQ3>THz9{2y3 z@=gBnW4CY%V3N)em68K<+LnkUH#+i_`oa2EjTwtaFhD(h|fY> z;BBJ#34NZAXkw_jm){OVC+U%sxzAzcW4*uV!fd6vB94)l2r|qH4a-%%hz|@@Pdd|= z8pm?{?Am<@xtT-uQ;ooasSBU~<}Hs-eJW7twNhfc{NLA`dSoB7kDqkzg(;@}RUS2S zT(xQ;eyjM`YK;3gWgGNW{6>qz*evAcY>`fnolAZ`e_{4pucs#i*Y~=wPJEWUKNMP8 z!?3iP5rc1M-0usX?5$Pc=ZhX7b9l|ve)uQ*(M-f1i)-2FbHB5iy{Fe^dnMncqH5!z z7gA+qZRgUf!0W_((=tICG!V2tK%?uq-lFnE^jkb3wdm(QTyuQZeve?`nC>qFA=^9^ z8a;EY_=UOur)@bU{*l2m|0U+J#>Mh*)TYB_6;)D>% zub6OU9h=+}PbC>|mp2BI1??1di$(FxO92(v_%$zI$v338TI|q=G1);mo^QsfnaH=| zdiTzxY3l$|pygx)gpj8#*}_+( z8{L-3b0s`W91s8ivCJ0aAowq3KLgMZ=f=pY<7B%abnriha0}S3-}uo=IpoOtIQ$o& z#~SwzR1Qsy)L7jkVGPr|Xhi40*cYln)O*l$8Ce&PRoSy|_a6ki(+E?jixmwhkY^gj zi9*x-O@VIiUz|zkLMf|i1e2Z8h4n2*^C;}gQnLleu#1+BGb&A337j~GD;$kEdbGNa zJXe1TuH}5QM>yq;pcB?9OhT)^+xxMGab|H{z6yRcOHR zhDrZf8{6e{$kXu}5b?r9MR$=0L;)5OMmIklGX$HC6xIMZ)s|ykhk^Qn3g2##m*itJ z4$(AO>F^B@Bbl$V+18Ihe2xwORal5q8BH!KbYMe?Q3eN^o*hW)EHNAcs<12_?1KF~ zv#^XHuP%6e*Vw=nB1@BbR^-Javd z@3nv6u8d4@5hN8XG}j^uzsuXg+%p79)Z3T?hz;rmDk5{XQLAT7&w~jddz4qFOM|DH zp@HA#;z61373L3YNbM2)|3JnY4~W0|vST=3&PRQNM_bWec1+i?i6KGo^&MvK43OQ) z1q=x4rbd6Q((T}}aQ;s72lFU&eD{q=)^MOdv6>4g-&w!2#l;$Qc_Akrw0>)i@FY5B zOkgy(HzDr+tLSN-Za`=mje2xje@_-l8qL=BYLRPXrN4(e{{?05(C*-_ov`om9uPd- zUwKRHCmz6DKH6^>%`KQm+6&t=SA3#m;F=FA(t6+h=$(DUYPALdmj_cdA{8fm&29uk zU}o~-X{hIXy0&S371i*t{Uz7{4il5)%JXtSjiRd7xjF)xZRzdhJ7+cKGhT7^T9py& z+46;+1A|#6T@t?6lkl_KXXOBCu6I1J_x!keuRcBG;$F_>U2litS?D}T>sR=H(N>rH zMNU#MhSF=^Ihm;#W}sE^MUEN*-;ARk80@hoyL$jGMC+AaIeNp>SA19+_O0Vj51*1i zChlDRpL5pWD?9d`=553`8oyq@f?+jpw9yhrbequDg@Qm!L8ZSI06s{f7}VgvGVn)Zw0$m_rJS-+fSL}VCtuR42r zbxZ2FBbO-otKka8%|2OQ1j<0d;AR1FJx-kd{*p7k=q#S`NPi`@v!Aji&~dQwO&awL zo{>P*Bo_jq$r}H5Kd-UE(D{3#MM6z#HxZYyF2xmpMN*%WRV$_91i1;aI5;UZ3FK_! zC&G`%AnqPo79+XhkiyM{81bbQqw>B_}c;8q2n~q1l(S z%5dQ(cMliepyUV0R!T1;-X;6vxq!}kqVs67C&&7 z@D-;F%h^QDjq=T>@KnHjF4Q}JF_iYe>=}8LbFFAfdfHh9}^Mu#ub19~zy(+mfMA!M!$#{e!g6$~fR^&Cx%+fWms{F==ems;nfm%=j zKiQky(1G>S>mAb&{kpgGruW!H3eHz)@&*oDSH+bh>Ax4~>=a zXiSSjhJ*!OVkT1K{MPalm0{~|nt@yK#y8$KeqU<>aFC|5ThQ2UK2c>{Kuc-!>ZAEb6%12m7&0jslD#^kO$Z1p`?RTU(nN z%$`?{tco|&jPC_B_lCg_OQDOAl#ik(X41>P!M0v>SQAj-HE+LNa-l&0`z6E$Gi!td z<8aBk=O2Q8C2mV0zmsmKaFS|xNn}~Bq-qNVKPQR4;pw6p{XJWCX>w=NS@myAx+kz3 zOPnCGg$9@H%3D{TX?^G z+ufNmp>6zr&L{-&hxaKIO4IXnP!;Sl1o|PuFo%3Cn%QD9F5=w6mf<4o70fYQM_gw! zlF_P?tSZh5rnSLiEL!8!3x!=^b|Z8yypJwv?a>;E&961uzAOq`2_}XGQ#DTk+s8`e zmGvpLd#YVPcct^E>bJ3IQUJP1Vv2w{DjnFJOYi*MO+DVxVA-AOXZ?%VGSGm~Ypbg_ z@kJEC>+>?`(3?g=0lTZ$K`P;pm@Om81)CjyH=W?s^NZ5MvL(-Bxd)C>8ygyoZ>G@j z)6QfjUtR=pXG1UiNGRNCbH5D9N*nQkgfyYa*h^VtEm6x{HLs7k&0iq}9Bq^?k++|8 z+~}7oGO0kM3btR~Yn(RF9zeG}ep2Lp(FIt&6u)U46kkA%JR^!|W<}do&t+}GLowR;fI0UdU>(M)!C|zk1=IR2Fg@kV~-~pj$195;Z(aB%g zd?&W=Z3eKfGN=7GBQoF_90|rP)YpIsbBaoZIk@_rCdk^e1DB6YK7^71R)#*mXw{R^ zzo98vz_>_M9nkgAhYqM|s68p+18shX@;2WW5JEDe^|Rp1%_Rjr`M$~{G^A`+fpaTk z^NA8>ut0}weh7b-+Rv`7)bPv!VURfVv?DHu-CI+5cM6PiTdGPo1aFei529KyE`x&0 zAtR;UZzBQ=?|e29>bYdQWao$wm`Ei@aPp5YH~sk&u1(N6ofJ+YX}eJ zgl{`93I_IX7(Jx1I{JYRQNn=gMKtLObO+^A!`vI`3CDTq?HvugF2g1^|2_k#YlTsU zJ5BHb2yMEiUl3)@yVNOT!;E=z>&T`CGZr+>Gb>}C~J>{Rj_+qDgDz zbM+H^eu-Z7)SNg^8jB%>`5CVk9Z&uRV&852<|vSJE77WxaISaZ&MZ90h|6~W1I2So zbIFD8eBr0Ls3-XB$J01=o8vEm1gGl~U|v8pz&(&-#G8N3n>rf5h-&u9ohg+HYfFj< zm52mfJp-0@TFrC=->wUX5}7s)6rj0_V`Zut+&HVj7~*&?dJQ?|(; zlRHBu3(M{?%(b2o1?tEBtZqKZOBrlq)u-;4riwXCBb&(! zX~xX{?Z%LWxB>Ja;AW;56llHF_JLcN@5IiGBhnut9o$Dn32wqo3-TSu14@7ESe!2u zk~JQo0`C}i0s$t~D4K!Op0{5*IF; z4V1FEzGvavX0`eQX$ML$lq3=+#zMd9W5SI=r%hy%hkTuQATUgl&-a!V@;JU^=8fx2 z#Tsn?8;Iu^>U9?10P>yIXC%4%OFvVGt0dm_{4Xr=(fwm_yvD6aZK2i>j@GSwtOa&a z2y*%sh^PzSIgUu_e5`e(JHMepbEkeu>T;pvn(jPw1rOrzPnzZvZ@#HZq@-nNupn!@ z-~*fA_1a;ZDSz?pXoLS$D+0(QM`A?v{nO9^nMcoe;Y;UY9KyA|>LF?-XH|Joc8VM% z;OKGowW2tL$tIcT87AI>;OapwG_y>>biP59-AyC#$IApcHg^+70y|8}^95i%H-uHr zg>O0Lf-t~ZI3*2xr4N)80^fZrr#Bbzx&>(F6Rt$VyAsCcK^lSRddhe%NLzRibUSRz z026ljwF<#QGA8_I7TUPv0!j}VYo6(pIL!}Gpt-Hrp_L*=5phb{>#FQ9!lFnAuK@f* z=EUuU3yWVtKx;Ni%kVT}t>~8+jm_VR?_<#Q3_jGq3+e_?q_=&ccyWKl$^}HkV^H`? zF!c2G*vuRCR-d^hPKK;@C<`pgp-@Tsp>U3HX0cntCOzZGYTr8u#&c*&62jc1fW=LyyMn1P&!U${_ z9&3S|LbIWLWl$buGmtJtEQw}Ch?XgM1K@nNhzN|Dzdi!)t>bz#DH5IhmWMAEB*;>G zR{gWN_%jL8QUSiT#+$OhM}k%e4d={3KrZx=P#tqo?+5(#T@0!IYFp;!hNW3E!@h`< z@#nRJOncZDMc|9aGIMN08-Rra$8AiSGR;y+G*Q1!&*&f@Q@BS3LSt-1&i@n+evs&O z=yu(9n#YonY#R^rcvD?El*cR1t$E{Z$&F&P4(5XEY2gB63`QR6gW+eMdk#g2@eh zREStc$rE*}X&yBVE)q(~u)uR5YhsRt)DzaD_vp_lX#^kk#iFKJ*6EP+4D;%IqPL<7 z77+^;R`CxvkMzfkGH)5bg=dDjjm95;D(GLyh5TJpne){I?P%UuZfuf@;SpB-CK2{k zuHefUYD-qlH;5oVJDL}@?(`{Y{RJcm)X1X&9#-FAgu@CQH!TZ(y!hAk9Eh^S;TMyg zvG&A;GryS%_^#veDQkP(XqtXn${ln%*D4S<0EmAo>_X!Mkw9K!fGoqYv9urHUn^Dj z!%!5uXh1Gi^8~lKrO@vMl#j@9uil}(fg|0!Lev2Yw2Og=rEUm2)m=n9QWJ}!GQ%c{4YH|NBvT1jgd z#j`~2&{b&R3;N9pq8h4){DuQfW&T>+PsrGb30NTdjAf0<=~p*M%5GZJa51JLIoG~WF?VZU8ri9D=pHlbN;l1zcX1C zJ}?8PL~ZDT#AlVvR67X(ptm(eP}`s^SF zn9BW9y@cv8v4(ai2b9WPm1=egWDP?b4{kaH!fN}$0(VuT~0HDA=Hez+Us1sQ{Q z8w>NtoN0>)bL#P+CX+dGVJoTVT*413bDGc378?GNpKyit z0boam!A!0~m)o+5e|$0fRg?CSkzBAB7Zzj9#ZUeR>hAt7rp+Yx$S3=|i-mQNS1 zDdM*)bU}=-qpoM&8MlLar0FP^vJAZ0=X5gTBQ*x`_MNq1IzaHzn;q}WCG2`_;~-Y zh@GRI0R?)I$->5rY-C}6J5QwHIa0{$1j;}g#qSqf-5~28`x#)f@jN(tr+#O3K+-rz zcYeQAw&FcIB@>cs;$L$9S2b1)c6d78>T9)zJUuF+Q@Q8Abrk$fP&Q$aUYH2KXb=V4 zO`q}EKoR?t?Nz4OH2E&m-2g67YbKfQ;9xzILbpk&;K%P*zVE-O{&Ppuk|?`R6r*|A zED-V+s7$Jtkabg2u$Yl6<1Z?I>Pt;VVy>gT%=%m*h?UpO0qbO6PuL4Saj1YA`#U|! zki*KipE(Os#?DTE<}g8*0RYAhzU`ohY4r=LQ!*wJlWUP>7>^*j_v8!^bQA;cn!a*a zjfBq=Rb4sk5*q7fFlayv`1C(Bf+g+u6jeVIbfL4=fr$cN$u*2B?Yd9~SxVfrvq*Zj zY|Ak@k(QMJp@T)RmvIcNxq!|V4WwzEqK!pUi1=ivS%6Ui@Pn zH;)m>oquLL2-l{8e|U`9?ONzmt6@xyyn$mp!X_5}G%_N?H1afWy-&4HgN?QPDqXpp z`eQe1n$tGu2_=7T9Ah5Z2!;Cq&sc5d-8&M!_!R=QuE-HEjgWxs$ERrE+^V3h=9&_O z1wxDdDxp^t;h9)A_vFktGui{>o(5<}WEI?pS`Cxv^`PJFx{nHB+t|>fz1!+6PIG5G zpEBo=a^oRqZGj(d?gfz0!7%)4%h0h{w7JycUTLC;fYQ7(b(TKUY3LRotE2bv|HEV& zX#zA|`i4^3Jb^qaI^ySdZ0R>z<7uJ>PszYT8m2|~W1$>rg(hJV0cjV+9#+lJvv|7e zS`t1Q4xvuqO+W)23$hqmvVX?w8%)j(V0^wBHuv(q*Y#i?OIU?AEd1^x(7WEs)A@F_ zdRzKt!DyP_+G2Cv%^*r7W8a3^irhQ8&rXpT@T@BOB1{5EH%+eh%yDGjwe3hw$#w;O?)3w-aa4GgOLTbKf`OaUIw zq*z0tHxz9CQq0sCvguUwShr5Jv$Lr|kiYaTH;;=}dtHm3nfxRiMA^_A-hjP>2rj`e z7Wd#2R_Cj2g0tzUFux1hYL;YHz+;A~-PGVH-QN9VQ~8x6l~MTEtiNl4pd`ebiakxC zj=pg!Q*&e+60&{OO>&Rcj*#`uf8`xt26{s9j524p-CQE4N02Zdtd)jkmZQ3%VE%E9 z=3Mmpl7hW2u;SWZe=A~VyQ&gOnyjY1E+7wQo|3!rFU7r<>)0#b5kZ4Og=g6eg~ABv zn17TGcMBy@#|;r5l3}>TrNojqE;+HKJYL1o2dnu4_QodZHgTthdA)S(OLnZmBvHgW zv$(w*ythP_@Ukf;>RFP)S9W(J9rr2ipu5=`qt4UuMBlJ5pu4^Aa1+6Vy2v5p3M7ai zYl|UiKYR=SnZ-@3@1~#EXYmjSzT`}N&XPMIy7v-cxodNyaMxXv6Dh~tVi!8R)OmbZ znN*v$ZE|tywzLEgG@`|w-ex}WghlH?`S+uH4ev#z+faUMimX)Av$&CRbooNUw6&Ro z(_eZ7lJGk0I|r15@^^&|b{`_W%#`fLu50~tIK|X(TNJ0th|+D#09{PU6*w*D)qAL) zl*O0}g=*-Y0DpM6ee6ckd%;YD&elwO_1AUq(o0R&T-_<+O%H^(A#_+@6+ivm)zG1Q zRvOCo$-^AJXNLJ%f)WROdxb!@w&e_xkgCV@+55DICd3Y4PRSNGu-+;7v?zw&*cYh2 zmmH}(bcB=dKl^QAzotZY7WoknxOiW=ogj5?B^;|B3+iji7f#`s@q+D#yQc#2tPuXEv32 zEwtug|4sN7ZZX+nmX~q<`D+lkr8Za;pQ+s{iJYslL_jL|l*1J+4L}`&QNI#prN{7^ z=aA92>ud^fiGFL$PKE7w^q+|9nOmI2tcyW`0>qr1xSKKBM-0b_q)BLz({!?fr>Is2 z?xG{#Ldw6xAmgff5Xu*W8P%)%NkabCF)^mlOWe(_9DfSnFJ%ss#iM^qi1P(%#rS+qp7xG`jT#W8ip`m0WTQ*j<=PV{ zx1!P`^VlP&=KOF$1Z@l3^Wr)Rvp{Q14F33Be2}!-pG;Ym)&XEr#^Mq^19r$_04o0v z=(&Dz2zfYvekh0jq&lr01jZY|FFQABtYr zkxe7C7iWc>b}0-JUSq7 z)MBIfN0rO7rs#&>p_L^U0YV$?rsM8Iv@1iG1qP|Oe1XWkiEA9Zh)r+HolCiz_s2u^xHbCe+4$Q?1 zF{RkAp?|*ajc@?(>si$(!%;d<@BxfAtle$yvW#A|b!7StMi>C`ipU>(&fYq7+Puqw z(lwuq+Il$loznnWQz;Wdo++D-Gj9{fcTuRh=R*48eyA|29Fb&ni4_qzhPFPRRs zJ3rM$Kx&tENE>DZO>FYS&@2e}2|jAT6B?S>vu40JOK&Wtq0b#jQDFWkr$<}x8cVA&Kx$fX@2~+1Y&B0Cu?vH#IOCE(Q^6=<#k!4!>-YnYW`X_j39}I|EKAxLm?RkbRH-`U{X5iL%(Ni|MPoWYs*$0PleJe~s}s0k(VW>7S= zI$`g0!B^Q2Xday3sjy6}eIE1?R#gs%Q_3Dec@FxJFAvK-Q$%WSMgyQsIeF^AL7xF_ zPg|>-SJ_nK4k~RGI+gVjxrdYgg?LwX^ZIMZsowbn>$KwEeWD5*rkN|O%-S-P*Lfg? zhZ_4kSh!Jw{>tkzRZ*@1PXPB2Tso^MoU;fOjBbId)*Y;ur|Y^E%94+qU}WTW$jX15oV-Yu7#`><@Fz7l_|Z5O5keSI zrU)+<5_4|onKaZJWfb0ggmp}t8b4hX7reRyV>O;Ehp&JBLAj?r@g%3hZXLwfz@#m83V7A>3jP5h; zynl0|h$E%GMw2lM4#S4Sf!6kMq-!GgSrywG!U`Acta$@Bt@``PpirjNmzcFwlpH#y z|A_xUBJ+{MErzd&<1p6Et}eCx>iV~SupCRMr{C0d>8rQD^jaq*3Fm31qT6c$!4ZX; zq{VPN9vsY3@Sj%f<~Xj<*7Ua5Ta?r|Pqyl)(2R{}2`i|tbJ=|GvzO(q;mUiO3DHdH zswW4fDhzd-wHdyj=htX<5Bp)@NuGBj|GjQnmsxbR&v$Z!sv4VbjF#hUzYoc%`GOql z6maRmrSEDK9fh&y^y0CJjvk($9?%tSs=uJ`o6JqS(O_mUgfPG5Yn~@We0+at9i?e4DKLW(MH|g9#7x2U%(m+a zq&pi%rE%_WnnfkD4KsAF$C+{XzEjd;6OQjQ2uec|_#aWn`2oCMnhtl2((*t>u={Di zam33YXYk1nU%qsw49_%22x4jP#H?PU)MZnW{?C#Cb<5~c;<#htlcsDHIAKZM_Qati zSL!LFG~P&hsju-al&4#SvR~$VSI8{wYNDNpQ@%9(XuYOv=agkmj(QSWCnE?k9l^cb zB30H{%x8{E4cKH|+Dlb?3coutTW>yHd3rxs7HfBM?q}|hArdXXN_-Q%z5l~#eW`AS zJ+15OQp$*#f(~>}q>Q|mAkjyI3VEn6)S+Ky>iR8)vg%nlS}{frdA%O}#C?HYa+L=( zB=C0>8a+z=PuF^?_>JIFxC;PBe6xz14&A_p#-x@-4(vaz&v@isxA=sR{=H9x7WF6a z=tdRYPs_mMVjZa$^tYC|H7IHLFflM@dY_VPLA|=?ym;;aGXMSXjo|-4((bxXRtG-q0>uHkHhmBp-vJjy}tDxqUp`5`X)`^GfYWvnXY0n*Tl^10K-`6L;W|LE_~aMVEpl_ zx1}{g51phUOMr}(n)1fhG%x4m^c{<0$0or*(X1 z6Sfq_@?uhPTl=|-YDas?!fa!HF@DOaPvY?9fG|$;kotf%_f0s@7rgp4YUOM`Ls^=Z z9BaioVu-cZk!n$0cJ+(b+3gC0%^UO*=9dA$a~DB|O(E~{Bb(N0PpWMHZr4uPyUGmb zXFjJceCV$AkFJrUn-WPOlNc$lqYXvA{?3+*83b9{ z|9JG0H(pEsy1TDI?fkcpwY;XF_19as?Eq2-(+SKIzRi!mRZ=fA(?rl zzaqjlIs&PB-NH4h?|;0!{aW>2pVqZ--U+Fn)wA~fuBpsU-b(=2Q{BKULtBUO0C_P8 z)qWHE@c9=v`r0+;mavh(_u!!%G5vi!N5~1>XL_v9rZ(l3NJCz1#2?B$tQncs>e^?x zmmkiZ5$xgN6%lf?V0Ji8izn&Ee-P5WkeJ^x5jjQn6VL9C4zBo8{s z#`LS`Q$8xR_4E_sQORwl9^4(REJeX0jFK%N7$+EZgbXwC0^;Ww%kw_W$Ta-evUpON z=IG7E&nW78DLk&ct-kG4#NT*%NNgZAx;W92oQ~)Rw!S~D<@Kw-(GI|xIt+@QiSHWl zmvU0mBW#>G}03GcGpJ8Z+r6ImqL+D#0R1^?F%m*GY|i`2Y3=&oGv;SA zbJ`Uj%o!i_XABUIufS*Y`QUwf(rTzRBenm5zWV7(2I4PG)a2lvN_}5DP4(-mRgX0r zZ%cGao%$8X6q{(? zQ2C$5sTuAhp1`iOOY|mwF#`Z)qye;o4mkSK-u6SF-dIH4?dA&JWv+VBMT9l1Zu1FW z!=It@miq06o%3H=6us$gh~S5UH==2KOy6dj-W9skf5~?H_{3}h)Jw~Dv|F?q^7EWk z>iJ!%$6v{Pin$I0aF(kjEoR4|IInf?x+q{K$WE2JBof*uLV<%DF+Ypdlj<4n@&?s=7(Fv? zZ&rI*VuMWml*%2MDgNB~DZM}ZwBg=TkQ&)`bc%W*{^P$e&8x0HF$M{9-hy$GTra!r z^I?vSkL913{13$Du8dUdduzf}2`po-~^|OjddodfUryyP9)H zbmormjr&1*A|gXwM-U!pAKDi6(dC0%hcs@>;9tBCQm$^dMq%1f{6^Z~m*@SaTmN7` zZ-+%ZYY=c(O89&^*6m1ZSD%Qm4LliH!Izl(DW3-f=U#TpSboLGNql(ACaf}m9P7}0 zCU5bg^Xl)bxgY#(s8;mPR2d~J9fil3gU>qR${KcRG9&|H%toaDO4(TJgox(k1f;aF zW94YsyyZ_8#=XO@+Aip-gMU-m!BjNcJDRf@0q;7!Jb=)88^?C+Hsi^!xQ*?&`P#ba zQ(L;e(`y5}QT0KZGs|;IJq_~zI}wreA^y+i^+&%0CGAp#|2xKM z@chzvMm3qH7P8va?Z7bE#Vf~KoCjXykh|Eg0)LAg^p5H24I2G2s7W3r4#qZ9e`*dQ zAnpr%_WM4>vIfu5hlY-c%^Z?XRZtSkX5_C`+7SPsTQ&2BEp5bf0k)vu0!^EE9`lvsu_p5Z-^8@hV1357O709_=p`mf47Hif-JBf3 zZX0Ctbpq2cv!IdmL#Lb_*+`)Aypn$|QU93xVB)=xYD(eJ(* z=)H_r`6hCIaEVieqc8ktolmj*rq9g-Qm@T}t99*S);Fd6^nd*>bR%S|(PPq-Fw2%+>7;?q zuNj^7J2)ZV9dW~aKlu2ykljXeIZR6O;>YR(KR~8&J2fwni4}2L!b8IJ)Hm*UstEz~ z4XF?#36Gf5@^pW}-CEQruNWF&mB0xs=)kdm|4lqGyPT|H#51`PEYkG+MS%_YCOv?k zBd{=D&Yk7c?`&%i;~!-_u+(dcjh?w9OT?@Silog})udc-gpB>?h7ZQimp8a^cw15I zSXs4X1aahQU)lLbvl^qbL+_?-v${e;;&mY-Yo8#MUHb$Xi61-HaBsbOpGqrd>fdtI zPs@Gu2>~soXN!^DY*gN?ibN_WrLT)Q zzF-V^#EoF~s3+^iqb~t?pkS=DZHre{hMx$d4 zH3s8CGMX}!1Z8KeU|6$1zYUYWifwg4yI}hjJ}P?CXQG`x7Yz#ByCcW1IBtcT=HupF zL7+9_+L{b&Fy9`qmn@zE@(R^AWG4llx}kR~p-@klOYvq(M78uPgT=8;Kt2x0c~SsH z#b<|G(TR7Rn9;_`zx_|3?YAD$Z8kG*HKRG;pmbHQ8KuPE(^(BtaFL1^jxo@fnuZ-m zR73b3VKS=ijHSq&fsHBhOeAaFQX(cMRVJw9@)zUO!mO5~GTsi9$fkZqoBugmJ4p2T zQc6L{kA-{O%7`4@sn*BVGGP7aXt+^&3 z)sqPC8-3q+)xS$o2GsWq6|!SI2Ne=y``dpUxz&fc_$56z)@Xf#QBRWySk_414wp%^@`u(m-@s+1m4CE1`*Yn7*!IEKlk^%1bB6y zN&@5xXqQ|jCl&HD@Ab=}j0H!o-PAf=%p%;2U|jMu`Si)?c5r&Om`o@!h)m}QVm`ck zE8KjLrfUGXN=XXPkCOY{8%2bRoSJ}SM11+dZ==j!W0?a*h1Ij}hW-dAZ(8=^eIde= zyA@3*zes7mz4CUqU!1Y%v}zeV($Bb&(RTC~Qd9)tP^9WP=-ZQ;Iv=~@?A98Gdxl5= z`NcdQtFxJjWOjrd>{kwBe`!E2C8#SJ^8p;g;powIz*gRC z5PPY6fJ*(YDfxlf9a2e$YfxMIVn)lJ4p@w)C`q8?Q5xUooODkD`b^}-*>PzPBhSNY zDvzSos9+ed=F{cht4{-TcSv70l;1y#^s9SIH*CY#;)R9P4M2>Z?13)Bf}D`Y+IBK! zljrvIG3#C`Z}(b71O{_^0So1mm~{%s&TfqTK$@G;TE^CKG`~!L0L#zNZ3sJkQ5hE? zCUo&X5G*aqcu9yjLC_mQ|H}my;POq90nG!Bi+u5A3JFPJ--L@HXBT(*x0$@1!Gata zbN{dODC1Q^(NOcFRbBuI5YAfu#?uu%&JK@&V|K?3ft~#*Vz)#6w_Y-A7xCUw1%S)5 zG!4XNiHI_>s*?;r0mgam0eY360z~P^qwyj}JaRS6!3UHSj7BTY&>Vz7Ab3vxkD_yr zXX^js_?XNsa;Yq1iW<7Pm5s6K;+8wz6{1jXbH8scQJT5mYK+K8LKm0hewq7(kh?aP z`#p{E{hi!y2ff^-Ch#@(s+RcF}q3! zj4`ov8_}QfsHeN9yv$P=0tJT5m>_}r6+=H-f*05m(4zM#dt~0{<1}@ep(6*wwC4?7 z?H<@{w{gvrQewPvMoYV|F@QwxxysHF8b~JCR~^J4hu7)SbW-BJT>CXf`7Cynt3#)j zk5;Ol|2}VBW1Jal&2wc+&5=gw4yU=NfZjBHF1o%)E446ruF{I(c{=L2OCY_nE4WUY8m_t_6wW?W&_1r5CX^*bJl1V^jUq1OF ze@o6xEyNwumZB_}G}R&!0v-UPWW&^vmgN<9+@oa79R6SguHRHfqU3m->S61E4NJYv zFm-=z@l`bKqaxpbplhk5)$jZr+sK?#ae3ZpoAF4f96HDn_UZ|b5YuH4_!M~iV!sy6U1oV+6xDmGf`%N?poTI()ctk4gp#kf)J`Yld z%@pY;NX3u8U+;v60dk-c-4WRbjb5Lepv6YzZ*HP^*jH<>YqoW@kfZUo&Hdr2r2fp2 zykRhx4 zOO5PM2+*eNYAeVX8dAQ|LqT{#qzwKKV*p>4NDFaea9+m?-Jq!Gu(nyT$1zC8JZ;{9 zKg?85Pmm=!Exzw!I$m&;Bsu-NI9vWd(0$H>`rW~I>A0>7m$L(N_z6RB)X{B%Nvm@*I_y150V9F{uCg{GvUQ<9^Yp5@!RIPV3+p~nltVaj1Oy>y zD}t8WO*+Pumy@)7f4@c|lz>R@*SFmD#fgca006|elcuoO+W3s5M$^yUl&R9$M~9B zvH{P5qOjIMN~BbVT~MqAIlg{yd+S{z{W`z~$RZ80a|Zc8YHI z$d2;!+GN!P`R|&aR7NML>UReewlU5MeY{-}z=%!RY%`m{ln7K=Bn0iO8o0WE+}LVq zoLfZchS~i9i52aDNqn{mhCjSHbgEfqtKk*GJBwC?jujsF)yChmas!E*(rQr zd)pnmur+usZQx_zs$q79{jmI#tnjK?v#BMQoRAjxC2!zWydj_4bW-yCbLZ5OS4ZEx z)X=$GKeSHr(ylFRJ`M#g8vlZGnPAc>+4rm`YGdk6VYnuSyw$g~`JI|r6d*tyyWSuf zvK$pw`v$uEH?I(OeK_0OVhP1M@i+Gg< zUFEsPsC|}X??}>jVE|L%pHzRD(b&5{O#k&00E`LvNwQWL{`R`G6wn4?oc|mO0B>&O z@J2o2KfPXghRpX%W2McaU5+diWOX;=Y+Q19Gz2_c$O*CzzAwg!g(Np^1nfJPiL>(T zA|nS`nEnJ+4*M>DmyCo1JSJq%3LBcYLuTc)sl>a7eHVc2QtHC~nt;$tlTq(OCkfsZ z+njv2|3E-;-(@0h@GnR4-3$9vd8bWIp`{Q`zD6c!{|p824wkIP1OcP-b+$82k}zeKJ?KA)dA&>x=;J4N6&<>LwM&93{B zm&?-GigZp#hGi+p&HV`q>f`GDxwunf@lU4q4?+wPM!9DSrexObd<7}WC4Ujko|-Y7 zlCP9sQ|R*8i()dM`AkX;=k9NU^akHMS4#DWjN0N*;<$(IY-tf{TjKd`*^H5dDk-z8 z^n2IkMEy^P(;W7lx&B#>8%!;=O1_D2qS21@45veyzxYV(v~w&m6z!`}pM z*s50k0gU{Y`^`qk^kzC_W7mfoIdBs5(@IJbd6OB6Y3Ku6XKfxXA>=`lyg5ZZKRu^5 z`g~cNg|x?qNII@744t>x0e+Hp8h+3M@F%Q0CQ{}*P!6eAO5ac0&T#+}?Qq%1zpd(a z4H(MS>Y( zm@`N6$2Y{us;na8F(_vICZ`A=6}Co60fc%AAmlkdvCtJjXc~4fZW%IZzdslWmdVU- zKiqY3x9;EzC@1L?th|Bg6#K|-{cG5cB%@ZqkP7u-$BDdzq zJ?ZXdlju$3L$Q?|Q|ch)z1lJ@fd8S^&J|xInGCBK06~Z zc-gtv$5o4#;7FeB0+<+3iak;eb}FYvG*HsawvkLvv%;K1Lg@KwPwpKw<({yipI~Ex zrwDp4C`^5z({Qf9Q)Wm*`{Aj=u$*W+*s5=nYY^ ziRnQ3O+Vn3m*J6_$l&FtaU2d`$*f1u`l#iNql;}>VF;&SP$Ptf-MzLHCj zcOYdINt=m;+y4mggKy95l-c>SxFsYLc95zf6+aaQxk@AsicoaMo!%-Ud)2V2xYofMfj#K;+%IlmImHSP6(P7SuYSGGezpNwVxUv5x(j@Cdn}<*w8p!XlkmhvWn$u zTXIeD(j@n9P0-J9dqUQ|0XTqkLef-l?t-GBj+Fz*L7rl^o^$n1g*=Z;)WFRY751Q$ zG$5Bz0LVzxIPlWFEGhcuXh1CL{i0cT*Y5sP&n!~2LaI@6@ z(;&BA+&_y=cXT#EM!{?H>HcJ}d!8Imx+l7R2B-?c1un11ezSxBo_`%zLySy7ZBr{1 zGNh&E*#oFHFEi~{P_rCPD>zL3-PqxXs?h+6H{J>8@x5@){GQ$ji4m8BTb>LTP1+Mb zF9U&X{&N^5^KH-|!>_#v#*}YYfm0e?LbO*yq;@nsx2wkC2!~BXwmrRC`*yFZ!Iikh zd9UM53M7;Fwy2yY9Rt8yo^1lYb~tW4d>Bgm3Z34l4V^cgLVc>GF^qUZ!cRFS(@MBc zq1e2^yK_{5Mih`B$^!Yqjg;Sw9B+`7K{0oLf>tVn z9f?E`2V?ecgsf9CgYdRNSV-oHc(qhSA-zrsi+m--l%ONNg3B7K+D2}+X*yAuO3DTa zao<;H50!>yHzd5XW35m$1r~ zdRse|m$4!2eB`&VTFM@o5l@SCIXrukk)X9zlq;2Icf(r-wQQ37oah)ij>8`L`;ebq z=*n18UheY|?=OjT ziCKV)kxVk=5#MeFoe}rr^Qa%$IHct)f>^!dHNt+c@)HjCW3MN&U??A)s(h2dztZ_qniD~{9?20+}uKB4l%ey+RqQjR|g*~w| zp?j9TiB(26NJ#jO{!`EuiEh&!Fi%|7q6rgTe^RUZU564yaWAXp2M5hcp-X@=W&$w& zaO2l12gP%?+h20lobaOItY;1?uA(lm*}3nVlH~YQ)iVLBub$7ATDMHJqmvtQu;a&dU2~q!M==m z(b9!LEIQ4_AD3HC{d%%(%MKq&@Zyq2$wp`w3dXN^mhVq8hN1%bpc8EnaHaj0bP=+B zZ~t3zY|s}+;2eOc!?%Tx`^48Bsq<}si6I*=!P}t$pyzT_nUp#Hx#X&C1m)oCm1Y(M z&2~soHj}HA;QypI?%=9QP!1kMxhe}!1=q9ZP0yda2_krRfT0CM0~?EzReX}J1j*}i9vHlfEumbicHBMCeIsDcQ|SeU6?G8dIER`Vny~ZvPy~9R#_Za zE9^Cni-twE8s&RHiD7`!<|OvVIRaamE1VQKZw$C%pXw$fS&;_4H!faKu4*V?ljA3l z^r9Sp_}AF*nDNIA4B~4cr%w6asYNqH;=W=-*>lild`|;iH@-jSQQ!WGp9rv!+Ud5b z`H0s}XqNz=G$~A*-DXc~~;=7<-2l(uW85@DjuiSCcl>Mw83BgjNvi2PxCt zHj#;RnJ*t=$Y1WPrp*DHT0$hx<|?-LCxZ-Q_U|7>0`SEe2s12_XwL0v(6CAh@;1B0 z<~yRmuCemoqkn5=0jFO9tA?wzd!XdAv+>_msfGF4NWn(5RlrHFMS4*l0?EemtYZZN zKhIbpAjy-(b^n3Xr>i8}CFz$OVb~VgJ!?!elKe%lPDXkxsnONPvSips(I zg0rz*NCvx_F+3DbvI${+AULoG?jNfEsmvq~jFjDkU>1%;wF^(`cvKTvrL5MILJfSv z16%aP*J-M{8Otc=^2h@dHLq~OItxPFqVihE2%PL-ijKj46A(9F5W#drGL3c9w9!k~ zm=_n{Nd>vLClHHvy-j>A|6HpTCm;9-IlSki(T^GYF@ymr<=4*75^h~EfL+FN-ebAw za;q%)y+Wq#Sr@;7G)Bc z#TWo>!FxM|RfM1f;U?1TtDGp*q5RXv{#f<=4}lATJG86uLE$f_jtJC#b2JCP@RmgQ z&kh52=RhcA)n8`H$V+^-GaoaF$ev=Dn!I~_$e~f*9BdK2PG+*C;_>ge;z#vh4t_|? zbCfl&v1qg>_4Tb$$>njClY6uz2%h>Bl%@+$nOanpZ=BxONfldFAuh=)U}TGarheXO zJB?yM|L$7fv0LJ~(d@LgN?{o|1WY0um}D!*i3}Q4Nz%>}MZ@%G^=_sjcO3^z(YbMy zg}osB_Ixfkzv`4?BTv{Du}PQSgX?_#_Th4+!2L0@ggZF-@dC*e+*iczuGef+6XK3v zjEpoWxNP^%fSoQg_vTb8**<@)iZMTby}>)`K4M0t9T3cI+qj>5cauxVd*P7D_ms2I zdmPqKp5>w%x2oC|ZjTHeSpyMEU8X?%Q!$z#Ia}GK`#+E}`}T_PzTyw=2O_DiVeV06 z@_{twgydna@8NrGi}6j_)yKN3@0YsQXUQFbwps0az*)dy;>za?*kdhW7a>c4NW(k? zMgTIx?MG5l!v&f`tnGuFCswVHqwkD!h}+Y?to4G=ILdjTlWJqtrk!aG0oCM71L#s zha2f|lBV?!?Hc`xYgZfE%*-~o3u(#GAZ|g&K&MI%BfG2B6$v69_o|YR9*%pUY!A>d^WQV5_2@g!aF$58yaffJXR zT*4@pPS);^`WNLeJJ-iN_#;cELDuEau$F4Qjwj&U2YK%IOY32N>Yx_ka-yN6R5cBj z{ZqXd{_6p;jO(+!@*`~}-f^DD9-A+LZ759Yy=bZQAHVK{gpQ0z+HY?IdG8<~lEKb; z|0md^LXjD+2u%>vV!($>u8a+>N=0kngA0o!!MKBR6tZI*uoQs8n*&)aOo*3~U3*9w zQcEklZX2ARljOz(hD{_&$>qyh+ ziQ$=RI44+wvZj+Zp<&qo^0x402HS46H%Pqo2JBbWi_o+yXnvUFzUk=To z<}4|-*e=-X+;=1xAhPjFpla`<#f{?Zj20|m#`3*s(Cj6 z;kLmc@0zNuo38s_N_nf|Do23CeyqGWaeZJkiTg>Q#^rmaM~IuW&0?|KXE$!^qHb9c zflj9F;!WN)E}dH%o(9{Cmilr^9Tf_lYXTGeY{rK~XFB}tv4S}-GZ5y5iGLk>#x8HnB?w^+-=a52Uf(i*cMqt{7 zQ#L2_YItwFfSs;7ZGgF;)@EkBjWNQZ%Krmdq-|_?A}5xyf*a5e9YUC3@~1@hPZvpc zMhB)&4#>c=*vFp;2uTtoN2Aj)SHt6N9z~oMQxU(LaJiUYj+G49q$EIlisw1oFMmd& z^~o@&%~qX&iIA@{Pb)KP`@GB#i+b=YFVC~fC9>A$sMX{N1g5$&k=U{Fnj$5?7PT*hH1vM6Z@tM_alc)O~CWpPgUwtAm(4o@R9^q4%C~wEJ77!m4e(jDno>m&)Z2B z)$A9Zx|~0@>PF3eWSy|PWL7CwWvWu$AXeVw!!PCRgNz;Y|J`7Ky)>ZYB?-gp4FdiGb*y+?N>$F44lyJzH<*6Pbiz`O1&WzCmrguSNL)Ly-UY-_m zKqM~{!)&Yx^%>sc`@I=-natk=*@P}umB2r@C4uZ_lG!^2|IsBkb5;U&I%u6!{m$1+ z7SMFrrsy}y4cED>S8dD+zvnx5Ux`;c4x}_aI*mCQ zpbI}Y4j2x&mCza%rbJ&I=TWKD&M!D6!(P1h)?j5k8gh}mp{EsJVqhEMviFh;QYEc@ z9fSp7#}6W|_??x(TVbEKxV$X$W5z&F6-;>wVbK|2P%<44oW$>ixcawA>ZlM^=&&H) zoNLbIAVMP-x1Jq{p_5}B=OJbM;YmWaSvPi6#O|mr%@iPoUiJBSS_GC9At65dHfeI4 z=@_bb!$$oWv*RTGvv&go_XmGL=Znx)_c5m6S$Em9hGYf)cJP4qrrC9Fh^4{)h$Ik7 zLLbT!Gxfyws{<$XOsD=hb04`wKanwjo-Xw$&EY&9bdeQF^{B&TXBN&r zK7Upp=D??Cz^Y`Kk!GTv%D=3ZHp8TX5dkyGkr8I5BF013Pji6HR5foJgULbHvi zPivr5%>1pFmLFQWB1tL@`6nbwIRAmlXGX%KWXpQBaYS#D=HBLwQ`L@IdOwBqVezH4 zrqbvpVf+vrxS}(L8(;1lf6$W7qYPy%oiyau-@JhS7&AqBnzE=t=H8$5l<)-6=5NQ7 z>uh&Tnn92YI8Kp%Vq|r^iUtA&sRO}{c)Hu-2FEJKvKsW%$X;5ouxtmyr?{4DwMWd|CiRqMMw%HF(EV@U`8i1GHqIS6Bq~x;Q z2y$qrLH|Ozn-j{agkQb0AiKmtCs|ABXq$5v70wF9HcEy>5jW{<^@NvAMjxz+9H30| zR1JFl;}`VBEThArU2d58^D^mLjrUwvB@Xl)(7`PfoD~)H8}a*&Ldr%a@S-3`o$poL zJ~$&j;~-=q0b4%^vP!92yP|bjoX}YfWhMX73QH-Lr?_M$_h5A&GkKwC?x5go1?kwH z6QMO8=xbE8Z^zcEl~PgkJ{`F z(fRUGJ$4z^a_wdc&}!B$A6QUwPWf_9C_q;H07_LmvcTgQvY1H9{(5pUp3ewmCGqjVlK9Y~ zTL8L8Q0#6F^rrX5wq?*yHTgfCCq~v!FiIaiGm?6D%XO?1-+y&YZTqO7;Zi}gvQO$> z6{61U>seBJ@zK`EyN8l`K4;%Gr_hf|w|KIh*VzF~7H}9#Hg*<Vp)9c+%eetQe}XLu_@$uRCFDs#{WKrT_d3s;`Cr zl0I04{eMdSiWqJ;7}j(0zMfL~;g-Fhw_~TgU3dC5f1P=&`-(SEuy_9=nR_T+t zL7bYHPYA7vrFvFq9M67qgU8C_kb%|kSpS#Ai*5(QaTkUR9*!617bWNg?usv4)YcNq zK2&@@*mh4Lk)_>`ca9!aV(LlpZ=0y-=P@Jo>z7R3`A5_L_5c-9^-)^Ni+R)@n7ifo zKkgH^3PN7>b=IYeM_TI`fd+GKd3~aN@sAGcqMRxs45n_R`ZJLU+-g8Yv{iik z$o%EqA11Hm*Iz`O)vMM+p_D}A~PEe01f@a z=PWF-HhaqI>+7?x#!_!%?J|uL2-AeHM?t$=e-8QdKh+U_W1cwM=PP(x`w{-M%Aj0+%3J-Zkjf3(q~u%O_2 zzCAYf_c;RS{PS}?3CiANPcOxa5>La)7QY>tLz=DA>5ZD`ABG{6I=!=UGA&~HiOU;5 zs~@ku6r8=c#8)*^vN0VLZ~M;v>F&9qo%Dn^S|gbk{$?<3GlGMMRAQ0TN^xjQm(2Dp z8)1xXvqVx|WA{+>T;bn?YhfmzE_Nz+8Zb)Cr~#3d6+bR4yMGv3m}p)%Cso({C^-THnq-etF~QBSE?6*!HO= zqpefbPVpv(1H}R;26`H9UnMIp14ZIgAuufJh`;kWZy@KV$E7#H_eEZIfX_Fi^*zSR=iQx($~mmg=02T6DEJPl7x0Xx%)3RG2RliJuyn>EQ3TBPM}4f($Ds| zfF*y-WOuX;Atiyn%ud6l%wXT&kk85;zn>K+oQa=H!!C#axJ$A*I8%`uX%Tvcefz;0 z=ZE5LXPaUP^jgjlvjN1sk!!)TKTKzLf$Lzyi>Xin2=0qxBzxfW$7Ubrtyi_YkK=!z zYAh6te(vh7tK8MloF7M!LP+^LfvJ}$Mf3jaF8)&A|DGneB9V4@c*7w6*K7U1%N;tZ zjsn?si6Gkyq426o9+_&3{x5EG&w`(=T{ywm!K=0N!_Bqk;&s`o1&P1mGV>qB&hEW= z<69%!>IX?XF0I>{&|EmGXJ-CPkJ()8GA#BgfS7cJcmvZLqxAv=lme8dZ_f-kmwacC zb1Fs`Ec;fhdD@yOGlGpYuaWo1iajq(Ecs7^BuJC_T$pNYuvVL`%Ae7k^E4!iCGz1nf-|3njV#Y|!W3`cS+*PUyG}S8e#K z{Dd1df|QknW@&Z@?d$Di^PdibE61Lx;au0#6wgKNXH_Iu^R>hN%I?k;sLlR3MfRvc znSdXzAH13A8hJR-oWA?$sjA*krQ$bZyTc3P$iIt=H?#MeheuOec4J~fZXD}+HzJ1n zs;X>FE^1tE_0@QN5GfrV_gy3JL()A^Qj3OaWMag97$aNty1i7DhtK}yjuP~FH~84d ztL>Lj#WnJo0&CWOl5}IMhY?}##?ID9`z>nSryR+r{2~h7w)8Xunjgpx1dkJDs3fg& zCeYXI(fRu`bBVq+H+-Y-JaN8vK^S{|kCC|Z=BWE-Q`q6J{acw%|EQ(5tD;rG*KbBe z_7Sx%>&x*R96g_c5pq2E!CwF@n+NaDRf;FPilm+U4-}By$|qC(_RqQQ^9SM;_uJ2` zxfhTUe(Pqua+Z*6hFgA*;RZhkuUD@#Pv7vVZVRYR3*$0(h*LcGVrAm(Gk%rAr zsS*fYum~^_hU+z}kix$m{q^kjk=Fc&CB17U&fWKgur$lt(we?hFW2s<^0iO@7+8&g zh4&QaCrxrf;smU0C4`&=G5X`nyC$yvwYG+SsB~2H@r1u3FlvezOP96tQhNR~t$3dl z&BBr-8%eSyd_3C?dQj~Ex!4jnd+O+DKy+}`%99DFy{eLQ-1GPeFEhNkn(5B5!oKk3 zp|O@@71^iy-k7E7GRIB2e<(8}P`!NZsKvos6{-0FX^Yp^9n+?cEnl#URt2DQ8g8ds zBC8BYOAdXoUE$2%@_0d=h*Oupz3n+R|rD3H(d*Cq@}OYRDOR#}^7 z?9cwO-){9Ux3tb{G>u;GG^6r7cQ1JbRs8@+5$+zBy0|U_c1klW530dK7KH+ zl-#@CeR{ydyTfMRkXG_^T=)IFM%|&LW=3{pfLy)ep>c6JH^8JIumT1F8+kj8w!UTU`stYD$A2Gi zd&_sa%iQpBj7NJJAd#r{`2mzVqOaIb#ZA3l`l&{R3auz0raKvT7n&Xs#4ANID?j>Z zPya{B+x5eBkBQ>|8+S z%KC>?x8L7|>~12c-85$y0|sA-6~FN>^O=)FA)k5|MQH}M9B0&o`KCKtmjiG5G&@wH zS7KL0?fEuzbfW=O2+noSY}|tHht{5L(K%D~Y3SFb+o^IF<;&@1(-&n6B^O76VquO6 zXH`ezxfUwIPJbKpz5C(PCH#3eyHru91eR3fg-}SRu1;#a!{ZBY3Z81<{>5EqzHWSR zclH(AAvL6&6*(YNb>`zUv4E$4Lp4{#`X09PB=MTOlvC(BT&rXIG_7r}_lDN3;9Bk3 z8}v-~EC=El5v@vYQ(W8aMI*UTkz>=XXKoHl88Q+h6EH@qLVxuG8migV%aLQlNRDDu zg*hL)0S#kuo|;}SM*^W=_g0W?S_k_W&nS2M;oSNDtcg$cEfc}N^IixYljo%y6GawH zh2w&9&ebcT_ecc2+8zL|g6Px;j}`QBuo69z0Mf!_E!A6BOacZHj`F!s9@KeVpPh zhbi?;k4a8ZmzcJvHjwFhWyK)My=|Ggj*$66n7t7gAWs@k9EbIB+gDs{$I*U=2IQ;? z{}Twr_zw?d<5Jruvp_jzQg~0Z{saL}b4P+F67lu2YE8i*OEVo7Q&{Lw~6HDfC8h#TrYrJ^rz(DHo<=R?HVxl-AP~)2d=$HuMXOVC0;#Z za40!SPS(4JTVh!pi>_cNY{A0RaTT~q0vBc|g;{a6a-sprJmHQUe}GHiTME_9B}`*2 zc}y|hjq*O~AvL{6z3zyU|E`+o>7&F;1siDHp^egqP=pR02O(v!Of~%}@1fh>zPw6&*~9CIf-H8aj%c2BDS&8JxbBoJ+$$q zY&bFWFgn6u=93&MpKbWapCE5@2-&N@(W6}1%{N7JI0!axQJhH$p^7<-_hH9%(bEon z>yzmfUVIIUgZrU>q^eG*Q@<)7S#IJ`SN*mBz$P!;<(dxjIryYFzVF>YhqERVjR8fr ze}}BkPLAV6$<0~rqq395GcOr%BTtLJc^lz12xL2IdF(a0*nRx;iy0Ev*7E4v)Uuv$ zl6C)hR#x=?D1!1qnD|vYQDb&}U^r2j2G2Srxs!ClR8N!AMfxeLLZCuqiQ)hHc!Kl0 zyxTZVFd-nOJ(P4=5!q(YfEXSn0MIU$7*|6ZR)SL$j{BUXr{TTY%6+Q4>8O^K6x|gb zc34qWP0!Cqw?Epxs?Z-97IXlhF#3g$&#lz^Oap2oP@oGswbwwT&u4p%Z0mI@g0KXM zo)+nf@6F>jOONSY^NtYK56V8RDk4)3A>;CCmhozWfez;XD8Hli^(Ai%E{6 z6ba%wfZNpH8$?11mr!U+F5QOY^wl5LImiw22b&nWDX&Yb{ZzcFDS3?#GEjNXBQ1ho z?|?(ZVTGM;VH6}cMwnYHFmImCOGhd5X(wc>((^sdQZQx4>fcVm^x=`$9(7^leHX%# zQ!jKuvvjE6pAPI^j#+L{7}yHfu70cSsenDi8Jp02M-Np-XPE4j;Am z_Z1CYfqcoyRfAmOuVx2S4#=q^Ounf=SZ5tkU$tGp{B7|KvrP`6&F#QDskRDYW|=iE zjK%p?B2aA@0o(bz1K&+NRoi-btR zFX}Fviif<+u%?_%P(B3Gt>%0}*JD+y_q7-7O8VjR}pCZ#d}YAH&t!_eHvNPrqB{^YP=5jdO4aR65wUDDvyG=VNyQr%?Zd~W7M{93kCeGDP82hw&(33y z`Y~DEjbdy3^YNY8*Pff(?*V{wpZXRSYXL{GG6*7x*Lm;uo#ewOJTAkLNo3#O$+ALF zE~$8LGltjW^!+=K*M1Tbds6|9f7S2s`3of!%Su(r$?q%Tp3l?KPT`he^H{eJNJ-F> zH!u0n7mQnA%CzBeb8x`M@JU{q$R35UujRlcLQ+LvOsV8*l_w7NAM#L`Q&!PINvmKF zH%RK(;1@A~7WQKM2o3;Odo&Nd>5%OF4|H+SNEOtbPpGCn3SA#f5WrQ}`N6-_jRinL z@?CAH72J(4ZgN35#a;-Al~6ah_T!x7=r6j|efT@&v(Fo6>R*9h2kb3e37;y38{zE6 zs>6gKa^c&n`X=??qBsp%|A7GLfy<_bco`5~{SYInOTY4pQlNkuMLvvN0bwnrLgre=`Az#gu>q!XD~-w51ohiVj5*jz^A3uIjr} zOM^hBEp98p=3HIV!e1u62e)%XD}jA5c|!(pkHmDs`qxR3)Dp;wOsxW!mrCE(qTwJb zyN>)Lyz+hMN2+E>u<$=JFWU9^Iynp&~D_i*t2QE^S14ogdv767{920O++52eCnuSbN z!;L^Q>_>o66SMz_lD5$>g)jx8=`zq+@4ZE(ojaG|itV!DRnvQ-yIWKg_c=0|pgF+* zpdrvn3vi8oXgQlzwA0SqlG_ckoHec<&7|3e5v&Qn(Tsnlfd!z5lY2ipnclG(hAM^0KdhtdDH15MkGL_>(YB0NSJ1n_7kT|+oPh9^ryi> z4K2s$>DhfnoCYTG?)lH@}*Lc|f5yZ7BgY>4cxJG7T zgd(%fJBC#t^7L}Wz}{CRBl!j;Q%{03!V}-Hjur9>vpe-rZMY1v``Sk20%p5`tQoDA@N+GjsnaYQQdY zEp8QTKkofM08qP-YwQJ)&_jsw#iXk3WIOXXuvP-$CE8c{W7>g(B#q5+iL39nlLuIZ z5Ak#yR%E0x;}8a4j}QA2*EnP22umD@P5zc&y;t)IaPfCNV5v^IxhuXN1TuX3?PjG1^N z+&S*Jj-5atWiJ`~YqrkXuC#tThYx%)O?k1`o9=B1$~J6V(308a$36}*VhrmPWFyBm z+rn@^0-!lpK#lx}fdTcIs+OtMpmhT?S$5n`6-V(ZTnQVr7Yll5#PxN*aT(aH_P*{l zc)(u(W0HZRZ|Lq)t2iJ6v`7m%N%3(aLHY?NYNJIUl%QK6x!Is?D9|w;{1LAnNALT; z#LpmknSTdI$|{unQG?nT6bkB(;Lb)K-VL_epn?mehO-qrCDNn-o5&~un)qFswvsKZ znXOp*U2@o)E74Rbmm%Zj`;hD1%8QJsZp%#_?2O5$u+Dm)~ah9BN4n9&VU zMo5Cy8?c9gq{-jmf;G%!fxrgVpm`NTDHxWZ`}<9scj7=e<*CIEVG}a0jkyQ1Xj$_7 zcg~Pey-m%k4q>F`vpLTH5mLQjWb;$EY$wD@v-3V0&U1>2i85BQtkL9ikBtNW(U1)i zaA1uogFTtMCqOQ!KrU-|y)0pzX_@?DmRE(MK&ctSbY>-&^KW>;2c)%K;(Aw8HX3At zHiQ+pBHe#`jZ9EzM-_g}W`o$Z_-v)Rq5a7tn=+=}v1HG3AtTRbDgA}uK6&AM_27F} z$@?n@+ow)9BSgi}p90;1_(;Y5C4Cpk6el_0&S36+d6IDFQ}~xulu|?TykSZ#DF;h% zZ#NAnJTOmW!7aV7s%JktydgTJy*~PSHfW}?ZL*3nnt9HuSNJ&9p3X9q^gwkyhS$gy zgu495J8IKCzQMIhVvJ^Y0n20Pf=UDya*9wc=q?1*syikK)C%g43;tDp6Bt?-{um# z99B1^hMvngcff^C*6hElN?cVfo1=cPO#LHm8E z9NvUFdnNI@Z2xT(jn2O$7lMCxhMB&9??w#bvw6Q*yc zX=S!s-ngRW1gOHEOkC_XwXI(MXBCn3J+=2ogtkjD;ge(KxHkTs7HbnCCl!4dbdk$8 zZQ3utqwNM}LVG{R65nNMfbmI;93KdGMIqZjUEU2jhma!l0^wjhCCHrC<=PUc+nA43 z$vF+E%P9{P5VIbcfU?P%i#y>Cpq5Eh6fEB)RZBu*xlFwr5FV9VMcAw25%sNfb*|fB zAwQLtJF-rRiWr$cf^O*VNKXKdG~kH&v71Fy|}IOg$1KMF0ASGD!dNxE+%*f)po zk$^VJ25@kkO8q=5dnbNs?D>LLpRVY}ZK?HUuey<Zcvoy-K*8de-s7Q zN?!p|-<8kZtg{~`1>~pv{=+FE91JR0zDjDIl|KQBmsMTo=wni4G2fvNz2^;--KSWd zYLjGKRf5?R;Hj@_87K3pDtmglU;@K8x%~u;L%I$=7p^E+q6gVDMEyT=73F4>HQI5G zvXPQJ$FgT>?>VyKJ1MekYlz1;{@p!m3%hQx`{MozMv3PJ!GY)6c%}W=S|P}C`42BY zZ0-YNE=>QN0k|3{pP#%H2;t0l!bC|aLP;v!Bf8y(Tg7s!!*f z!x>$LicyOfG#Fce`78;?P;7Em;Wg$G&J>ai+Q7?vjSJhcPeJcY6K>F7~%@!@6#2t^i`9B#;WIS3Mj{7dB|D2Y@H3a&l0wUE1Np zZxkgKViwAvqHM}NV+$D(%ORQf@Y|r-I)gtoic6q)bSw#bB1}-wr8x@#vpE4gB5%`7GSCT zhB96$bhR!gy9aZ%?cC&|5qG!tZD+sK2IX;JbYs&|K?n8hj_&N4zI5V~sOaLIe~%?G zM*|=_*7Y?2zNLz?F{O#~hYnzb;7SLBoUx5EJ1@vvK1V+07ayrdQTg5IWn6m%Wd$4g zn=9OPWXFqV^@5!d{9yrwz=6wk(Ze42G8z2{3{l_DpRd-LAjE_P?G_@ox1F*6poW~HGDPMh{1|fuvd^X!z3uD=Xc`42M??cR z*;nshe)P6vs*I@pL){VVts!2a6m}}RTUOHjV=q=3X3*^mH9xz1JzD<5Ypmi-XaMqL z`AeQzmb;FHJvT8u!9DjzOb|SLy0bV?+X?oV{!^#^3NR;TlNjQ2^1?Sq2YK>;1evHc zC%i!J%eL+|k}>1BQxCd?ogr+kxYyC?c6h9}1Ia698k##GHkrGs95&yr$z8qBop)Lr zW*YO+l*2Rs?Mq$snoKHa(=iN;kBwn=KOr>4Wv41(K6YTQBrrwJAD-NZHG8au?&u9x zpT?C#uIgM@xP1XAO#8}@6{Te4=0T8TKg-tw_fJw?Lx4;kjgF-_ERB9MqP75%p63qU z#vz2VQ?NQJ9_MC12Vz~z?@4M4Hg{2%cS}>OKY)j$w!A@kn%{0-iL_m)B|mwXr^CMP zM~aYN(>#x7^oj+rs-Xh=HdGjlLKmIt@p$_{Rp7__0B}3X2&iabY{+WMSajB*i7#TW zC{FmCRkY-e3W2^~h48iTNa_NyReAijQ{ygqU!v4EtQ9Kok3kA^`SUAEc z-SV|F=7gS>>m!@&AWO^=aP_sCnRk|@qBN9`EQ7?#;vSOZ7RK>c8JCH;QAxXynv2-fgnd6r0x(5?Jgbba z-Z=3ZM5;;dUi1ok)DO%RKAS7gt^q>$OP^4MvRZQA_F z|$e?r^MQ+0iYQeJ!b zW?1r?1gl!~wg31L;EC}B(FTCJnMOOa?mU6_(gFp2{|>82cr@^Jg&WdLmd&c&^0sVoC<;Rqb-wdgmH{F#8ndiZ`7w z1N?P|Anwt1EnbZQgRFD`^$k&A>A2Qv@vv@jtgh}AleSOa=&oOif%c^IK(ta=#9m>) zZ5TcN&;|b}2mf@mIjQ59m@-L^!640Zbk6rlHDn1EEA(ms#D(yR z{R9~+raw(WXZWLe={J>~+57;NA%mNY5E#Fyi`+jlAG!KRAOw~X&8mw$Y4Wq2sm0oo zSj}aX5K_8Mguo>=$%(BhqGD3k$0!zm;39unyNdq9yjJ*6lq>?OhC(e^QC~V+F&m`L zYKk6I0+N*44@=z!*J8B4*?Z>?lpC_31>VDb&hSwi6}iPQlS z#)4Y$Z5&R}sjp}5+9z**{`Hk5=cahj1dXfLFVZ#EX%!qwA)eVi3KiHL znWwo^;Iu$p1^jF7yXo!RT;4upkI+-e0oY}(wodjY3xYjxy(n{bxHirrnDvh;n1g6& z;J7ctLYQ%gR+An2NO!xTu?icATpL)u2aYat9S2~}c$5s{uTpFw|Sf~$|ud}{;xM&8!;bJ zyzjY#;7&Z?`%fc`fjO+M%pr|rqGKnc3K^ox-M$15_B<9B#Dh=mh2_cY$c9Xv^hGPnY>5C|6Y zB%^|cvbN;9GJItip-8ge{o9x5oIU?~Ga>;e;V)}lu-*uN3nm$KrLIF2SK*%cu#hOv z&7e}b)F{mmCBIvG(juK~#&!7$^hUb_0SNBLx)Pf{K=B`tvY%;=8>*ZQ@2IMeF@i#A ze>W==MUd;>y2js&@)LC*SV^Vea%-M^7MK_VvW?8KU%X{0rsKH#PstoLT3+_{RNq7G zq+Ht~ zkf@>avcg4To&&Z$5qgB{>!W9hZ+DF}xoGvUqPP-8%TCI7bO96WarGeB+VZ_S^=003 z>#jcyl=Re{_Gsh9w?r6!-u?ckepisDz)Uh@qED5U_F34^1m&6SV01>g;|;HsKvz71O6a>N!c~Ri09k_+Byl-hm0F`pEF_CeoA8ASo+NQ%|_fFK@=*qAhvK$8F zp5uH3IqKc+#~&uP%kU&(I35E^57k&qwZ~ zM16Wd7M%56JF15^41vf;#&X8}j4ekNc%WwG!s$s z+^H{_ElM8ugRu4UXNI?WWMriLL1YGNLDNr_7cd8W$TE;J^XA)v?ejn(jJj#ZN@yel zMttP#DaqN9bzBqrU7oT$l=q4bXlyv)nOgYu$R=vw{X!jr@$)yqO(vrbxd2Obja!22 z-Nb`tr!xhxqhpsJ=QxlFa$MKGG2*9{d8IjseL@`k8BvhJ`5vG2F}e>9PX2SD^iB9) zFuXMVf_gRhslVsNp&BzV1g7R=(&&x(6^Q-DE{^+A?xNbFh+$S5&er-^Rfk)T-Vep~ zMps){;)ThQ7Su&5%Gh0ZGR4R{H|Nqh97rIGO7Jnij)=JU%JuTO4UKXK*xyH+-_l6g z=6!XS)T?8DUF3YdP{^y9QuC?mmO$TCyBo#5x-2pIw~8gl>l?)BeKpkB&fmkde3I+o zD?xz%LgD5YUeLwwyeYUlWiHKQKZA88?3P?8Fid;1Zq1+PDP*54--|A^d1GnkYa4%V zTYm6(@QKNI(E)dj+Rj;Ix=>{U;Mh>_|7Fb~r>XYE%#~wnL!%0Z`-fUf8sf3FZ~-jUIV?aQm-~MJj{kD?gmA#rtKeh5tZ-y{acQSu!%g zKY(uSorOjD%q&)v5UZd;0vqgXMd$x|t&7&Lr~V7~BzB}gYp>Dfk3Lqz2FL5RrY zvpU1;v6Z_XTq=t>oF-zRwBvpmSu)(p$mv~JB+V$!bNLt zFT)Iyf=67%xOmTIw7mR21F4*dBt!;?c?ew5W!A7|-w&HfOzO+;IIiwM@+Y72GQg5@qh<>;H_V^ub4(<8&? zX;Gd?MZ!)Bkm`B6|8t{EM{x^Z5S#V=gJ2!z67HTQdQ!{eR%!_J>zbNNC`oq;u%iZAWFrPsLx|v@cd(&_g59S ze`5TG-NabKOqh7Yk_PqW5S3*lm-R?Bg6oa+QLajsAD zOPlWlu7!B@b-xy%Lu3)JU+zd}6ZG7vTAD%ku!pX4X{KH5A$y8RSt&i~-s|K%zm#Ti+XV z+0#GAh6!I@TiUR9ErFKDMHOUUU-)|b^!Q^~erb3`b3@Ky?1Q+6%&+e?a)JMX0G`Pa>z7kaG8CmBx>axH0JGk2Ky za&E8Q=KTjP%Dj^bPP9KSEX#3FD(m1Gdcrmmn1x7>-cB}*kdt{T{q!kS&LDG@u^$otbrE*v07%A6ft&}NVpb9-T8LaeEe|C~oXRYBJ5xPKqW7AstT`^x+@V~dZJ4271YUe#imvqJE z&3A|O0+5L-;vQ+SQl2q+4URyd?#F$ox-9|B*cOaF;cQinjfJigSv4Qbg}s_Mzk_ zwjy$=V?IEnD5JRYYjf@ym!VDE(bKXUU~Zi*a;LsHYzb9t@b*Goi*1rI|C15QGl$CY z^`r^cF7uSN9w`Fj+|X4 zXIA4=RoU{*~hdOOOO7n9Ma#=(d+hQCtrFEg_zg7*D>_NZy z296k9UiVhw0^|VCg5XEh%tOBjhj%eh+oQSa{?+>ruImf`**%mmE)lTa$emj~T5LG4 zMMGzkO^IEt^)KR{Xe23_@?GjYzeDeB4R_^b)g(XqdS~~#P2-KZ^Y`?(Zp`Zosh{c^IWhgeq4I_?__N3YV31pGmu@F{ru}^ z_sb>KTN%}ThRzh;T1T-Rz1as4{)FOzfiD9T%q>CI6_fD;t~r+9vjYGGo5qqww(bH_ z<-qqutID98>#0+)t#fU`hh;RIe{Ei`(($;r?Cm3JOiN(`;Pky4q@}s)!*D;(Qhn)M z6jsRPi%PzZB;VqKAIWb|6#Hj&b?weqVUeXTt@66Z3y&v-Bmah=`s*hdR)4-Gs($+{ zF{?LX-F`d%;oI64l@13=B}CQ73IOV)!fncmzBqi;_jPd5_`UwJYWQXYFMlN?lKygq z=k51n6AS)pT4#KNwO7^NcLFj;`XA#}ZPMOM|9F}@Z2k7cXDY@RswJZbT{fG4rB5htJGfC))le0tm{h^<_+^m1 zLB}Q6;5@sDp!b3nhg*3B9^Lu(shm{bvk>=o^cy1vH#{wn^I@`EN%>jw?s;*W-&Y@S ze?W$HLD{r0kpbeD~ADBlP<&)_Z~65AH~%*i4Nw>H!6fS@`SaEITm(4bDU_5T#}2-0m%Bwt#be?)*GC5u2)Q>0CO7!o;JSA1wTcHTf%Fn%xCM90^!wjy|y;56v>fZg@|m4r9N z2ee+B-$leJ275>R=~}yWN-EQF{_;eV>b(G^=WDyG(;yp`YwOl^$y{R4=APWQ=juIR zRN{3hw=;pdOnvG>4nOgqN&Ld=?#E6zsZzIIp8=?N#rZpLLj9%P$LwMXhRmi-wg!(p zK3%n*x+=RaUxVcTlD)LPJIQX4JzM=Bh@Tu@L@z^u6r`L?$k_A7q;!z)Q@^h2rQaV{ zde!@mv4;7zoQ%8#6wBFeFC*i(_ra>z3iHeJ^mPbW9^1aP&icK8OPlj=aA7AoGJdM+ z^p$F+hXT@Loy&UPwrHN)2`R)rM?spY3|YPTu$mGaZ!O*U@YBSv>y)diN`KM) z`mX5Pl9n9%Oq?jAL0PrP*u8pRzPi4#F+%aj)H4pBpJ86q|PN&e~f@{vlLwfyQ zs4oG0FZS-r{7fx%yNW-|6DaOGi+b*&uZ}$mB=$YY?(ks~p( z-oRfAa;;^`t>RNfPeFXcxyEia*B&kK16hW%V|F?&2j*AikFVI6-S#(;#bUIt%j`-V z{OQrP+B_|)mOerH1wH0#JG1>g+0syHtjDYmYk>*Pn{N#N*g7!Yw0U~T*ZAo3^Nc=+ zELM9eP-uO5luhy!>ToT7OSu&6aNkf?q>PzDHc~gZfQn@gy|1X`pB#Iv`(wcU$TH_& z`N!#r-ldpred65p5P5uVqh}}MjHfwJKw33j`F?q`g1D_8L1i*SN~x}mJ&C{KgJIjK z2MSV0SSpTv%<%Gyo39KsTKSEYBQ06)U(I`+rm6ITI^}FVG^M$XbYvF_>P1onc0(ql zT{>>%e0n)&$hX>2PV9coEBTklSz0GVG>t1E_@v%GFLs_xUhUpy(benG3yfz z;Grp&9%Z*qC6883`c57IW!9}5*>XSMd47uNY`I47Os8&&8+t#aaJR|0S0(41?(^y% zzuBmGg|2f0JiPYU50pM$Jrg!+hgi@?_Clj+(SgR3d*W^P-_jdavnajUp4pqQi>ce| zv7daZyhbW7_*Oo<@Fr8EjMafN|Ln#G@Y=?3Q{Y9b=JTKY^&CDQyb8&$!hC#SM-Mcz z2ILlkoKL6x&L{>}?!`V0dJ7(90S)Emn#xTHFY^ZGvHTd98G3Lg#VFq6A`Z7L60O8KM^6Yyhthg4@60lD$sShpJac!E@_P^j6TKUsa$T9&$uPudE*G&+Qi6p2qxT zkaG>7*Jm|-5O zTyZ>fyWp_0v12eLZR1P<(?v6&Pu7EEdgOY!j~)t(73s11EQP@PAh6EM=u}nsso$Z+ zj5N7D(t7`wLgnbb=D(wUDXflaW(%}f)r3y{0^9GCi1C^QQ<`G8ksn-uPC&mGadi5` z%vlJ3j}N@!o){vu2JLL0r^cV401!xSkqt16V5vNRq)b@rPx5eDKyvi5_|b_k+iVPb zN-4Q)JJdg+is{|)I z%AW{Dg!Zvr81@A>6WDf(U9-UMnmbW(_NTf}tTgBEBzUaR(|}P7In;e^0b@~!e^l_y z&8o#Wr*U0^Lj|}~9qSJKBso-rqm#+rSbjLs(FryyDy)0OZ$-JE;bH8PUom99 z^mr-tCW)Tv3D(y?+I_sYn-Yt4Mo@-GdS)`EM=w5wo$#iNzoq;G&xJ)kbK|ttk9c*5qbh-%FpS!3Bz9JG$E+ifgrFf7$b~ElGXo62QhtF*d{ke z?JS#Mj6R>lozE{eMi&b4RCfG)M9AGab>kJ*R6SbbS>WV#rn9(FmCRc^I*r&^zDo^b zmrrWaD}v}*o!?PcWTg$GF!^2THY`%qMb!%Vfvw_M^_~(sM%0)PIJXf&na$2Ir9XdM zcsv5CxYQg))A-H4L8q7B&F2+O9|($)H@w`Y*FwFR{*t4>h@`dhF z(>AgNc+a|jy#O8ME^BabG_*1kbTnbTYG>6uDJ&_qGBYyp*yQTIV!vx}`xuQA_F3ch z2_6PY-HF5tEj@Uq&>pL=pB`&uDErM=p9Zx9erVOZ#H@Rqgf%bfUQyC}z&9Ph5RG$! z>74d73lJyF*`{wiSUf(BUS2)B%iFpAK}SZ-y=~o>ooN}M{Ps#gaokUHHt@07(0_=<-{*_SpU4_TI&od{LH;vnZg+@0*)K>QG?!&Cr&BP$u$PB|FN;$!FXORoMk!n0W`$Dvt7?VH%XK4Q50?79GrftMuSG64j{jr&laOZ%t^jbTq@TC16eAN9H3~YzzF%j1)ZN1yi zZ-1Ivm+&^!(|gupuG&gWSf>_BFI;0_Jk(XNHc~t(GgO+Qs+As|cJjyO3H2v^e-u=D z&M!4|F<=sq= zweh}R2?J98UERX_z~P=CpTU{SFY4EkLF8tIB&_V1zBqj6m*meveIyLF3?MXm5ZO!< z)lc;dVbQ1V1P2Z8Fo$?1a%e_eRK0*rYeqUN4F(0$njRIW{Mf@4!3*t1V0T7<#%QvlNI@(Fd?jW@Wx)@ z^_;e+r``lW*eU{7!Hs9ZPbU5)Wy&@0xA1Jc>w%yO4Ghyc7!b*auJyCurMn86)oV%bGm>@Rs z?h2cZF?#o8S;xB(y#0SS#l}@Bp|#Wq@=g(zZ~GLiaFUF{HBl5uyp+z-6%7SYqo)@t zLBSP#w%PfwfS#k}ch^Jg8r>(IA4Mpe;Sqkeo+Nal24N1!?ufeH?vES>OaQBB(|O4(u4{bT^*Q+2ri zwj=ORZ;s$cX-A8^J+YAr4LtJ)tz-5`cdnvU1rm7qm+04dRcUblJo}5i>u3iJC=4#K z_?qaA4%N8yj8T2>YUp;3{6R#78(1fBCz)9N#Em}zmTEf;4(;m?50*dVHc z+>3{VihMA!Unl6GTJcXjd_$6x3rLUh|5FJP~*41j65r{(fW<%(h%#-r-&K?c6 z__UZ(Hv2DJy?9j~55(KHo$I~Vyln6{4)Q8r4 z&(c|m!Mk%gSf>r&31@F$A{}X32bw%tnz?qEFtHB&`I*`_Q+E+9;yZ;vP&~nPb%?+| zc{sf;@E?el?S9<#CON!C=dKI~_i+oO;7#;>+loHl$b*yf(0475ig{7b1%-l+a|6@B z6`lF#d=2pOt9P;bwTrj-$yDPj;d6odZJT#hK2SwJ3Pi{oF>FFkfdCO@Q;fmcto#7t z7XnfxXP!q>pW1BF825$n6Tip~pFsiFT0+s7D@3alYdV?f(v z*qRYNy*({TbKs~?pD$RPwl)@BV~;`=E5u=C4nsk5ZeXAK$z=8^{=3&&T)K7@VuLa@ zu49v_SJM_H{zP2UxCu&b4(|7glIOek$y~Et%Nqe{Qh<}ZVmu#>tet2rG zocH1Ueu3i~C@3u%9cl}*yOuZ8hf$`+VgrkvITCme>r()514~0Z`kvdF9w2zLMt!1> zZfn5^qQCzi=v56ckCzhOgW+l;Hb&(RMdx(wC{4qPXSSn4awTf%USXyb+foTXf}Ce) z{V`F2Iw8~)U2R3Ww6sCLY1MSOnCn8iJqCeX{iT$L-x`3CP?sZlbViMi!_4J~cAMBd z1Rb9F!%w7r3j?Iad^veA>A6+g?chiG3ULG{iTz-J!Aps65nm*$p_uK3U^Xwj48H|p z*{TVi3#2ES`|Q&YpOw(uLM^S=F#qF0Q0?j5W8GF$d=%xrmED`bWHl0d^Q=L$1bzH* z8}j6dw;ymO(F*OOM=sH4=SNg1)ULZ9!C=F1iO3EL zT6g2 zoDR;j&&_8$aWPZ*8(evsl`{XD`ca;M9=3G*_WR^^Z5GF$1MZV{Kb&Hx`6ao;CQu$LFtI(g|nGXTBR+#Z)~{O8bL@NmikQy(#{fl~Gv(>QJ-+r2B2 zH8BY2z<=wrpj#hZ=OC#qhC2(FGqP)SgRi~~w{)6xE#0csFsba?yB z0yGTmda*7Tq?$X}SMXw9m6eDn@;YscDqGIocK?{vsoK8jhYu6H5Bi+DwT8Tkt`8v} zSQzPUL>`j-2m=#oAZF+!tmEt}Bjia^{uQ6KvnXb88mZ&!7nxbvRPQ^o-+0n!=sDV< zImnc>{C(enD5)XCCl0vT9WkP<6Cly}HU{d7#9zb=QQfOaevI%gP|>K3Wtk}9H$Y1R zVUq!JS0nsW<8)ZAX0CzzgrUppVuzNax@`{+(?y)x(vc&f91+xgvWo~?l?{j|4H)OJ&}+z zkm(|_)3YxdFpjm<2$%=#<$;k&twL*fYM4JyH^;n-Pb0e#oJL0g@Gr@TsliQouhDzr z4kV~r=ot?qcD_?q(uv=1RlhnxY=b7AYv2uot`l{_AZDp=MG7O z)+DbPG%7q6EHnckUnMiwe$df5=4Nc$Nh?!XC*61j_FCJNjAk`R5zD_(-ROLxZn;*T z%f6zzPj6I)UbNguBp^+zuyv_P9c?#~v2`Xsy%qb?Mh`JP_@}&I^xJT+HDL8g>vWfz zx9_UGX%?Eu@Y}?Rg`y)K-H*@w=^7cS*|C;rE91?)&7Xc2{+r@Y>wzHiQkLXCk<}X` z+;bIY3{l1m!M{}eV`%Xi#uF~Vhz-i_yBFJ-?ktw%&_0M>yBHW7YObQxJ*~ZL&>SAH zyrw@H%}xyI0ZC$CG7PN()v+IC(zC3D8ao3K3!ocGbA#mW^xPh0qb{IMLm6)Aaj~aV zJ}B3dZBQ{s{8fuO+OthdZUQV6!M#v1zwx>}?VRSQeI94FOGksH3ppb?Gkf9{Di$ga zdq|geV0LjzGN6_s%^MtR{M*`ET0hPhABeC6|5nSdL3$M!kjsImai%p z3QBpf?`D8?LO>7#yA|FhlEixmLF3?}b(|DP^hSpXio#RI$C-=Y5frZgMAfZ%PV&cn zEJ{dTuSSc+(R!2Ht_z}Rz_uh&QI{nX65@x`P?O+Of8r!4^(qw&4B)QzF=pDU0#q1D zCnDs61749q~|T|MQ`6Uz_#SYNpT>{l|ADnyTYhK}j5$$cVxloTi!>fYP0v@;c> z5gRwFW8uq09dG7|Q_A)gR0)~iqdbX(c6CN<{Sp`OrCZ%4YXbG4DzBS2kGkn}0ZKu} zMc6kY0njYVm8Mz~ei7ABIkyV16L>4lcGQh?ob$8aO^0)Bf4@z01fpEshU?3ie0W%Ad6CL5alg%@q>4VPYFCSi#8Z6ON4xlDT@08(8_ei|);6 zG7pxKIdBiRSK-;@!U`ykK`O)I>po+|IU9i(+=?T!T@h zLZFFvCS$kr4jFd~DD++^e}E0)FOp$e(P3O+H*>!flzO1~s|`XfglrVw7{(RnFnk(1qc1f4N$&LYZ_EsXQ1Zhn>nnqSB{CSO_ zd@OO49EzvAD%3%e0rFy}GTyuq>U`HmVD&G;OgWz5$OFI$vU40a?ajEP&-)kEUP4WW zf?mir5&-Bzfo7Z3Qe;RM0-wvXJe{3L$O939cz}A)7hwUd*<|{kX8|^`A=>BasavI+ zias?eY(A@c7Fm3(!E_ipKaaOL?4s+jdhDX}{5yO3Ir-1wVV9r8F0#K-*@dmHX|Rbs zL#M>U1mX#;HE>x-FtG9mo#9ySfvrMFbo+6B?zS7a&8sEk-;qgRi03vjZW9UIe}vew zCesWiVKK!(5C{884ogHxPKE$tZ?Rha^;~FCBZ*TMKkANwK5Rqr! zp&2I+&tDSCE0HPS18fa z+QkMyvq`@(E`itCWQTDE*y!&j+C&4w)*)~x!gE&nh|C-LQM}$T6ubNfdg<5h@^F-1 zf)q-{*)2&hu7+QU;Q##K<<;8l{!6OTdL*=0sDFjlsnXLRa|hCKrdU*UWQOAp6Op>Z zT%p_sAfKni#i1&GCYbleRN)Psf7j1l36=_Vf*$@Bsjcy->YXBGF!EJ$Z@}$Mx*vR` zQ*FLqb>VkngHLkPU7N!o9Yc63pZad{2NU~i^N}LCaMuoaAaKdOY{Ze&&@INKY`%S9 zz8FjXw0hV4c#ds$TOf9*Kqb6I&Zqhe-VaNvCd?lBMU!bJmAvV*m4aK8uX@3V?(ke& zXLDbz+>E*>Zkqprq`NSu`ilgNCn^ZB?$ zoteq7(+>LRbgT{LFA6sB{xaOovfFnH7K;gEZ#%NE$H~TT48wZR0^L-Obxv02-J{(C zIVE{dBTv9HHTBxh)6oTgwha8B)J|q zm4b3cdH+U~dVV`^VAOap6-+)bYq$9%$km(QGiw=XM}8-mBP1d<;xn))_UBlFHT2b@ znDbLJ*ae#4+>0GqzO(W}X8w5>TD5q8}k)dFRBIi8)PC%-Ye?w~P5gOQ9!E z7RR03Id{_D`>WELZ`s5>3Kju1H;TJ%v71G>PnH!=D77-|6@UTA!%oPY9Gvm1AQU zI!<)g^(>a-5EV_8&)v*pIcNU2&96vKeHlp|@s-~g%DG&egXpT+kW)+{pu zg~UFK_pm=^W7-%vG>0kkGP094D1&Bn1JW+X1<6fSJ=#*NH-$v(A$RZy*vSVsYvr38 zdiJq=5wa$|UqbLW+1`ZYJdaw%B> z42Hsl>&_Dfs5qJLHs9A#rxtnNS_u&ot^(CqK}O5r=c~kBuy6t^&3!dlC2YV#XeY>1 zj0+8qM&HYAe3FBdDLa4JUrEK0eFyJm!BLdQ1%g`K!Bbq?zp zv_J6a__r(YM=DWuvIBT@;(YZWgMos;%D!u3IQ?!%_ekCYF`tT<2^FZ0%lPrSp59B) z2Y5pW@@oG_Sy3~*65+Ya-_U)r}s`WuwlJMoE9@P{3# zD@w^t;C;BC@hES^JIpg78r%=qgmUjMT}dQGsNa2U;u{ybg>|f2sPQ+u)9Qf~2f&4+ zL0cON#w% zF;G#p`p|*IFIqp}gJX;%N-pbs&+&Y!^!%%fYmGjqHfxQPJl1b;zf6-id41vb%z1Ju z8d_+0@!|re7BauLcQltHLU$l9>ZMHpDpH&*q_~*xCP*>vQy&urZWfQSz#g2K>>h@zuCz!ftxP z6mdwoYMhJ?+utm-*dA@O*jvCx{bzBPQ8^jGPf>%eT8Q}$Uqe+^fb`z(#{mP}LeV3E z#nrwMCG5b^H`X^aEU@$sG`;MxFBrx?EjmyFoytn^i2eKQE6?Mr=PSJ}c1(Xn^$YXy zJt4iUerGubJ?_Cru@@S$r+Zx)t7CYSQ&;Huq3rv8*Js7c8cb){@jpp@>h9N5umU6h zE|@&%RLWHZOz4tPl-0y9LWz3Gj>sv^Wx!M&Q1CDj`-Z46qAyz_#_mAg;jv}lM(WFY zBXrLs!^)<#r7bp|djshwqQiyU1*QK2wbtJUbd1R9BE=80MKea>;muO(U8%}Wa0q~= z=0R^Xk_InS6LJn!EC5+q28=H_cwusp5ToLgyqoB(^gEN~*BGs8d90k~uLl!Ww_eUo zpNUux1;m$Wd9oynyYXN>mX4!8wfPx&DMoU1JoBf@_Jca&-1c;B-5#KcwLRo%NHNJ- zKgct^6jC9JQBDNKcBXhSPSC)9J2bkdy5!VkjU zbt1X~C3%lOab_(&-~O@IB~k2=!07IKJ=3-QD~;eGU#3TTqE*FTU=@x!1D+4|!#f%_ zQu?TH``!70`{Cm{KNNT$A8PLYdq11^wz3KWe`A8Qq^TYSEk3_dZ(`|Fd(u$9GaWrt zT^|4H=JQ*cS0<_+z9Q(@@TTMoIfr`D!~M&Kl7dnn28ECpnc5;E&Rt?*{P+?c?|{BQN(az%kcp^fZuZ;tYo(H$zQy1U8%5Fb(CW?@|1 z);rooRVo*UV13aCqN{gF?H^QRDdu zCXyy8VnyP1oNk?6JU>GGf{z)K!V)w3Kyw(~+g5|$Sp&-ZD?R>1*Gd&Hq*)wHR z#tZL&Mg!Xy;vZz%V4&(Fs-dj?AsYHh8yT+`CU-7~e!mCbTkW|7#(tIxYuwt$K~Yr0 zry{rN%$y*i5K$1Z4M0uNsS@-T*JwzyVpg5M(ExQ}$Q$ihV4!Q>{x^z3(@?O%>+sB( zc#o)VyN_HI(iZQ+`vAiA$P4I>bX$UL0G@j%2;_ILw`?X;rv_!7Pl?R)|y}TVhpChj&gfH6r>)OFuvZnh7d)r;ufsrg))^mKLMuk4m*$E_Pk%8 zw?*Y^6mZ8B9MPJLSU8vjf2HjK4WI|zPI#Uw&B3kRA_n0;4;){wFL2KWU;Vx4Pkm;) z)Bmx%9HadWw0w5g7|v1#8()m{8Qv~Jh|C^RljTr$24HOR9eeHTr!C5?kdx()*I%WA zY@ey2lr3JW{t>$q8y$P9vRLR=DIblKN+vi)bJzdu{vStY9oFRIcJYlN13?f39nzsl zN(mc{C@rHyP=O&`V|15*2#k;vHk4LEq)S0!G)hXxfJrwVa7HVcX8B^_P5)>IMU9RjP?QSruiKg(_oy=ocIShY#!b!XaD|BJy9#^0iZ zK6DhDmTdh(vE2Y^7GtklD`O>ci4g-&7UCb1KX|;!$%xO zmYDQiNSp{Ir&Zgn$%48#)NrrtIa>e&CQKkm} zzVAW;bxhhmHi2CJLJo&2`_S0VX+YG$(5vU{EpN06U#PIPRO_2T4#GDtl@E!-1+_Ra z*3aX)7kRGAYlm$Y>-?zrj71QqUAe@WE~I>TWFYlZ1Bc{1fStcMAEp21NB>2>-la8o zuQp<%8l9EQ828q&w=HVZZYnPs{w{1)Er*n?e-oB3E%1Bj*Yd%d!Gn}75arBD9eRk< zt94DcD;;g2q{kz;@`H~RGs2$g%Ry|XR9sU!V#PaUf|(d=!mQ6!^-SxecAt@fHI$$j zJzUgSIc&zRUi8%C)Zz{=ol5-zYMQL{Q9;TRX19W%qJO4j=q=y{P<$^Q<5WY}d)h`i z`|VzdMBU4Wu%!3$@TDaBkEV?e_wR3g(9!4WjS!t_A0>k#twXNkq~l#744%94tO$ZMPPu|=y|Aj2!Q)+7hUseee-Mawp66^xm@gS=@?KBCAvSKtV&s4 zZvKus=k9DqWg-_e`(}_J<)h-XS50G~Jb=ghIz_LZtHw5r3N*MxhZ#96vmb31`*DVV zfl`my97St)?BF-pl>zl!BLWX^Blk+bKPv?p|);t zrI?!)$a0t{Ql7Z4?7cc=W1@bX)2rjdR#Xn0i6VthPa09)ckId4)!5GVhHES{xYc>m zHoF(-8M1}4JxoK9z>K(|D|_GWX;Gg@WL3e8xe=2oJIt<H{iS!NC?7paX6%mSnP}5kQr|)#MA4r<-8p`es)> zHau)R%dY~ms;ih#zswoD&5aLYQVV7)( zKPEbuQRi(&{P_ySLw@9d*q!t(4_=J(U2 z*{yRsZVU-ElQpb$!*?|P8da=gs-m6vA?6mRVSYlQlP63D$P|-^j@z5B8>o)M+N}PW zXnE0A6uvTh)EU~Mo_PPnIXVvCOu2*YO$#XF_ATYZJZjoPoDPPi1jPSXCCu9wqQDc~9ko3@f|Iyj&S@;lSBYdFmDzdhy>#t{0BXaBSZ z&T&;teq^{u5--{WerGK2P*D2rujL)i!pyhO?d3W&PMnQG~5Eal?+bu$RlD;$H z{kxGuLl_Wjf+jdai0vcXQTn4b6d#tT^M=~%?g~Xig2aIp6jf&Vx zC{%Ar{Jb-mtovfrhZo#kI;(&FxIPWBb)kr|CuqAI_trn&l#hb%gi4rso497(E}@D0 z7V+7ysWy239yB>eoDKm!C~&RikbXQimTf%otFO3Ce&&Yn4?8d0ptUaVRN3@(2{XYU zl-tsd_;{<9^+YAM@?|EKcFMj4Ys#O9w8Pap$)*j{qwda?mj1Ls-l2ie+yt3}G(Xu;$JF=a0ZCF|cgsgFHx z4!Jny^K;BCzGWK}b(b}0No+7j9C_NhSKY{EI~`&_lDzfeX^>}M;gn{c{pmMv(kQxZ zYru8hzQhjAa{pt&5XkE7!UwMQz`j-pD-d#W-tyvq#Hb~xbz9s#G4MZ7y~)RQHAU^q z2Of9dacU(M%NgT$v40D8Nn}FkW3A?w~6D4LyJTtMLjot-l&zD4#77x?^K>05i zI&f`0yx751g00P|`^+=Q)C${i8WkN@UI<9iOO07J^!(LPmY0~WC_-8}XHCzQ&hd;d z)oEpVf9k+FyVTvw$yDc&3z{d$^E+g@Hpo4Xw9|NF=M*XW6cd>gJy0M1+4nE=pMStI6^;Rxc3E5qL2-uCQJ9koIuKa!lGrk-SM2v63Xq_yv&Bg06_zg5b@c~9cK zS-(K`qgZz-5i1(ZY+|MpKHkYn@vln;TrzEB^#X^4{{u}5sDQ5mOpN@{1*dLqCI4>k z+r>U+OWn;q6bf|_#JefS;zm??=qL{cOm8?nHo9PA&BOOymxDReF0gIOuHc<<({8Wup2z{b> zFYcef{PT_KXZ}CsP0Z4I+ch#~mA25YeQx)mw#gDGk02XQ=YH&N@3h~Q<$ELf$L(b& zF_ZUgut7w&0_Tu5TL0p^^Cy!l6T?>_FUg=v*4D#eLr0ia}gKzYUsCmtHDJ|C^& zp!(hvAUAg>S&G}*n7i}aLLA#?IJ6K{>O_BQ+L79&+AD=q`^xlDURqw@+I*)GxK`*64OT~epZytHnN0a?k5{9nN5R| z-Z;lMP`7m<2+s@tycPL$S;Az;zfJ${wiHHt-HAK$oqYr*YGSv1BI&()&0jlDZe7}Q$w_|D5^?Bpl*7*F=38eTIV58Jm5%Vt`m z#CkkBl6{v&bP(j=P%$IxQIz78uylmnqeo4Xlo!z{CzUBZz8{JYThZD3{Jc_IRS%x} z-I{g6i|2ouEKJHrRio-YDr9`QY5#eSW4LB$>+=4pSn2Joj_>!t8sw48-NZ-w@K0$h z#hZb`OQUrrfok&KZ3+7QH^1MhKx|EYeSP=aoXj+Jr`W6L`?p#Nx=K3)v~jw%h~xTQ zY}C$j9d1N>taN8uUrbD_aazuf$5k4nqttU&r(*-(j=4&PElky06))L)8 zRr%?x>Dq~A{-DMGKv0J)LbT-cwFI){IfvU{NMdpn@5L8C6e1@Q#_UXXCn7%mF5?@P zdvR~6T5YJ_Jk<;&&y*KeY;n-`GRP6pC_d`hV4^zK%xtwpjehvEj7N76%bnqBEdp`X z|Hk>+nF&-e2gQAFkltT=_<7dC+{wS=rDxMH-*jAv=dQ-F>=*g(6K;irmX+1XGZeJL zA?4(|Qn>mJjm1k3S+=0;HE#^v{nylD2xab6P8v^=9IEKK_oj$zoO+G0>)@dVjW%-( zDCHo)fBA~(<~VP9hsacA*{VoMu1P;`(guAbf->keD$BD45Nd0s0mU1_P%As)$ z%-ar=3?F3XY343?v@&c*B|r4oxM-o@IlvYcLfJ4wuY$)Wr=6Z_I^lR_d?F9WtDQI* zc-~u_k#=19%6zvrUs9%Y7=I!de@&P?s)a>KJ^tJ;N|;&3Wm^}HGD}X!6>Ui&x} zpUP@A-WueE>m_5dArrf$Gq+yNZS17>Xg$%|K8G8zEzZZ1V$Q-jnIq*oZe5eRlJyHKJf}Oq#7%= zf8MsrX5BNr(`ek{M$Cb?m1qw4meI$ma`^p25VTp@TR!Vo`g|`^k;f%DIgs^zkt{YB zmzC0LAQlfX7y3p&t*aXKtj4|=E)rjRC*D$gsw?H5zc1}`=KU#1utTabP^;vRIcps} z#RHYwMp6Xor1J_{AJHedeOP}H>EI)4($NW*i(BdzA5~93NyM%*4+;ryilogbcbB;0I3}e$gU8} z0SIt5?1Ruu0x`}%78G~b<0tC!I2o#A@7pyd8#Ut20$N!1M95PD7E`h0Po`~lx?tFl zT&x4bK6yQRu)<$H&|);_|G=D_uMX{L7YOb`9SJ%z!}z1hI7^}{6fuOF{_C(@eBiFj zlp00mCBJ*^Nmw^Bn~6Rwyk0VrpU;(vPC)H`mSM$Yk1-^8b&3}K&!8xDXsoL1!4Ht*p!Jk_j+9Wsn9{dBKGg=dUK;h9|vJSrj z{7*fhs^S_vXLrd3@+WV+hcPo-6thRqWvdP5cjfz;-AG9c*bPBGw-mq3&(^|!Q+%ZY zIBP1af7m?TUu4m&m>Xhm*Z}@Tc$ITXhVIoMPTVHO50!eMI7EDQ>RtO7lIY8wjqIcB zv+?P^d;pSFQJ2czBFE`qG~@Yk449KPIroqPF2x7DfO${Be&!E{5Z-6xOuwEkM8eZT zO8Ow2e@RpuACSs>zISw$YAz1pH=|m<3L!m`bpELfK-Fzo4*)MGg=R^E>di$02Jo%j zAH!7NG-YLMIr&!KdzDqwWQ}e3?G8&c5@-+8~+E z!cMUlH*aenbAX@;rNK5GvWXoP_mD~IR${Z084m|QQH>X0-q{bXsnJLrfC&m2C&dTn zZ%yss)@sA0QhP392bAolgYZGcSG}JVHu1Q}kZ=u!>|cgCmjGu}}Sm+#D4)L9XF)c`Z+>-RGvM2`?} z@G+o^Dx@-0U3;H1)35;0m}gTxtj}|AVOIz>5LU{0bW=Ye0|@~-GdsG&VCk$0QUXd1F#G-?_%cyk!)u`W8>2#>DfZQ!s%;C` z-Xe#x+r@r3JjsVhgYPc5dw4+q{ffTtmaHB0Q3){|6U2Gzao{fCiCNmrA6_wDyMY3S&^M=ePkMkar%v(CLFz-W0)DKr!REH5SHl0Tnjfo>_6B3Z@? z+5%``cn_X^*1cz~t+F?u+eJI(xtn52RM^>U$v{Z!C&Lt%5?cHQf&gqiuj_?gA|K42 zjbKDfcrOP-0eUclXlElC6)^?>@&`$BP88onsOTFGH+Ni}oyc9bPKgg)x%$UC0^)U+ zm!bVZVzjk*;-feV2YK&kMeWZ=%ug^5-{BDmMg3E=08Fg8%LpOj*EYn3164VWKrUXKHS$$7mXu_g)y^#6=4R^6;mr#a)|LS#Tx zu5i6h0ZSE;7k5swncRMz-=zgF&}I_LSilgPjH@5LY}?FdZur=&C&d-Q3f-hxH9M=b9i)V zf6Y6r8J}LCOK~yxuFqyf#~&1Oc6boS(KI>|W`SBh?@FZK1PHD_45l&V+&+=MfsMtU&I>3sVeB)iM8ZoX4M$m-c^9Pp0+@XL z0`W|j3$an@kBR~=qdp4A`~TWjKkTpg zu6zI`WDoBFFZ<8$v0?``YOKe(Ak-?z?tsw~%6W7m=?b$D+?N>1Rj9T)z`x)7qYDl- zrF-;MX~FYj>f_w&lj{h{1rxp#dCE%%Dwp~8lN*#2*DzeCuigylY1Ew%1t{rnjkIVq z-GtA4*0f|LvFFI%g&!av=%~x>;dghm$>9PGrh}9VXTRg<570k=^vCHFDg{?xwi0w0 z-w-x&A!0+F{94t0=0^7Sft}d1X8H{c{D+;spV%b==SgACc|O-!G4IJz25s-Dh%`cW zNN<$RKuy8I<}}3jR?<|K;VIQgsLLK+))iCEkH z*Q|<29f!&O?4+xTykNsngPc4Z?$QBhag$3Q;wXc0Cd2kWBi7a?j&d`}!s&DXuY>5d zML=1~4wi?GXOLV+R@kGl+5mywedgsZ0W}Yc2yxR!KUA{s_osSO5P? z#|PJch?BVkevPxWcUT1)3G{vS?lpZ)rMGB7x;bHOkyLFDf##qXO>Yb`cT~&^l;Ap zsSj_cdfd^rysTa9yWGUnP$Fxb7vL!5)Tg{BR`nIoJ|p0OmsMWi9-pG`NgG@1j@m_) zyyDl#c;QbTdSZ3IlCW=*m0cSWatdwZ1UtNQ}3(goQ&oW5t~47{L@DX z6s;~+w&vPc2Ii){4{VQu!;ek?sBVWi9k;mLS7|S6XwBnAA3~<$N-j^uUx|y7kFUsW zP=0a=SD!*}-h2TxH&4#bm%&9(8~%5m*&K|x5AJa8&ae(!1-tXcT{K%&wO-oaY+h*q zx=ER*d|g|j{T+z5DxO6py2 z12lLJ!WqFlOv?)?)hXELaiKPW#7rvo(OU;`PZb5HVG6VBPp1qJ7L062MoOa_X|j2o zA7MP>@56(CxAtIRkOEa|YjE}Qr@f0cqWAH==o?*hugV}Cp~t`VOg-1Dc36Wc%%x2H`pyY-?cSr|q;$nO}EM`(9^DrRsToip>_gie;+c zHlbH+p+_f$_hGQrSAhkbAdpnu+z=!-SiwvjSev9=Ve}MsL<6J0=tABGfal zZodS)WnJ$h>9c!8g7qd2p=HvTZMUQZ6vwNQTu7c6{qku9C|2TaIwxxsqA*QC|7h?m z17vBCx`qR~dc-{>&fE7%Ys&Mft(@WhNBKkj3Thy(?u{@_A569<5#N8HL{_N6FmMt= zv-$xT+IJl$}4;yqvz>Q+**4=eNWM&{sx(9@AJhk7YF&{Iq9E7K%hAOQlfPz z)oHZiVAxE2cWbvtVAa|szY35=vxgx_MUF{^uBuatW5=Xe4B6|&l;Q;x_GjqV`B}HF zO&cjuf3%A0<W!rcAQtb;)R?8IMe00ck%4Bb9Ek>`(0;TlR- zaP7NdlBj|WVop3TJGwT05nKm#!(j}hWj$;KbmYf)I%mZktGqO`@Ok_28|Ch~|3E&h zhnMzX>HFpkG?>$4$&VkL*w6jWMjb!jwb)+7vH=%y{_8di7bpY<8w|2%iz1!Wb6$>2 zWuZygeyJjGR8kHB_UJb4_@V^hdjwhJ6m4H#E$9u-*EbNXehoh~B+C0Ibme>G9@M4F zQ~2gSQQ;yo@BBIJ)Z#eX{L0~Z)cIukNdt4&zRcm-$Tyl8b=QV&HhVG;^OH#a^P=#8 zVF`96)X7`mn7!Gnx5Q_#Msy$N&;0qTumY(co8g6<@cg+OK=N=6jjLa88PYra#Qh|i z)YeL3uW?-L>v5g4xc3#I8$v@GnF@XS^v0@mFFeuxn)))+OLZ>vyyRYAs1X=wI^>&6 zeAAI#sI--U2U1|}e<(ze(7i&&OCp-@MjWo8q;kLOiOY$+V$T=KV&`q}d=j6wkL0yj z%Py#E=1C#PQAHYDz|lHV-2=88BA{y8+;O>}_yC@3%&m<%P}KI7h*F4+ zJd#v$&e)QN>cS<#uk!eR9?1vsHz;^4(AFsSt!nJ|>iqOa>wIE*gfi-~`4D3WM|ZINbNcYmwKYL|ZC>2|Zi-G=B$9*Fz|PdY`oO z8LVX+=qTwmd*xF7DT&Slm>|2w2~u|b%DC(>B*%Js%4kB1L&hnV=hB#RQ+73!ovQWJ z0?729$EN%4|8bE!0#quEDUSv&Ou`{|WzzSMQ(N@NZmvxe-bu=4aBv>kg~tUiEtg!# z8j8@irNMzhvxFadN`ufHtc7I?wqj9)b*8U6FW0LQkuqM|(fv}6ST1v(m0%k;gSbA)p7Q`)-;u`y@lmLj&aj9>S0Cmp zALJs=bJn%t_Ww9Sa<|2l=pmu1zua{^o|(q+tE9mMG9V;ox;rt34 z*-AoXA_&^Ut?D(2f!TeDVsQqqcD&?+9)Kyi)CMj*5AE6mdrqQxJqz)f8U!w?{2P&+ zxc~9$L1@)Jj%vZcRzN6;P5hD*jUM_i2rs%ZK>0I{6bp6(0yXEe1R#DMmLw|B>rIF! zaMCvH=&&Bc339(Wubkt`DdmofAquBJ-4Z$S7RW+W4)-ZFc3WHW5ACdZTs9DxqHzq? zvDm}=2jA>a*hXt#do0xlpg-h;D0PHxxvX2U7M6u(bKnu$#P#|Kl5fk;7YH2n5q4)0SxujRlvCK9I(p2G%j2SBgMQW%qqUT&n@%JxxszK=Kwj(`O zbj4g&#s&UGQ!ft^jHV?!K0aCclmxVBeq>QL2uqOtMW#|U;I0+%|`?WY)-y?-1H-Y1BRfwNm4MGt18imDA34%qKBQnuGfDDG(gn{WGu?C;W;FXNAdWx(`u994fXF5@%DJRU zs*p2KQr9~!77G5`COPW-j8mW~{N^=;#|XII>uTHK$`D(!wnvS71v-6~E&Y*znTjN5 z$yQ&GjND@GV*oPoXXi#WuaglWUY0B{cpN`;CRFix)Vg%+UeuA*u@N|I0f)g3f7G!V zQc3m)Dy~F>N)1a*50sdp;WD>$Y*4NP^&AQKH zOZP$a`o9@9#vVxgjM|A3ajGaXN%a21bn?KwkK1bFX-ndP1T!M5O{njug{L;QFEqK1 z8$xaW1P9XK%Pi_+pVkZUsh)}>fsfj%X$9m1wSh#cziR%CSg~)i8B)}A724SS2bQtl zZKc!NPWM{Z3|!+*m2z2eIete)TM_*{&T8QZ#c1SSA?Uk*d>(=agdR`4t}FDq!|qb| zSQ3vgg0uO_|KRY?NqVO$H?(vylza4uKEY^?CPA6^Vs`KWxY>=*Zv{G%y{YCl~7P4WQ5BzXslU$k=(=Y4}1rFZD{?#sDOB!Ej%BzqJGbMd=61335zpS!oX< zw3V~pOijOCSHSn-hd(BP8uh{r5#3O(py(Q~ho0ak1|DAeIP^>aZuB$#;5Hb(kN_{4 zdvH;#B$je)be_k{ex#Q=w#-ugc#~BM7|3(T?oG3sX%E}^yyWimMROfvB znJcs&i|FzrwmAO$<>vx4sCk?7arA}?7PlAE(V7=(koLZ~-)Y%5g@SF>NK4r`tnfej{rz9QM-bjZ3|0oI{IgI(f*lN28%^lJV)P3&v`9>2NYa+Gh zqZc{jXYMpS5MKIG4rG~`v3Dg+F$6>*D)ZY?SW-{zR<-2~0)iY>q#YuV|I)iQ!!LP1 zp9l$sB41XkdCu7sZ|9Nz-hosg3$$Y5oI5N$@U8y{6I1WNFMn%9-`AipWzXojSbJ?A z=8+%tQPm@)rS*8==J%c?&HH!d!s}a)kIsztDwm#|KQ3uQ46(`oCD)U9k41}rJ4Jy! z#3|foPgwyo-uckYnwXnKa(ivW5ppBhcU}h0fydT$phZ?o+DtiIDz6q*r9dz7T8teH zf>a4nw9qF*W^CPG-wJ!5Y~!w>a9Ix|t@xZmdsf=rkyD~3u0RrWSLRqQHvS#Y7C#HW z8M6;f!nK<{oGlW`KOPn90dN{Z3&=dBpPS|;#cKD!lYVB=Uqhi9}wEdw=P{pswE zyozfL3=K((CFFtOm{r(p>j=`RINEVJMcUv z<(qYUmY~!>g~1iC8k-m=tL^*an|l8EkzX0M-QkU)4(h8?u!FO$eej5V@pgInmbPt< zs&pm0NsT(yxd1(DG(AUCtV(?kgdtM9Q~O!lajRUgPh0i z$3ZOvbEaSI(BRbm0~L>qs~+T$-4imjsr#sy_TbjNePl+5Hr=31Z14nK_*%}4&6)og zg16~T;u1Wx&yFwIQ8oL6={}6DDHKu>a@7L%_-!Om1c*~qUFy&c}Jyj^XO-3 zbWwdbM~(*Lhq4sR$Atp|vVyJTTmoPgC@FTdD9StnXS0aH6m}#Nz^eSC0>}aTq<`B#BK~jeYUl1XJL)&{P*OO*JYbzV_;^A4KDF@goI~f16(I z^&{DXZD%@&qdu4X%ABgyI{Be(rhC~a4kh>ne`lV(;my1o3H78IuPeh%nvk^OrpRKw zm^Nc5&je`i-%^Ijs;q@i7u_k-EwO7RE)AD|biFa*{&vGTzeMi*@<%Jt_`I%%IJ9}d z+2^8Tabggi_I}Vd-wykq0tIu`hNp{ad}W4+J6h?8IYxf~TS48AU!xD7@r%yhivt?o zq7dyu0EuMz z65ahn;^BEz>f)Uz8S!~F8l3vvrzX6&I>k(#F)xI_aYgPo9I^#~JGBd#8NYn%{|a(0W$6u-dx;n)e+xc_m`@(A&$0;HPt8K*j(0| z9#D+Fiz)+Cl3|Q^8+I`aS95!*AasUeB-?Y~4q0^}`tC&{%{B7IWaRe%^ih7o;uGiV z3_f1G^#4$2W6?Yh&x@96%f+y|;>fj5l}Z{R&4UJIA|CUxcIzI;U z;`g`jO1GtlL){4DHIgNpTQw{uAGB+-#NV)4_@C?y7mSu|Mdj}+`xR!aBui6rh;!G; zs-Jl(E~iuU`@BBjaT5xfBkp}Mp%_@Iv){@!qfX)*7+vt{X+7=|cxxty)03#TOF1ks zc;C=lPMs2Q=DnK15coY%=?jfdYgY&a>1+j>J_(5dU?ws2Gr@a%34OTW@cb<1Vbkvx zwwxM7*jmztQpa0UDy4>WCkqA&-{s{Ik1wk>HxX+on)Sk52V5i^bZs7g5F4n<{i}*G8;5ncQ-GK>`&V!5eb)S(^3or-!&&(a2Q3{p1thh8DN?92p?={Ptd_ zxLPv8M%YCtbg(G5i_ZDFr38o>{Pq;F0GXJpcwE%|$0G3e?Tt1_c}R}R0}0bS)nD7B z@F2wkgMrrMZa)L;;bVf&NfWjJBu%HPX=$1C@K%wF(L+%>@uHy<)QUX zn*O14WY-@b-23|{iA%ZnraHmDqUCB_T9wGTe?#cIp8C&TJiej~+BZrH?hPn4oq_7{T=MsTxYoeIB+=EE;#ZQoAt7HTlFvI|*5zQCocs+YJK|47l0gZ(*41fm2_$83q?1fgjq}=AAtZt7h^Ig+ zQFAx?BotMrP!l{i{VaKXG9m9Z3$+rPT_T7u%AiCXku##A=lPMU;K9JD7Xx%IW^)u7 zsH6T^8HGlZTW;~pY!wK1eE30C*YhuAS8iFGeGr+oFCC6(Tkr_$^$e#J0EG^#2N~7OO~Y6A>i2 z1jk2ihxTb28hlsJU4DKeaeSnxqnR+c$rff!5JD#-EzZZDCOk@8hAqk#@@|8O%w6&g zUmn{RxiQr@=Zr1SpadGM@349@w+`|zb*-$_ixs>zofrBdz7n#)xy^h+R+h1KJFs5& z1C^`?rF^5otu6ZXL^%e{MVH>Njg;&prMLsezT?nqe~HrC>1U~r zU(fuBA5;37?Lmu~D(jK^;c&T3zue)+16nxDH>2n4R&RLvSN;cmEX8YP5pT7Oawy0C zZ%Q6uSlu#4+1+5im*Af?oiK*d74nqLyn}rIO$IT~@;T#lwkGDfI(sBhZY=)s{N|K# z%x}lEba#y+D!D)ZDE+R%yx8-2idx7jlQ`ommfBqA?IEEa6f(&aAhrV)q=4h|=cP&c zZv(L`AA7pjo{PQwXA&F>jV@Oq$g-r~A@?g*X0=&qJQ!hnW7P6Y#=PF9E>xAgZCpBN zfFr1&i8!7?*Md7;)nH$ijB7``MjrC+je^9Ub9t6nR_Ae=31F?m}^aLSwgIpL#Jn>t4?ps+`*?w$kCHx+}!Wm(ow6{h1{U&p<(>p z0q*WtUhlC0_a0FlmqU;1?K7UQ=_-)Ui!Y$l!qu#}mR@$pgUcO~R z#N0f0KdEp51)><_-!K2+u&nj%bjkiL*y1Y>8)!K=)$HiqPnUl5U}?&-tBxUdu?>PP z$Q+l9}u-w(Pm2z~x0O===_0}f(^sbG&(E1?!l5<@mduo=g*iEQ6Q>0EHan$2an5 z!2b5#M_qXpX6p*#w|(vp41Cdm?avapn7@f4t%Gy?S7r;V3Y;D?)Wx6_tr;|U{apGx zwm}sz7{}npJgrH#Cs{APMjKHFP^EN}v+m{s{)hV|lNky_LEzoh$n=-$phW80?NHYq zedQr?w3&C-_tIetjT`t3gJ;xYHU@8;_aY)v5}L=>TqVzQ6uNYU^_4lN6_y6w(cymG zL+qoSp2s}VIyJtJAc}GKV^?q3Jp5A_C(uaS!-qMdXj1olb!}D!%~o6Gy}{{%73zFl z^BxoR5-$H1`iSJ?G5?_+_e@32y1__H_V&WzGTpu-Tx5I77*L9$9+xxwFu40)DfXs3 z3r~J=BS#ClXB=HRfrDY$gmS8ct6rP-_vI zQ?B}{xM;ikIUi@|){}-pKIf(CF+{$|Yp|MU27DvNPqMBN*A1psmIRHci@NtR%?xi_ z1b)_F#XIkrx__^6@=X|*c_(<^D8oin;o>T_Zq?~T1|t_tKLO)Lh}_|{@oKwzZ&sf7 zOd;|%m4}GyQi@(;Mn2V}>V<30G>1cQh4-hyLoPRdOpbJ-XILg#PI6-=)p;Vi{JP}% zhucbL?9=t7QZcLTWCFA01Eq9a<}{|Lv2^yM+o`3#-x$*bi?i%`#+YK&KkL%}VPg5J z#7CM|2Y?WQ~c^9`$vb`9Ux|687 zEw0AX!Zp*t9{*)Xp*qdyHum;Oic`HUj1AmF7-4i6mriz^N^SgCq+S&efP{a@Z2s!M z4T%`A?(-`(n`=@oo)dcKRn3%|nlN&qWXGZHQwI2zw_X|5hLhBGXH(Z|onK0ZKT4{X zBZ1g+?JD+#+BELuzVKL@Hmjze>gUch@4xlN8#rKPUpKM;i^PRQiR9vLE8Bic}vXO-1OI1H&Z@||4&2QrKzzKfA>8_JJud0T4W%^V?zvdwzp79vR9tM5T9VKFzd)vA6 zk^?}h0-Nb0YEnhl|M~>n7XI4%ww=WDbr~FYKqz?1Ic6Q*ev-JB?xUnhbQXv!ccV@Tdn-0rC zTAjy@pI%;VF5mmkxLXmHoR5xM`uOEjo}fhjc)5*TL-+-+ep$Aawnlv#hi@9xl`dED z4f^(k&+RLLSoI}w)uL=Ma#S6gxM1z>3Mt%fRD^er;$zu#YIoP{?;Ym|9E&~QM^3Ec zU#~5R#h>=ITSfoEdt$0nPQa-}23;zRKL&;r(K@pF`c+jN_a?;UE(?FVHk)<_+Lq9K zqag^zRXriX$RXW_^0b1sP657NH+06YPRznZ!8Oe_!LVHuI3N23U777WH%QKXE~zh) zcS_J1CtoKYAWq$b;BDP!hI9II6Hh$%(_bIgKy5q(q9|t6<*|8PH zEsj$EiyVNwb>WLwOBZzhnKnD&ib>_0G5YIvq&16tm3<*vpPDtegedo%7EHN)3_XdP zZ#H_(D+V4702{qB$BnfAGF3w#$1s>l36Hw!ixc>)muVvA$qh;`G(NZI&OOc|Qv2Ch z@lr?%NsOtCEaG#1TBvmgf*!Ia({Moy%*?_mqY;|1EI=~5V4hwz0 zn3`PS$BvER0{1ZoT`0VpVABOd1+&; z+x<`Pc;l3FnmmKQWUx>Q(k8!HpyN%kWfJf~PQ`NcqcP3FQE-K>~f zn`u`};8wkBpAWkc@tZXz8`n%LW27ao*zR-F$>90FFUZ(A7;ApY$9V+v>Emr#WaY)0 zn^5x$mygq058vRZDVj_>2Rd}#8~_mivD##JaoU-H-?>iKo8RwcSgfm#Xk}4eq}JjP zO^rB;3y{0-!oHx+l!H!m$^6jnx~YpzqoG6>tbCX9{7|CCUpiq82=u zkq~4%)tq86n>M}tAl?L_$Pc&JTKDiMOn?p!V+k)HCn#9D(g@zaY!0=PaBs z_7InW3>92c)2&QjjGP+#KZ?%%k*WWW0yL~g0v(uOf9w`49|6uHekWbXI5 zlqBY!OKZqwluJq>WGJ_Uko#pB=90U)^ZB0dKX86H=Y8Jq_v`g~J|9oI8^M#z`h{xw zz`?-u0Flb6hil&-^e7}2O!0~*-d;BpWDL=H^E&g1vCk}}CHIriZnu^^lm=H=?5ndM z%|e~v*Mm@ENr6Cn=dXd!lM2DAk+!Fw!%*iN5?;AHIDeNBO-)Qu9y_135{xEh{cUFw z_}ivY7rwZt3~&r``nQjC&XnuIP{P6FH;P*@Y{^$D#Eg^I+gXK}$ssrL1h-|!B^M$AQNnVA4>M?iq+UK{f#~0R^oxhw!ZI;FP z%kjPW9_FdNcG{w!ITpsN2EM|}U)a$Mu0B4~xL#ht`NWfI0F{t!3j;_|@JMEPzsVXS zx^$@JTtZ@Oy)wZ~dXHjrCB_|I{pyFeX0&KJgP|uNDt+h6z&(W0F7#fKTps!pmAX7N zTGa46R1YUr!aHQ8a*s7Ax+94S=}h5B{inBU9?ax-NEu!LE-C+H4^d9$Ms-QI@aM0N z=8EY>aV*cQlL!w}_131E|E^6=ZtVQn&$r^w`B@dM`0Z3|i)@EjGWl)I>sv9;@AVg) zOUu2~q;xW-lkDB%Jkhk!dmr}>>-x@OKEpIrt6%>7OYe-^Z)L6MK9n8pw#|-(EZ5O? ze46EOXWd#^CCucJjXtsaV$yo^>%)oS?-5fnB41Y}w;*}t#xDOnuc}XdnC!UtzHy~# zd_rG?^B7fj@#y-IRF!Uw6Omso(jr3d5GI6pII=caNjvFrZ<}tuR`mYjw-}yL-gcCT z*J+_)e3RYXcaPdLE(W{!WQV_|?|xSKTKR3zLo!iJ#r?hbIFf4De$~}+-E7J&K)}qp zM_!=abN0>itom)iMNCtV;lkB0`(}$7a<706udLX0NsJx^dA?F2g<-b#W_)!TMK=xo zovSN8<7bcfCDh~Y{~w6&@2)^ zlT{x#M)t6~_X;U5uW}(;MCJM~to;XiUSM$b6TJGyt8vAhGr9@_sc$BH9;KP=nyQqY zDL=PD(|iUmLQb8y6``wd7yVDpa%SGVs@l0nTXM57wM+04RS=QGq=3G+1`wYnP#qwn z$=+hm*weu0xQ0cy8mS5e9@-lvJwmjZ{h^>vSV@QZtM~CPXnFs_*F%w?Jx_R&U=RTfcox&ZnjSGnGIqr~ltu;HEGsw#QpZVCatKRtFpRYey(IyF54hJWs=|=dJ#E zdtL$DS^xJFoIWqiW_FDy)9XH&MC+Fne1XkwBuH>#DDM)HbI7)C{ZYlXUGup1o~ZW; zyTXauOUwFD?pA(~A2e*$O_%$E8EkFeJa$ZGp$awclhaDxlyErd+ks>jiE|ryWVKeEG{GF-57cvN;-+1 z5$7Uo*;PO`_{oDUA;plx_*<}I$e-%?m zkhf-S(eu66;2>*^29x56QR1`4ULRppdp1W1KO6mF5%zD*Cz41|WBv8fk!`Q$WX}38 z5RvUxHA4ZL#ihifCc)8#A zMf62Z#d$*8l5whLSnbiE=w0v^@-YPbydy6M!!%xucsM32^WP2b<`RX$UJ$01@V@Z*@a2J&9aT^W~4% zw)#u7I05C#b=ace4Msl2yU+MbMRL(Wue2^JX}9m&n#WA9!Q1LOCFv1r_{0iB7r6`8 zn7umqah0O&%GeCj{yT{jp`PP80J_|x2r^S->j2G=8QJDhw7;J|ArS+nPdV9>w#ma$$HokL`Y2DxeI>tT2W(hBMDa zGPJr{b|ND#wOpwf@Xw1|c0W66(5ffB53ucA z@)LK#71bGd@TubvGg??)&0h4031S#(-~5yi=n3>&z`hr2n9s4wHb@Mj*@!J;)3hh} zQmgO61Ad7IHn&1Ok_Ed7vCiLI!2Ei%tpSP?+G8(VT;6 z+**JttoaMXIULImYU!#JiIyY}?3wHt>U1_<MmAp*0NTS6y*0Wat-6xOj4oDEz>5x!eRZ-&JTgRZknG{_<~W|Cm$labFs zgi<7zJ|jt+f#de+Wll%$M9Ne6>FwCGwU)%qUy59pHWO3hD@=#OWG(1VK*uvPGuzDn zK*%DJ7x-B9uq1FS-e4z$6yMA8JhWnEcS2jj8`OeC?>nXDX!qiyk7N~;-+ULT=|;OM z@MIdiX%Ui$)_<^8b5{_p>(j8DFArvFy;S#Ur{^*e31`7wLT|0~kyblfs~D%V zrhExC+%1U@T{>&UhdG(QkU@_7(HSW4M-WqDFap$Kr_Qa)$T|Lekg5Fav7z^7NvgPcp^a$;p| zCY%jg@yULf@_tCl9}Rez3N<{qwH>+@bDC2RayjRW2tag)b*|``g11(oG%7WjifCE5 zm2{HvWZIZuqhpM{@{^Qx0r@12MOT;ATg|V>6vH+C_B?E-VROGv*-3$g*`pGDqVdni z^|4}+A4spVjkbZ~I}xr^vtq-IzFV!}!+CY@a8mp^)2P*E<|1;@wZ&;jatn$lHxgwd zg+}JCOdZt6n~x9&IG#%>_vK47d=mFu7v*k_-es3()u#2?%2;1;a_UGTHl_=ygHCrE zcjZ*j^|h7YAAKQ$({Iwp5GRu{ry3_?BZ&<%_5l^r=@ee2{H{=+=y3wGLAF4OzVRc0 zoCfOSwqclQ#L&pfMCIN7ZV>VY;;XKhFO0v(x_}nu+M;QVwW2Tm;)HVg;|K&pfgldU z9g*GXY|5NZ)fOx}5Et`TZA&zb%2J(eP$P|EAak$uNK3Eqsc-7TVNXaK!4N}9D@c!Z z)oak$X2mars@!)tBOsJu?OLNgmN?CvE*M8?NsAq;beHCYJQc$an?zu^{|>E!w9dJ9 zAH&1+u+2qXI`CUN&x7^GDhY!)Cv_hqw)Nu`F4(Y);IgqGQc%Rz@Gv4#`-hky3`Ws5 z3|~iF;m`H?^EF&0=ese8wniwI0aFlD5_8PLt%bpigkMeFVX|6h52~JuEdWlK{5RtS zHLcZ(ufe8=dUMM_NQJc44}+d+Y-`%7I*QwM-8P!DYpV+s>WID4yEB(`>VsoqujmO- z%FK!gV036c7P-yBt-Mp0jk~B)xlyl$LJX3T02P00S^HAmsw&TC$T^=mnG-<}m6ewR zoW!|o_Q~%Ibv^uov4$=(2?!-Mwd2}r*ShkgH5GrtI|VQ8zRkZDu5<9BR&-+FnNLls zfa!p9P8mdG2*w$14WzE9S97MuvC$9%Fr>T%J^Q;V6B1pSTlttcucb+~HgV z#$E8a1d0J$ST;t|B?+a7bcJQm(MmMn5vj@2-sFLih?B#IB>^m}ZHJ$i;>l^R@2sFg zPxl6!$3;+9xeGks(o`TBQhGC+bt>FtX*SG}dDrW+Hp`xq~>g0;l7t_gMgkV~zO9XhgB{=o)1A@4kLOY_^ z0RxL1YP`1fntu@sg6Z)e=!cInoTt%XW5!AAZx3pTnJ)nfI#sgKrXAl0E{G6J3Slnj z_FAG)9R>K5{G6hoL(Mei$;vyC%PZ>r;5q|)P4^&5&ch_R6I#f2td7p924H}&wP2ch z@cBZW?WPV`)4jSA(Ayr)rTJ-HSvV%VxpINk-%rF<0iX>J^#f{2Zm5UYdk5)<@pKUo zT_fASe-4s5B(h1(W%yEMR@fyp8S8IpiV+;+Vh3$=(@C%vK6>g>4-M_w_hch~PjIXI z4}-r^YbWz~Ejpg?&{nMN)pHl<70m(2Cdgpk7*8!sl1^)JSC68OTHNoNc6HH>4c`Q&X9ye|B{*4jgaf2AS++MBF#`~ z+F_?^Ay+Zn_6lH6koEHl5gDr(l-OYbL*z)7qaveUx`<~t`+he+OHB`oACw|+rMZ!7 ztH3>dQTgZ}Dc--Q5V^V$%m?bm6O~7k(++E;)nQOze+TIik)Blek!+{A)7zju17%GE2bw~Ca_`w3((8>)h?Zn`_(CgtoDOiAoEz&G@eM9^3AjFKchCPGQtIb#JNw;JgV{goKPWdzQ=Tv|-0GoSSrRk{KwLrzeuA=h%2}m_ zwv{Ur8HfZi!K7JTC7&t&xq}G@8c1=dFM?A#LyWtH+?KcB9|P54z(KJ7$>h7CSs%t% zH4-0Ug4()STbkfNmIJ#aQobOFlsF9yQxERv7fY(hh6yn=a-RGLdZeK{XB0$o{=lTD zJZ`KI^K1JIQkIzeUN#)ARVLfrp=sVc<1^KOXwIoKrNW4Z+3bItOnk@#;PWwi;hw6T zAv~K)#&)q-G+WQ^9m`mMVO@PlBG%B8vEjH~XGN&Xz6bt?NknN8vN1h%(ogntk%Ex5 z0;EFq_`$eyxoo&ik{yuD+`}+}u}*N-V)vX_(HW#;mG=YQ_16TKZ^I)aP}h<0zU3r+ zF!iU;27R*t^f#Wenj9Yuk<%MC zDFS=anrE-i zZ*F{d;8AklVuD`folr)YHKFWHM9HBDBG;>mB5 z6gFlh8~4Y3;y}2dkZ{J%bIM>ur-TGYI8JxgX@)y-54YE^ZIqK9DWjQl+~@{H8SYV2 zo1jV9GRfEBR{FtYa${d)v>FSAs2*&lU{jZxr1i2N!8mjQIkKMK^tFo8lMqR`hBzM? zy^wmW@~%`DN=b($VgV?Btd~+BLx`%eIUW)m$MPM+cSj^bfLYi*!YXPM54sz>Fm;&y zNiLdYSvFyu=6A*_cZ}_4NRnE>vG0j!EvcZ^@y+9_a$hqCaFQfk_1>XtuIlclIE_2k zugzZel-K)>A6+;JY0}>_3*%*7*W4&?kKK={q# zLroR+L@gZ?&VA%%s5@xY?^O;-R5Wk{j;V{zP+H>VVey%Wq)X3%-htXX)#IHxhEiW03H=0!oMfcvAS2``7YP8Irh_Ev-aDj2C~(_;S-Sru52PM13euG>1MFcLtur zd?UzIL42h~upaBK9EF%CX(E$s%RqAF@Z$|*${`TDD}Wk>mFczHE9Q{Mc(ZHlIT@NZ zk{qnMdfgVf4O*hE&o3l7Zr1g)7PdEaepqUPN1L|ls(b1qqf2NUXyE|#Ql6PFrfogl!91M>;!(PQn(-A- zeFOc2Jt2Qa4I_@_heXouD84R!fU+VB1*$*B-91fTgkrZEeZe?EAUL!o9gTg+@;%7P zTRBDuaYSI;_PV34@?iCwi55sJUC4J@s-Ld&nHhgg+1 zYO0j-06i5mtZSj{kV_{`Q@Fv%4Jt$fkm`e@yl)-#|G2V-Z8~@k_aYqKVUcpZld&f( z!VU9THoeUG^dIcxVKJCZqX@re^7I9-onn*>i)j_vGIwirHO~KZPud<%9VC55NWBoL zJL6B$gnC688)X9WH5V}Juasx5td==CW+zOmW9T94A1F=|mp-n6ZS`<2%Vrp?lz3 zS|V`xf(+|!aEAUsneX<=bRzur%?x_)a!)5}XD(N4 zk5cZfCdyP25P*Jg7o7>%Dw+xUwNWh}g3kuIB5G|NOvd+t@wR^ZdHLlgNJ(yd%pO*! zkHpVM0iEKD{sFcRxHwyW#(=tXkrSyANrnB&{S>I3uNib4x_FJY08AG0G=7 zu{i}?wv*oI*9;!IDpyIJRfGTW>0~(>_V3Qix=xA5sY{7&_EP4;+Q*-j>oHJR2}gIuj4=A>M4Kl@zwx|shd*OX)O^T|N5RR|P4*`GH`M-}61eUIi*ZB$XX+0y z!Zl6p(z$v5L8Yq2^y|>`W=Eq@fo-CeF+mB(Z5ADEKhb=3qedJ1m zjl-WiUvmr--(APYf9DZ>P5}(X#V}rtn)L{7{0}ktGuWSfWYSN;mgKpV@NfBjY0p4J z<68k+OPCma#LxH$C7*u5o?UY)>&S=IjuqFs*ca*f?mkV$EeZiGQp!MHllaRT7Hw|s ze{|_W{k$mDL}FTMo6()uIEU+JlxMGD2{yqAdY20iaC7L!)`&A7tU@VIMQ%Sh8mV;G z_tusS9uPLz_z>Jg;sfdf%|D{tD?&k5TeLnfDabi>=iMl}F>Q>@9W9m$wB?41Rf}J| zfF3?aBDEq`+%(|ak}ww<=Z0%!D4WLXm~l;u&iEYb@!q$rU*6n^Kzw8czs_A=zI)z8 zBH1#T!EhB~09iu8;^Q^}#^F!Rz&AX$Pre*t~ zv&+1%{5B|i_R`YO`1tLGC5%{7=*%jFtNrAW8xP$~q-G1YpL);n)XT!}l+5mcfJ3>j z@mSSs3=Q&B22dXOCd!Y0Xhli)B`b(%OONBE7f+}QyL(9St&7&cat6B!^RF>vX}1NpPE*D8^*p6)FA``lH@UsaS>V?=T<^Y|=b z_XSryl+n%el^GsSC!& z-r7uNKAXLyDmTx=YVY5B$-KR16iTR!9AoAKf29(>>zdYEpknx$F8ZniZ=i;MiRDVA z(ltra00yqN(MbrtUxW=7EdM9yv1=_;L$IBs`rkXeKT%F$7>v&^P3j>bg zJ))u!aIMP!evJhDcT)=(wiIMiLfci9Gu=s_^9Ad9B!4Xa(E#yYrY{W*W@Sd zN~Lyd@1Fzno+W6^8D9gp-~l{#iDW{;r}Tsu++cuSGHi};@!gwJ4+4fEi!Lqo-e3*e z*vQsEi9;9gGavVYAe+T*V5Gd$UMSqWQ5Ns7K^!OSmzaY^9#r;ASMEENM#Dx%jJva7 zhj>hTbKY=M#xpdbO#iIE$vuX|FBsf5I36ev4HJwk9juKR2S4Mv$7dK@Z&@u?XAli2 za}q$>Nn{MEfG;}pCUkylRE0@vkfV|cG_CRk8IQHJCVGm0C>eZ`j2u+Z#gl7xj??1i zO*gnf_vRJzt|ByL$5LYutv6)?*>$ z(h#|=Oee}+5w_Cm$6D(HL8n60blw7~d* zomMS5P?-w*_KU>K#Ytyqz;a@dn;+O7!sqNY&rZ?YD83OrC%G)dfja&y(LCjR?*W^* zmwy;K_;<~jxEps?R>6pz1~i}ik1rM+v2C!Dws4@>maz4_Yn78p^BK$@esGKZ!`XX+ zZ6X8?qf;Ikuw!&tgd9XwzdIzDK(1=f@=@ATf;?+dq;VUsG3BB=6*1{oFmsZ8@OQ_!4PxGbyu;4rHbN_)VEhz zFwp0mY&0U)HoUPr6QIny@}GQVE|Jo$)i5@%#!+$UV^|&l1%z;5gFn8@E#|Im)noE^ zAmF?$MDP0PNKQ+Gr-sdcBR91*_uw0@5}X%Ky)I1^2UX4xn9nSU=Fwf53W+~8ci;V^ zFY~b~*nZK6lUsqanC4xRzs?en5{R7pA_#Gc zAif{p9DEnr5#Kzl1X<1o8JO98%9&)*?uJQa3EA;e+oI=0RsT?IPlpSOK4@=syI`xk zT+FI=3Zrx~$xwXLLbd;Qgy=w0)q+>NeQd=~(sbFILL0@)!RrG@c++Hs*f0GbPQa4@ zE?IHQ7l@m=1c^v_d*nlIa0>}N8`@se(S{CBn-w$Ez6r9m<<=XF z6za-gSC)}e_UvM3NEazCG2i2V%2nBAo1Nrk^VB&t&J)hmSbWxIN(S$fS>0X;7tGL} zHA#$>OlL#1DO%2cqe>RZjRd{;}p!2+(W9o)cHPJWPck zW_)wm#w)A8$Q8UY)4bQyiH}~0!{1Y=qla zXVyk693Q#Ydm`|oW2kHgh@r(OG+I0mOd9DC`X_5Qc@FpK_#vXR$iUv|jfJK$=5Y7N zMb6SF9VqSB`NQY1#Lj~L@?VWF)gBo_pEF zMm0<7z0aj8`!mTcdm4w&A!viug*M6={gev_6**T@v-O(=XOW??8;S#QrgKSpX_|{U z4NI6a^&Kge5%+B0KFJpxk zp7z~Y{mSIVP=E$8_fGCB3zEJ!FCROTf46MM>gOvPrgId&*GxrTBnmd|goG>{^PhN; z^y0FGfIA5Y$%6NfNE-cZRfRhMHwEXKOa)D=zY%n4XqYll?oWMTdW(RdJ$FbBW!L0H zQo@H*S9b8g7|17bzh9Tlw&^9yIAac7nQDB`@r_<)P-+DBiRo6sr7sV_2|d3Ca3Zq& zDp$I5e_k86^0Ws4!01Ij!ABmOf*ZQX63&G5EX%boAEV*Ue9`^6`@y}`l}Cv=Tk$h93ZfC&L|M!qUL}^N3h0GT@;^0RKj?%mC8izp zLWy8DDmJn^^hbwurIqtWaqHBjm}Vp>s^MVWIPTW7+Db?B<3{2e%r@gtZeq#u^B>X+ zH=LA4($fgR0RD)4VX=UdlF>7>Nwj^ho40EI$$2_DZd^}TCM)MK?pU>3Uq35m0HtvQ zM-BP>FVBD`B#ji=mwCPuGgd9j%|T`d0pDEhR;JY1nPO{(5DJB0e7=41c)3;oqyUCh zjf`I+Vx{NRCm-Cp&FQF~+E!hrFBrG;!b4c_(TB#wGSr1Q3orM#_t*l3fN5=I^dm|9 zXjxB>t3-*I(%UHf?&+AVZe@!dg&Wx|K_U20Li_Z@^`hrEb@z9IFVoGWq&dRP8X`p} z4j7|llCcsdzl)40>D>yh-z*IXa`M=^a!|Lf+Cfg?&5tYU!-^cA3Y%Aqzg&_| zAbtJt5}Fuptayxn&?+0faw|&H`^Wvp-obj$3K!lP-tV@=`$L-F-e&O1zerA=qe~-X zne@E3H0Lpm&dwA!yzFLo-w0F@K|fsVP^%@{!Rub^S@3e-healN8O*!=eCgxKQ3{)D z>teMakkdYB+>&=*#%|OgltXKFn1}NN;3a{D`>a+b<6s?l zZQNxE*_*!y@-FmbY?mUJk_gS8Jtgja`4~8^Drx^8=-P>3&X57s4@Q3MXA13$1VFaF zXe~LirR!S`iMj^FMDq;r=J9#OUscH1Up~+}f8~azJL728B+a^a1J?vpt9cA z!;(#H#g1s6Vyy(@3X#dE$_We0vaVC_3fhMs2$ax^2-rfL)tJ?LMIB#b#X1m)J6c0j z$@NLTtc$<>qEMCd(QnD7@f2zpR(iAC^MQ~xO{rZ|7aqk1iwOGnSaovMG-h!kA(e&{ z)`oA!Z`^bppRgRSks3X`HsU=c13E=iXZFmydJhNG-qHA5EyZ?`WXoM2P+UODB=JUW z9!(bfdG_4I(m?3OZJ{QVy<&IZ@m+Zn>R9}~V{ezFug`|6s3whuWA zLYekP%c=VfPXCdL{^v}{)_;I(Z{>lJU4%+lDdKBguQF8Sm$n^kCRKH{Ldu&p31 zp7$v(I;ZvH%$UcekMBD5(zu0lhDh1z_SrJ-s*kywR{UipJb8+g!b1MMcct^S%*|JJ zb*OF>U2VB?ofSWnCj{ePIUne`ENo!;ruW}{c5&-R2J;F^y5Y|k?piPW823CoVWF2i zi_)y;yKAsdySMy@^4y{E)_W1TxlvgU<0V&naK7D8G8v63)r-3Dhh;Uk{7?5Bwct%t z>V@rmb_4PH2533ru~+AWw#e@4Xj$28!K>e$z8Oj7)9cL8kfV@$?#ch^%$!cBe!(yn zqPNNrAH@a=@4Q+U{yF>Q%wFSH?WVRoxJheP@6$ErPK@2z?YZs+yE!37r#>YQR;d|* z7_9juRp?RSrdageOZX?p`q$9>{&>-_j`LN}fVYR9{YB#+xXR9N11c`L4RuuVt)B^X zhK)DT?EtFLFg@>f7R&MUMJ7RF=jG*pkk1BNW$Legs=wz#cPBpPoHx`}_snCqMZ3O^qUzmVsM=3I`~ zrM*(OM0?c-_fw`yf1ALB>^(xhFQc_o0i_evbV#JZpzC( zmQbVW{i2pXD$2SN?wGb>@6|$ngZ!nvJ(lUmHbXoazuL-%obg#2eD58UGQLz8raE-T z@oVrP3RnDu(ie)0p^(y>Q@ z&Sp^u&4U~Im+xr%vTvqi4rtI7l%zz*JmflxVWB2fVoN#^)b=B3j z{E5rHyiy(>i>u!n%1?}x{iZ-_Q4blzH*^H=mytS~>fxYq1?t2w; zLyDJGVXO&~+=}`SBoux(g}(gIkL2zt9IH9M4Jj5ZWxFa8JlSHF;AcRV&U|&R4YUP` zjza5l-DWM}{XB4U@R_ShqR*)~ZFMKfu_(6r5ivw}dge;9lV8ng%OE}-c|Hq`NarVH z>26J{h~w_}(YYSqy|<4xP8jY{+1 zZTMqd*&?0sw>v4n&X;v3QY7~{tW~v8J3U6`Ta7&9i@`sdst2nwJ;f66oPAIERxU-w z2TFo_C}(n0cdgY&@@v)z&xIb!vd+i9+~n1zU9%<8w&s!BjB?La+)!^*iO73CIsI#w z{kJ3p+XgF4ABdc-%iB#fP}3Xp%-501c1)N~cMuL{;aFoobW4AgU-lo!aboPNYrtSU zEA2l}9r?=ZsKgpbbVnzA=r+LiZ!zCh-5#=BhsDm-4d1M)b})c&{}e|#Zc9K0E3=21 zHP)k6(+JxxB5b9vnneb^KlncAo9rmN!=Kf0)$({XF=sXL@Tu;t?9b z`7VX!{$&&hPRzxT0GE&fUSjbfbm;d2%h|;HzUlXFcUnr$C|Y#%Sn&%M-26TCU2Tit z_C@(~Wr*wbId&nRbQ%eMFpzOg9ao$6NV9h^nVlBD932jVUt7nOEAN`xHhSMa-7;!T z`TNLh_M0TSe$6`V6>GvRCBHqeqcnuH0V`A)U1WbZGWNjjtsZgP zcilXS$3CjPT;+Pu@%R135>LmJ^|=De}3=%b)qn}ZWUU4EHA3R62|@b!|f|SzBWc4nwEB4P9YnW zI*$3C!wkdxLGW}9oP;Q;Z@3`U+F78~HDFswCe@Ve_;@L@wqUX@LlFF25HL_jEV-&c z^}Hs7t2^_&-a5hUYPzwI^VR33@wpB4WW~A$MYr2u_zX5Sn@-kMiv$O(n$4H}En(U{ z2|UX-1ITmG=D}I+QFItq6B{Uu&l->;Y2(6Cg%q=~V z`jvf;aB4Ky7^=5=twH@o+9eevqJ__@sZ`!`M_E}f;C!*5#xhvDZAj~xMe298y?zh( zE3J6}PdsGr5?ut-#cVyqylb>%H9#RwWnV~rJ(7>Z)QUZ7l)P^0`Ehj+mwx@hJpjoZ zAY2IctjeoDE)+9mU3ZkVJu6$@xy5bTXgrf>(z}kl#}v^s&SYH8^C?6QoX-vm9zvi~J!QAllMIrFP^;YGtcYe-(xSo)wmi)`2C(aMcKEJN#JZ(vr z%(A?4#l5m!Nx?MgaK<4@xoPoYV${_4jj3^~G0RD%Q5#r{cR^Gw3e`kKW2+yo=B~HJ zncrXg*V?M==eAaA)*LA$(#-#ym-I$`-k;9*_oljW+`I?0ln*yM%~7PiI4 zXWoQSQJHA-XLc_RV=I*5F`^I_w((uYkkEoL_ieY*ADVZzoE*FMSo`-S``J6|rLR_wxk&YwPtEFHa42}=Ba?>;?^tKQ zSG?++f8ph=UY@7E2OZxeY^;0mb~2WN+b@1oQQZd4Czm1rb2QH1|3okJ2MbG@wC#$Dp^p|oJB`W0}dplCEm6ysZw8{d<-C}QxoJP{(hv2 z6wrYXs4bO&>+~m|%jA}G^?D}-l!0N_OoSJbc30@WL5OSPw0+%;r!vqD&8$^RsoRKy zi|z5{t)aCj5%pul!|>Gn(r~$tmdj^ZqaNcmtjLJuzKe!nNVBA zUOXO?HG-wT&`g<`S9Ulj`!L0UEYd3>v3261*~94H!#&q7X}*43)EGl^h@0r_tUQ}? z{h#r+bOiej^8GzkwVhEMk;}#526uTkLopZNUvWx}KF ziCBT|7d&evt%m!YM4zPxDS$UiP3}>iT>R5=%#8D99$I(#H`zI+TTpLj2sX^URp({G zp89MhD347^=k<#~6MYbG;5H=^;3>qew@v{+ys993%ZkHn$NsSeB}(&24x)}O(b$(X zqs$^0MI)m4YlJQ3bC(yl*i7!@yVf0W7l9)cz_A|wQ&cr{o)r>Y{w3Y1FWremV`yPA z(7lz=G%)L|&|(nu)B=X*j(fS7hlMHA^v&pbD_jG5E0XZ+M(c-5F~owO0Yk@P<*00G-HFHz$}e8tHGxF@B$Y_mby%ZH@Z8O3ug(U;*V|^ZZ;ZNQS0$vQhbk9L zL|XNq|BM))jX^DAPIDEK;Dj=fqY@$9CMw{l(ERMYXKgxdC5 zg7Ip!h0~j(NPi2;2V-DZo?hwRR!5Di9%HyCiajq-H@|){INnAI?f0i$?=M=i)H$d; z{tTuKfd5*~3n;~70#E(*Ml1KDgLBG2oN%^J!bi=SFln^c#0RuDfdhYUqa6GuX0L@L z3I=iWP|jRb)U-aO_p>dp2S+-7QL4AfyRo zxNzfz+VLBS8Ayb6Tefv1E01D@b=zRtS{HlT`mR&NhnwOD@~`vU*;!_Yjf}$c|A9(8 zZPYjW`=UVizLQQ7N$`DO*i|6%j-$B6!6DcLGCX_dm9;i(RwQhJ#G~Lo`zZ>IUhq~; z-gKz8!pE7gN~ph9C%goD;n^&7b1yHY%nf57l6{){H8-JlU^>FBPOvGv-1VzwV*Jcq zKOk(|ikn>v+nJO&$->oo4WFFYBcbwCcW(R(;rQr#9*6%~K1zCf|>;sx=xHYsK{#MmOBLe?wAGLMO1u{l2#e{m0DiOEt6&LZL1I zqU^@c`i+9;(wyy`ZVKaOXer?lJ{WHKVT{MPXkE2qAbl^yjMZn>8#L8EaaiX|@ih6! z+3gKNTtNbA&~vsvR7G8G9>GPd4~Fd_R*~TIr0(AcLSd;DjE;nDQIf^Ke3ABoMmNty z?;Ts2-_1W=V&{?c{Ztk02#tL2-KFvDBpm*B?S7FOeKbNY@nU;5HT6ZTmibWPlF_KY z=E)>IA#0%GxYAnS#q(;?Xlha5QsWnV{C+?0@v?!v?w=+=%ti(3A`Vb&RhCc;euG#$ zW(id4a_&*(JA3Ul$4yW|A8#4fw1K=*y}7U|DZ%@WUo1kNS7d2QiM(JLgI0Q#c-6Q zJwwVaDNsbq@Fk>2jCR-tVjiN4#ZTtE`I-u7T6xnqUxRGd+mC(!!CNut{cP5rONpVUx`50{X8f zweg#~y%5Y^f#e5agPFdFIvYO7Gc-^5z)Ap864#~qW`Rd{j}%Js7s_R2shJ`37+N7N zY*wd|%zIf%^^>@%i9_o}DK&tD|3476^(83tyqG^^b2@3}SvL*Hm)bj;>QdWcP5zWh zrNSAX6(riFFxQfIam+&D#$f;QKQH z;nn({9D9n05aT+qu8f5m$A_0)X>wiJk?NqlSVWZ#ZSYG?10)X z#9(?n^VjAw&kU(sRhcbsa?rnpr@cR}jR1ir8hfsa`A*4I^iJ@-<>H;8c<#nbHV`;$FKhYruS5ljEI`73M_|jb)-wb{qo1|9r?spn ziorUkI>dtiF9g5HKiQ}yd1_T(pB1C()v{Iyg*{2fb`yFGmD`UxG;@xEL1uJ36nyZB z$^RVY4=|c|gbrOUX5rMQDtyKoAfk3V&%P!;1i?(3S|GAt!0W+dfTo|@IeJ`t{X?$GJ#V?82KPK@TSJ#ji9_U~=a5AZ zB!eS-wG93@+?f7Z<5UlOe~C(#5aLQO$j&NC0R+o=MLK1s7`0Hg|FVs#knjD^pTbO3#HXFB6nOt4Vj5L zy7XtWfo{hB+e^A?-i`hd{K!e4c~n9B&o#AdHqnH!LMc`(wr(_%l`xV|0)kDBA){$A z)P>$&MXjgaQvv+X#VWAjvQ)0$KBgT{Qf0@6$RU{cSkNl-h;=U85 ztUWBCZbdpx?RJfgWptr~*VrkCtyjLa%pS^?)zsnO+5UnzFe6jN^rhWisH5=+8&_wt zF=DYF?|DH){j{-}p{#Ucu+AUv`>PN@(NVyX;$J;^FtHC=HP{5MQ4hu>w}A)cWNt3? zutbtlU{*YJ%pyonrbN4WvT_4M)RQw9I5S24s*{n*-}i6_C1u_5Qvx#D#xSSYSl@2r$_+n%~*L1xp?~uQVVskmGfQG^GJDF zPf$ogLzsA%>7>QAIx558>+yJUlW&l zA*W)&pysb}6LGj_>j(Uw&17eI!+QTziSR5$jsxMY-4pbgg0CgUP$O*?;f z@0;nLue}g&?%(a5sYA(o=ET)YYq@_D|LXFdX;GvKwNVJI5yqQiGsof>P7G%#z{*3h zrd3QzAB=k;Db70y{Yb_Xux?o`|3}fe_%r$be|$3;5;;_443jfC#fBLvr<^H=RE~+n zhMW&`F3p^CXoj3Skdi|YBAas`M9wtKA?H(b`u^_UAF#(BH}|gleqYz?^?Xk36tetC zgLP!Ys0>xZlM(KBOrZViXSBG_ zcWJ3#f7DLjfHzN!5QHe?)8V1T9Rl~q9CnRwb^y;;qU7}A5eZIF{beA-_E0Ez?G#nw z=Bs%6@99Z!2`K8<9wuNrs$W_&CG%9fHWoY3a~@JDJ5wb+W&(pQr1;+V#7{o$!QO=k zH&?O%#uEPoO10|Wf*dD*a$-5(FGvs?$p6mrw1SDjLa1^+ZTl{|WP@rflZATqOh0RB zPpY5wda5GpX#-Gc5aW9?=TH7ap!Ix53EE;F>};5e9-MEDnClPGI<$HsXnQ45D0^nr zcvdTG8_W9tz~vlAw^@&--iQUAH@-T(-gFvu;^m~QvaKkgx5GV99g3A-Y~j~b z`5B=(6zvQ@4mZ|sk4`(pnRZGrcY`LP2|!94u z4R@f%`~*_gEvXvf5|LAk-FoJIDkDIT*v}nTE5etxGNfAm3>+>y98NmK6jp z*!iItdF{X!BrpJ@Y7OcC$@MVeG??wcAJ?ivi7T?g>nNX zUo3jjj|;|SA@d-k2k2EZePZMg1O!h!5x#syR~T3y6o)`vcuRqKDaV7b@aG+|{L4w1 zytQuh1>}GU5K(6m6&?&>ekstpmV^Vea>3rDLl^RxvZNw8R`Y9tZ+DT4alIrnH?>gj z?xW1eJ^zOJfbu6+E!!RQ+k&H;6(u{`56EgPS5Rlob!I#y>!w?SmQPjLcw}}C@H7}% zcUOY^d*(ks7O-TC_ppw-r$&nL{UD{CODhI?Gup!(X&{N^rXM|>x{#EPFdOd#cuxH(IDQnE(uC9Dgm0~O4XC~%$r4B*uK$u{FHW*dpSAMuH1>d(LRb)9 z;faN-S*8`wteo1gTvr z3(d=xp4SHU%_xl`^?Ew-l7cNk$C9b0H|#DV6L=F&MJMX7sVKU^rTSSyHi%jFhCAoa z;}$)%Pn2}jq$il)Y;gmeu#h#KlK4sXZvfV7>5mJf#!KDw>D%Vt+T{#*WR z3Tc!Usbsf()`da>G0J3_%xRVSoPVgKHd zz#4|+@N`2_f1)qu8^&m9%#*ezR!urcOgp&WTW04ZkvF`leg>ro(tLtb`1~)>$1;ct^%)`@pfSUX;p?^LP}Sxaz^M;$Rf z@%an9|20J~HvfC znf`?%4T9TRTb~GG_PHO=YY>~Q5{>*>8pM(NA&^dfzgv!1>s0H+>hPu%PJxRT@orw zSd5GlN@d<{LvmE8k{0tn5F#aKXFN*>)lMuf6k1|C8e`e`v$ncw$g0^J+<6+f>1T#H z30P!Xm<;dQQQ!hR64?tpvsqfilPDP+cg`dO#V~I?mSU0`=7)LX2AB^A=MwdZX8z+@ zq4oD|i^{2XsW+V5{|D03NmL}um>I zum05ln~rsBLf9CVsaRtUcs+$BeM1nTV}d$hy5O8*%U@?xf;Ivng%NFd-C=d zE5_V7?I7cPH^@N84|6y?!tv2f<8a5=%vF!5#uVI-HY#DN8WNuYsvY)&dH#!6MF^V_ zo(#=c{!U!|3||f5P}<|YX7 zfgN}O>}x&T>N7ZY-8h*ZCS5$L9vVp{fm`A7AUhLpam6JKve(6#T-!(U+?2f{BjyDi z@dE^~CE}2l)lqm%Cu~cB_$QD)`E06#e4H-;6)bU>@=)Y>im}Fsp3Vs$_$0IDSmkH8 z9hv#_1-}^ov!|j}#`GD!*b`h2M=sm`!5iuzOV`8B^~6H7N>Wy?UxPpoWC);VxUJhA z5<3nSi)}?qkZn89MFNCpjMFcklVE28U8x5rHy;X{7xrDt^qEzkgo?ZFcAW+!7^wz{ zXeIpDVGq%ahNZYfIm8?1>LXE>GX5CVHo!Bs-k;gL34j!7(=VEL3$}~~0;_tUFNii+<_4ew{;$qBHc2}S~71-|{+96tNZD{S> zat*VZ>+=cScq0TRC6uTpVbo^wKTrvAs%9jWOQ35-S!LsP|Hfxwv!Jc$>34?K2u^^3 zy!+;DQ!i@}758)Zpp2tN`eDGm9qyO=^vzXEpGRY#_+q8gvS=;Wq~-SmoTVnY_~+;M z`j}j{i#2a9%51iRsk?R?#V%%AL3TAT1~4_I-z$W{ZrEn6K)uMQE~CVk;BE7>WbG`(U!K$_32HQfy*CDr zu8wNs%9u+Q^~E{jgg~7d`+zCM?@Zd48m&lpyo|JLx^3YKfDd95_G~wrc@Hw=AoQN2 z@Y7*;l1m;h)R`nM(h9a8i){Mmsx*Y2W{KTGM(ursXKtID?b<2We@*^2BuG>j-BC*o zbS)e(BjtJadtknbjDt7`fQmSvZ6(w{ZNOwUdh8kDRUBH|G%jN`BaJc}fnE`#tVzv? z2pU3(Ku-c_GF z#Kl>seT<1s-5UR>)5D~DMdFU!(NqYIzDkst8V>xs&_qA#;CVYKoI_&_HrNp&o|h+b zgDytu?9zsZFGcF)GDYV%b1_b93%=U;@NfGGghqs%CKNfU0&Mvm^)p&-p(&klXK)8y zXg~9RyKmplt9l&ZF1A4yPQ(3gj~R;R6ll@%LO&MkhsPr$i|ag}J@2`40ddH^qYZjFAiRs*mp@8|%ho#VUsQKVe4eq)A3zZl(uP>kYbL}Ld zbrHz7QH}c9k1W{9S@+dmtN~}2x*3vaKeUq0^Ry^j0rGz>{*d|en9F(UljWR^T>YHEmF}khRn1=y zOjkrw!VfK!nOr<4UnJZ&hc>POO~aA$mj#G}6`W{4ZX)Te4ES>gfy;3-7n~ILNEj22h1D8BYtT$?-cnKD=$C*+U1X!oIaua?Trv3?gyE+JRnu&WCZ8;b!Y*2| zgq@=M43iPYZ0V7jywSjLRmQv|*xR+b5I%EDCODBA{=t91+~F1cOA5c% zQ%bq{_ZOfHChshVyV>LOG4ttY4SG`gh=&WJ?-LVekg+oBY8+)XFP`wm@$W2=lIlO8 zH&_(6mKGJ-+|Q}VRW01&0M0{7SiRO^&1ex?{#eY4?f(t^_w2{*w35fqx*a>sUxM3? zBceLX?e7()8j0aJm8T~;Ye5D_J)R)v_O0EDaa4Fcdq+5>vHUwzqiDlb;!XQ|-&_~g zp;Szq>m+bU^8H10radj;nsUq?rM=_FK7;GS|9rNmb}Iz^*LgTA+J}g2K(+3d0p3WQ zL6O2Dmv6feeRIO`a=mZV_wQ%suojAcI_@L3H7~E^`n8gHP{(M`qY$U>=Q=T&nojVY zHB)JsiZ&}njes(Uz`T{pVk&R_PQ?|am#+K4t+MzrMm?8L&fB*M^#X>cN1UPaN1?Yp zxtUx1v@Y^fkzK%To26v;lHJ5s?WbS?%1X72TXF<2+xA$ zGYyS(-YW1{sDZg3Ag}Noz_6kndZHP5n?E=fHJLl$8=4^5&O9T~>+$c?OkQSdVD*N% zQtb9q;Nq{JCqNl59VDTdXv^t1=L&KiRaxW80tBHV53!+ z-jU^*c8n?Hk{3){E!Zq~V=4~NS=(Dp(|cF?Lcs1Js8I;}|MJ8m*^)2^li7#`%7e{g zuOHvQr^o?V<^q@vVgJq|o!6wB$SkRD_DA)L7*cmWQo25l4(JSm18ucVm|?!og4~`%2$I zf?nX?u!o=Ah)a%yTUh;Go)vbyfr-bl6u0NYXR->Lf$6$FMn0_(q5gO7+ztHQ;#un& zogqM_uBfc-zH@~Y&HtEWCa|!P8|e)c;l)zBS3P&`9&`KX;fBd^Zu^vPk053?u}aML z`pf!3m%T!qr|$9(ytlD;pL{bo1uxj(aDz;>0CnSHU&jF@OmK*hZ&ck3EXN1AZvUi#H*dNSZ94@6mLfx{h$b>CzWA+=IQ z**2x)L`E!)dimzJXWNF%#KO>nH@weGTzi@WtuF?Fls1O8-#q*HDwXXzd&De@%C2y& z-Rnth%aOUWp>J+9Ou~yOK$0>0PTx@%Pq2}~Exzz{Gb8c6F0H)2#{mZVoNsRV>2^N0 zWPV=0erfi&5S($C@;!k~09T_#{dU8i-AKl0_4bYQ(%murIflT7!*hk6&a3W%OgYHi zL$GzP+%(7%ao=1o#rTY?TzB$g&iPgkm}Dfp_aK>6IA-4M&CuX>CW zILy(@lU2b;M=9ti0cR0G{^etM-jPX^`GV^xAWf|gb=!Y0_Y*FcZ5`(>!VPd$nD$jA z@MYCuN0TnIvK)stk;XvJXzS!O6w4Lvkyo|l1s1jS&5?pIM;X5rdyJjBi@cP?DPZL2 zAxwdV5eAv;|1`#0{N6IW6m`Ig$$o#UPPyGJBPG*^10|m^Q3!JKzu9*(zd!Oq&MIAg z`!7?6_|V;!d4~AkFPtPiG02`(N6PxHf|T=oc%3wBIM1Eq4u64*=q~SO>22GDn=d`*Ouy zvlJW~b^g2;|4ee#kL=Yoqaw%Yqz$--Z;kzh5H>88Q)P&m zh+&Uey!$kSwbnepQDm#_U52VxyKnXZ6^Z!#Snt}`Ws~>rzNB*{+M$O8V3kIdJD1ts zYV43utg;mc)V76>jQ7PiNl{R|r`0MF4zKj)@?%=U1Gxh}#82mSM?^?(@-~HbOe=A9XepEdG zLs;IcySj8v`|(!+EA=NApQbsBtl@|TC?Hp`SNY7ntg`y<@BZq_{{I+WTIq>W?Jbc_ z0j;X$5vqgZeKoqwY6cwU-$E{wWIR${8i}!2-B4|-RZ$ZdeN8kM4<$w!2i!Tt4qhvK znbw*)925jIhmh1E%Ym-wA52s?vDQMAjOukQcBLHll`NyrkVTcVZltP_qTG8xShmS(|6* zMm^~bW)9@v=P#;Fd`ud8l`!$~K-#oPTYY z_A0rm58f^C5l4OR31sR=ZIPdd-%pQnb*BCipi{<|jyr37lzXv>{n+}jfwU5dBsW^hg&mpyoXbuBj zbMKEAQZHEbvP8boy#Sl}{a(&H@975?YyA1mFk%?z@VmM`et)+MG254r+IuB5qo&PY zwHHr?K79U~1kR4*kwAR)0PzmbdZ4$g{oebNg5RAE+f9f?3v?Rr?*l)lp_dUDPChAj zMA53n=c-DjlA$EURt$jB0E0T<5_YH1B=a!=J-lO@^f*mnEWaCul^kL_kO0 zoi%3xgf$TlI?^k>`Rdto4%K^ctCRabTziBw6q3)ju~Z2Zi`~e9;YJ1invc{JtYJU6 z-z`0vnHB&ZupdJ6311Y*J9m?>TK;lNBVN4PBAYf0?lkHI-UX_5g>3ph9vJ0~+x;|l zZe;!Vq*L8lAOSOLe|YEK=k?+X3$-u8DrDcWzxnU0?AB5{6mwnH^m9n{-m2TL(qHZ` zzZyjaBbf6`w(WLe)6JVNK2pe$PfYr&x~8x={%7u8Uk@~YP>oQ-Klb^>dEAUI z&GxhDo#{iGIq$MYc#zUk?>9Hg58cF)lZX}>p~17aihZje3^1pAbY~I&ZFLG5X`RTd zWevoVy9ZIVtF z{@f6D%n|6K^pMS>jDI(O-F|Ac@A<_dAWQdN9DybRAggd8&QFWHJq%He|?lm8GCHpCjL_xe&ze=Yf>iF%WrKB1!v%~d_ zvn3u9Gd==e!VBTAR={sHeCne-EAHyb6XVfeJO}j}B*yIRTyY{1bHfoD`>$z+^DkDh z!7+Yuh@jaO*r5^DNwpIxI505q{NKiwqwuzS$J7b!1hj=o0u+07i?A zW>}OQ+4)FyjnuBPZoedpwKU&O@l12Q*+zrLuyB2-dN^q^f~82j4ez$&L&uQg4kKhg zjd{4r$>aR01-uOrE_T)4G%e0OhdC`th39WS*Iuq453io$i>Ujn>88j}CpK#ay|IGw zn5yV+#vPj6cFA1i*TXr~U9EcglU%-CZ-ez98ABmCY& z@NwALNr-@vJ`*wMA{690IY07iAij!TKe~RYQ|W%!S>NsYgoh@6_Rl4eOQ_{_+2rR< zrdN*tOMWsX@|o*=?$FuB=M=C#{(qoHb!ad5FQdFxgme0v$S>4jM>>ucqZsAzvH(}9 zP|=Jl^_;&_s9C+i%>a~l%hBN(q8mZq-&b6{16*^c#;8dHB+p| zo^OAf`;{gXyv=S&mMb}fpDr#JCKpe?czic*IPU7~5renC-CXdv25zNB)_x}&C}e*7 z(ig~ty+ehb*oGTNIdD1`YV&EuSEzY7q1`mk03rhv8DN|kc3anvD8Uyl+bi5JJQF5* znKX8eDevzNN&ZXrcv5ZZ{&q@E?b8p~x4R`_mVWvc>HR45EvHa_tFDOG+9`fMS{MG< z#F#mcaf(;%u6CB>lOKxJ{!6VV{I`pwrm}oVq(8n`9uZwW=CvZq^nbx|Ckc_ z`M1}7(L{?uZ)?k+s%f~(@w|6=ngXpFkX@@guHzMD=?c37<;f-EgD|n!9shpR^6GUV z!co5JT`zUnOV8zfUcWqVvlTrd35`Jy%vm@-(iVGh>+|rPL*)C@pRQH{w_c)I=Vt+q z#_y#=OyWjUP?TG^=^PqZnfxOBFk|I8Wla-n6fI!V*v4Iq3Rm(js&N`mu9^r+=Eglc|Sk`i^}|oLoume+G^1v$JdbQ%fOmhxZW| ztM@0uYBMYz*&i4Lkf0X9FrJWguEoc%8;0bsJ{i|Ve=kaYbp?dP+{dt&GY^!kT>OsK zy62Fu=5{U^Jl@+f#f%SwQ10XUZg#14HB{uS+C_}JPrG;=f9`gZP<4Se_A8tb%CygvHKO7}`>OEk~8=^65( z(SOH`nZ^4?-W(j<2e{?w)X#st?1HoHhJYa4_}yiP@R89=1>a&GOI8htWahmZNS+@-+ROoDTEQmxNKL%o- zhIxYb#KI(N8|ysHIvqoz!Smc31LzYOhno_80Yku+rB=^bweAM zf$&S63M1~7Ca(D?xi=I{0`Qn#hEFEjIjWvjiij7U;ZNlZyF8I3|77i;tGd@b70-4> zJRWw2IbBC6(fCQpWxl-3kLPsW<|h0{;vS7bfljaeEK8{{c@p0I@XWa=P)}Md3nJsM z23hKfNK#|Z8<+h#@l>b{LXI;EfNTq zXo!Am%7zat5U54)t;TJ>8Xp&wJqkE;$lK-^xT~5On@KB9u5;wsO0Wu{gnspWH~F@S zSFP~2o2VbfciHm3;tR{)?u7Mhl%p^}Y)ZfKfsIXV->2g#`lh8;AB^SKStZ5kmhleAb2~{3OO5L?%)>d@pnJo4)J@`aj(!zc}AvH7Xu&%g;72hOvGY<`fdQ}9wjTdt!r z%vG2ZIczvBYX)ngn0iGMi=tE(vIbqRDKj?!ZS{v)#hm$iA>^YSF=xL_>u$-z)^4f| z5pd^JRPt*6^ft>y_-MCefu!UY`m(ZXv_h?53 z9Y!pw&UW0GO@IVbd`|zBJe*^D$)s#>HVNe0Q)B)L3Xb_$hviy6w2Haw$wMJRVcQx* zu?mawiXyFMy4nqg`=z295jB3U1HJwvS9HjLSSpV`!nWu7KpKftIYOpbg^en$HWUy_ zEFXeuaZYJ}mX_>y-j3LY4((Pp6}rf^#x86P#+?aJ=Kr0II~q!%Pw-vL`1_7YU6!;b za5z{ML{+Q0Q5u4IDJ}F&(?KQX_kG-n^fPnSS2lt+vxEeU^j;sTMAuDEib|+4dmlR) z7Rfhnqc#M4E6x05o{p*rtj||-cjQo5n@Tjnk=U8R#oJWolTGX;G@5E{kkg`mNT7q< zVo9g){@CP6gMmyPkrPd;I`iC^o>UFOnr5>U7{YNf)IUcyg>G~P!JAlttm#Q8IP-!f zEO(kRvq|AUUY^mYJm;b#Pu4eRVZ!s%6%<*RwNmspjG7q1y$`lF%hPtLdvwMyUX*bi zpkn%{SmL9LL=?gaxM2WG65$D&eGtr`i8$^FabZTMbJW577OQnLf;B_JwSSdZuRA@N zB{#s$hWk2z?+&EUok9wsZciR$dO!tWM*tQZlG@!q4@y8TW=d}_U>X+;!OiC7BgNj) zJo7l#{?Nr7F3q55GK5nQ1qfb9tszPJ?HOcKRo;CU4y>>tBSC2+PYKTj_t%X0)OPrv z6Nlo%`Ls{z9g#MPFn8;hOE;0b&^}mQsK3d4uA=j?fve#e_$utUWu-sD^S4XS>Dx2d zW`n)~Xq}995PZfUzd6MMILnS}cgIF^RVb^tjh6TeHdfJM0b715e>)pn8Y^MZm~Lx(oei36?L zr@O6jPeh^z91m1Me>yTdk~jJ>JepR4C}&cE16Ph@n^S0PS_Zj2$&&(ue*v7_!R%ae z{;`Kv=go8;{bS&l@e!tiHqq%6QoyQit9bs{I0^vj(oe4Q@-uY!?@&)O@#o5OqsOtR zKPfrjqfU{-ozd~=Prblg5uw4UlIJACVZR*=>>c`>GP9&U@ogUj2sRz8&t;SfjQ!$w zZn?Sk&{e_Q2<#G)emKZ`^s>Fme^kq=r*=|d%wU>6m;mk3;v{kIf?_qffVu;+&}r`m zLOb&tsoLCuB?z8&y41B_ii@cYaZ(HFoEsRn_Or)>&1rgPcKDy5C_XLm0>7(|YQB?Z zpO%TaDaFCQ`LFif6wO{!j)8LFBcShZCi=eepUbYG2pj@#Cq#)Ck`df`cq*9(vbyUD zIGP2w8MP;;edmVtrL)? zc^TOZAdf+f3}rpuPeO<6x-qnk|A8(jL|SgETMpv8`C$>kg=YqO13!Vg0NoOcgFYic zjjmmmI=`+(6G~C7YzNSId}gA)`7~Mz*t2!z zV8I!(e7N$QUT$KYa6Ry7xV(PqSh~DgPbZEx-1fXRcQ`U)1)`9}Cia*iM47=t7N?kY$(%S3OU-*C&(s0EL;xC=r!<$o~Q@#sphOIdX=8-s(RV*PnwI7kQ)0r}?Wj z7}j* zvP`NJG#(z5iu!3g_a?SfmiXy|3@JTbs;xj4J!BVR7{(cc6yhqDkcjd8#`?4m zPhoi=0(gN=$2k*|O>50!s9}?U*^nP->D_oHu+RPk7JMfQ=VV%_c zdHp%mEEaEYzZ1`q-=410$yy5BBT|%b7#$5%BhA#xr&mIg6u^HkYq@QWlZy+?bXJMG z)9UrIqo1KsecM&K`~#yT3+Ck56p~xL0A<;bG7ma)bXt(FxNDs?U{*Xr`VpRU`mlc* zS;+cvI;RH!=*9!yk+!zEh$wao74FG`_L)Nqbve_Fw1O{TDc|wu>3win7$ET-Azi;j zdU?Ol-9WpTFRJGna5OzU(squtOPgc|`6zxWP^9-DMH^v>;_BwIgoz_adGbf)b6Mnk0JqMTJpYUAv&Mzav$-3)T+`@#aQQEncAN zNCL4kuP?O%eBa)|=%b(E?fLCMv+n%4ce9+=v5_T^Q%t;w7Cb9a2&lm@M}M0c^$0QGQv9ZGJQkCFk|oNbO= zW_03~mv0dC=eFtcGtuVb5LBlkXM~qZ3NpMfW(-B+!z0@P=8#L;Gv+nBMpUN;?#{E6 zF7vDp77;S_J)iYy)J~)M!~d}7T|K6=*M%D>jqsCD%MQ0c<}KvfoRi<}>N2bYj#fO^ zh0UjgD)HMhODBR^vo1M$uJu#pQY?s_GCf7cr{O4LubS~#Z~R|qQ6Z>$N+hGTpS2iv z(tcWvMpfF>@tvQfY7DBiNp-^*Xx!ind?(~=yp#XT(9Cv`0q}fOcAX0SX`CCPchJzf zziK|v&5FoaoyH@?eZ6I11lVp{>HT*g2oHFs+DW?>6x4Rq!tgetp(}Cez-OZyfE?(Z zM_{wSI`Br?@|r0)NX_`9XvX5s^VZHbMiTa3cj3jOv4N=nfh`@*bp$xBuqtY_4Y|)e|v7#k5aMB>8H4E?UMz0Vypo{)M}%b?scWPU2^`RD`0V z%)X74aVL6nMWR;>CNFc5wO+PDYfd~9HAgwLXZyL}`K&C2Gp?k)!bFA+2aCC{*Uhff z;~q+&(AHXBHopj4=ILLz{exAVb4$eVik$(+#WbZwrl5cm&jmNx7+y`*Rm*K0^=PVj ziAjH~ps9ggUL~V8QHy2JFzTWAih2C6o;{gau*`2Y7$t`kp1DU6Z$wm^VW=hI53Ltr zNg?NIIDZ^bAk)R|8&Q+g$opnUmK=^3sbdsoJX$7dHuDKqIau`%3N&kNzJ-n4TKyq- zR!Wmb)q?GoB9y8pkLVkmwH%;s9=(X^d{2U*zOY-c7LANEV>HZ#(zeP#WoC{UM5AL( z3o$J&#pAw%#;!iWgn7opInbW)an#I#~19+A!_%o-)PCv^{2>*{JM%&dGGb4(_v)Jqh*_xxsYZ6?U?Y{h{ zye8xS11Z2pFc3nq-PcL!JA*Y&*reV|4k;2$?1_fDYzZ z3f#nK>S(1kbmGh=Cr`J1n!+xFG;O7aMza60vHzCQNMib~0+%C1aXpCIDq~t0-3TfV zr@Z!9)N(1Y(Y_lomue2E48^Y=RY2iEjjQIVm34D132)a<)>%&#Q5)7uFT9;^CtmlT z0Z`*qHqBQDZdgs5XBY5D@4rY4%`8*uc?u@1RMAOP6JAI!Y&)$5tAt#?*1n64%(^65 zb^5Kbv{j~@pSD@ao+6$S^;Z3+Rj zIVWGNA**9!BmVdwPryL=eW})Q#uV`4FGP0U9Y_1`9+aB!`4~*0gIRK(C0)!?tkRF7 zR@y5j(h>9ML(=envjTn<+L4N(?BY(}MPu6r=sI@McC633T?73zhm0O5g}4L-9F)r7 z2$h|nAL@^$jXH58GLYy7g@EF_g_VY*9Gv0Oj7(>Lib=}Os`&}s^zDK916{+`HGxUx z9NK5l4S~3|)ZYU@+P<`#QGEJExEab=Guz(geJKfwSoah5xreGtm8Jv=L>t8M(D0!B!Xms+utB!e{2sd5 z(>)pVU9GNK<$%>JEdJKuSY@Vt^;)CqNwV!m#?9e61M=1eCLgU7;fr9}O%stU2dkZq+uh#gsX?DCVnV^o9=C z9SHxqgD>Ddeu?`h=tt~r23`IBea0&D@Ju3Bxq-f_L){warX*+&&lL1p+{niocwspp z9Cg97p`o!uNtlqA3=MrF3SiFU1B)oQVTD?^p?Wie#dMFvK|V-B%6x5tOS-zRv=Ggw8M8YsOz@y(;6eN z@0`^va+~A#G zDZ#U_0l13Vou&@6k)!q)w3h-7w+-`fMwGu7fxURvC8Ky3ppBTjvnc%xuU(MhW}%&a zPPvaWb-px)2iJ6}tWJ#J&y35@1G7M%ty2fQD)=yjg*SUg-o0MERWHI{bcr&)=DtH&u?A) zM`E56HxhrYu?uzOQ|yW-mOVxhwWP0Ii+^-PlLF$4(&GA&CVRATLiIRb{JdJ|;M$@} zE-psVEcD}`!NDUOmekqm};ztwu zVgE9fOR&>taky9w-Ugg@u z3F+<*n@(MhMrxV)he^ZkH!S9m!eeQ$K&J~MyDsW746A59{a2d3?riu>)MsX+PnxeX zOjtf-bkEl5wpAJ&hXha{tCl>*Rri3LFl_cIU(x?S*dAiz+8`d1#?L@*kPAvtH}tPy zqz|KvStYb_DRMjHTF@%r4T{2LG2K=`+K0$k-$NT*6aDQBSJzbPT0zHyt$R}B{6BCA zYYsa*n#?q%rd$t36@h-;=z(Yz^M(m)_~(Va^+Bd<$2lcASudWftN*{t>1n{z{$K5` zl4%3YmzWx%Vdp-=r!z(?b}IublMPbGY-muXcQT(uL^x=pEpvBF>|!PK(i4#s;ny$X zlUSi+Gj(Rc%XzqIP`~aBA&;zT{#kif90qEy9xTpO?$)(Eq9b9Ea0gy+p|jtaZ)gJ( z>r>ON0@-YkYn6F*;X%c(lr+)lf>kOds&c=4BIoiSr!8YcW@#uqo`X&8wSE%9k%Kd? zU=hkkV9n$PZWf#ODXylOBq<0)o^Tj8gz(J=XKsd(eli!?DBKA5zJugn9;klMnP8HW z8~(G!O@DRfXTgKcf}@sC#>`8c2GHZfU(fi@I#vCb(Ios2alBj7TCz2D;kvV?F-({O zEarwoJ!vKNwqQ={Iy9I0;Ab=KZa$ONOCtWGWd@SRGRVYZV@)5r}JpW#=znJ(94 z7gPO^o!SX6lG-g52Qzq%Cn2@G|4yGW=^?-5r#f40WtT&sFyVw+(aFJzCxyNsC)euT zx0Z5`t{UVq?v~#B`W#k)Mt?G+)#S{wmE9q@k+^@R0K_O8#t1H_sB>#*l)P81YM+(E zx{VOH5_)*zRYh^~_r2suD;yYkk@Q1PWcw#*G9~BepnUzQ+TT}xAD+y+IqhNYBTMb( z!#3n@f+d5UCmN4rrxWokc4c2*kF;WRhj}Tc9)zNxw5P4%lQWV1(=jEA#H1;?;oU( z&)*6t&BaST2Dia`w)-sO9}MKE&2=rL7%NDyXzP%ygvvkx+2^q6{H;no z1l<#Y5~-XWmfL=o8^&ahR+-#gJq>=#-u;uiCby|K;}gaKBz9)Ktv#L|O)>LYZFRMj z$?50ycY>v@+1Qbacp4C+|HsjJ_*416fBbOd*t>FaY!OLTG}0dJH&#B_Z#SI2)pO@uIA)G=WR{RV3+-8t!)n< zPL7MA@9dEPMI)oa45iMG&dxPbZ3eO`#Nz@yhvQ(Q2W7X^j^Ow@Go_Tm0Y28Ba49%z z$W!<6WOiHQdT%8kL6b|b>9m^c(Yt)#A@|UJfP7i2{~-I;(F(~#vvJ)=eRJ}RDBBxq zN9=K5oTbq`bDjNeN|4?*V0?q5cOuu=xV2hn!+jsb#E6j~8OtTq%FZz0(pT+f7ks_X zbyY9YA5T#(3^Ni5JXNg;zf(|mOi_VN^vQANN1<4>bo1}PcIs4)smq<%Nws<$$?U;XJ0z%T5V zjgl_UP4TUI?@uP+w4M5T557w%SDYZLhSAf738KDXKC>5K_^9eGuPx2fHE?@57H)ssdiy(*sG^Yw8DeetO3*3vsWt*}%$frj|; zBL^mD#&V}PqCP}oD*Te?Ub;ib?>4%yo!gom~KLcoi*%)6F2`gGP*A-0cCtb zC>H*sSbl3lBV~&`=%0(STel(Ynh4Dq*1fy3OoZRE^F|zB*u*SVc2}0q)SZP=H9 zwvPpf0#{C|Kbz_u*zSWsx%mNk)X~bK)nhSS3F8P*_nOd_s{V=)1aUs1Pog=XsE(YF1dW` zi@@?08&E%tZ{@qJlUmVEzsVS;mtL^iv)k=t@`*QD^IN!l&srhlEM-HKnO&n#IbOW5 zo@+5|P$s5RrJo#z?Rj5q`jw!|`&eaweiy4Zo%__?LmVVSNx)pZ{uEEbNHXh*BMq(o z4r&FgLGZbiu!mSt6KohMy>fs#vIl-qS?uQ@E-0Ce?@2 z`E5{^XQ7|25QBj4a;dtD9p+E&_1`;c@$};on$l4*387AbTgSzilvN%TAqH z*wpUw6iw$#-HV)lTaZL6N<>TCCj=BH*>4O(0Q&@zAQJsTSKf3S8K9#A-G~qhV;qzwkwJ95Eh!xCHjrwN-)p| zOq3llJ~#A<%_RP?qMG=9!DYy#cv^fCdWtVNt*B?^Rlp2Q zj4jNoJLmDQs8`kA@0n>p!ImFS-7jP zXe@6d63IdToZ6M!t>aw988n|TRuhI#1z<;ANQc**#(bBB5Z;1f&y+9u$npv#k2bA+DiMmOjF2fiBr0O9b*(*}I5hIS$gtJ2p z%8)m%e={b-9>bxCMel+H4L31*hTM+{t-HsY1`4@w<69xe&H7Bz$LqwAjHsSA{hkJ0 zI*Vu_K5~FON?U?AD$6xvqcPBj~*FI^%Up<#3&MjOX z%HO82_LfWtmxsb)1N0MisW#e~NOSdJ*5zq;um5ew`j3vQLD)V?VIj@8?mXagUU&P#Iqq;#|I)0A9ztxsjYQx%;3F=#s~0g-*Sh!&)h@cDc zW=(HkwWMGt!XkE$J_qc(qkJ^k23{3JiAoscf4uc84~IdD!%}*n?E48_Zdk7e-3g@% z;_U;e1-eFV1_30kFgoc8Hp$Ai$59WeZE~}Z7OCO}8fk9UMf~7}6J~1fQxRTxy3k&p z`FgO%D^U#!mOZN>^SjN(wB3VG!f=Ez>P*kRa>?V-bd?{1xHTQO6`RhSty%{U^{yp? zz{CU%+r3$#Svb=0u7Mg7U7KtR3Xx7-96C*PduVCGq*2~GgE_^zO_o0TTl=^6ZK>L~ z82#VG0sFiA!%3~UC4&zs**9NvJe;Xeew?P3LjG;AX!#GyQy|sk>VofhEWu!oBFH9 zfBy6&3*z8M$WK3}pBLJJJdrf^kl4~(V1g#i>&G!@`1~E!HwKIF;=wJ063Dd3z zMPc@lY9C-Q2LD4H1!o`H`H7|tU(mJw zoxWY~!srhNMa6k)8ZMr7h^ywC(J)DI*$@p5(uVIh87(Ued2`Tz148x&^!ZuS?{cVgb!>F>u=to#Sc{yWIOJgvoQd~=w)(QssrV_qXPcpuJNKqc2+KDhEx zi~3N|!}n;deNz>oecXWGzR8#E#tuC)nzb-K+b-Bj+2Z?I+r} z)r^nT zJ`@mJLu&K-&(8KS8_#skL3#aaHpL?>U#>3!L=)u1Rwlya%pYlXeHB5Im*Lq>Zzt_$_5+n7*j5 zkCB`_bQRWS@=mj)gX4w$j`_bc@~fG#Mr{)iwyx)ylZA_x;M!WmwZvb+O0#`+znY@9zCTvU{H%Y} zCMy~+YWFL_i_%VY#uI@Fm0C|4Da?s5)96V8W{0GSqVS5J9+NLh891yw33mAV2X%7h zh3oo!7C(Qb6oe~LCDiOd_Q`9jc3dj6&?=_X%l(8iM(w}-8XI4}tR>iy_i;U8Q2XNKQ)dGE!EHv)?FYTK zDEoZ-Wy+sa?yQ<#wouMujoX?*Qf|M+UW=03yapWu5VPgCm9(=PE|1umUfk(sUV3#$ zpjAhrWk6zb|H`RqQ@KuuzWU7i4fo-PKpL7oHtX!%PWw%>$+}c1n4QBk+UN3LQY4<8 z2>I&f)c8uYrq&?gg!uHWaIo14K_E6KO?~6fd$6BPp{0k+VT`rKD@3fDFm8}- zwOZ4MPJwlM;%hV6+#RNp!W$c}a&9elNH%^DTq3sd{!^Bt+B~WfcshZQS&oXZ7^!sM z+DcU2PDgumGTo$>o0n>9<6=Tun9I6V*^cl;Z}{Bh{rHWoKT;!Sz-Tu2g|cDDmQ>%X zxoGHEQG&)3@P?=w=N`A(rzGi_bjw(_Xugq$s}15pP^+I)lC;CAd`z;NX$?P{qn6$0 z)YqHx(hnpyKwZkex+^%Ng;xR^dW=aI6H>upNfB4h{;$;A1B|B~d7#{NZZMdKDe2ox z>OzkB`*7Yyo)fQlxw)s>jhcRQfVe;z+FjvPs+-AW%H41kBxDpoQ8>o;ew#D!DTgZb z_yp@FM4mdeSSP$eur5DLkN4ZqdRY{SR$Oyl$(`q}?DvT3${NzQ1I0$3xuk-*U>ubC*JjkUAp3$y8e%Zu(cf&SV zv-j8M3ugZsGXSA zD!7X`1Qf0W5{sA)chPYkg+%F_`f3fPJKCBdN@*?+wBP7LcpHvNW=))F%T3Hf9DNge z`4kY!s`4~4Ae301SCy=c&Yx9R9k;+ImVbVyZ#6U^YO`a8v%(pPjr-wb9n#%ad291HHZJ!;8}yU*}RY+^Sgx~<~AtIe$spJj(K zN_+1IHkv};v8%_cA`YL0uQtn>?rnMBO7*$E{UdYssl4%IxArkzql%1*O(^==@kEsM zulh8N31JaejME*sjb)GOig-3-?tm1&87;;i@%V?s=)9Sqk}gIyfq;H^!T*a|~7d}av91L){HRC$QVfETtq2pO| zj%RMi!CL*yrlalOM>rAF_byD@-?SQ@!{xSsy}6)SN#+mnue&uDtBI;_pjHq0jUhPM zJr5!7kcydqC&TaO^65O|gLQ-72DhXLD-VqiUELjx7n%dn9X9S9VKwx;j76-k`x2Lj zmie5u`!~Qu=H)Er#j>@=x{at-L$Rv4h za>!z*91B10t?Z9x9*R-@TG_xUv`Z46I|;-CH~(Hxx^d^WE1N<~UMW!Vo!Pa5K&>a+ z&%)F&FKmq5X_QJoj3n?DK4yV3XD8B=MU0YVX7(Ed1bLI^KHxdvvOSpm$}42Vhmyd- z44X0^zW1`A?EPAI;#FMazy5OumOfV->iX@#4?c7F2D(0|cik5*$nPjn+o3p+-&2H- z?F%WspYND49XXUap7dRj!5$G@1e7X1+D{Mu9P+#Ara2R7D1ZRc*}2DmUO9MIIa2=z za+eWqhZoPo^fcySTJP?pR7$I)d3RA}ta_70{bOc@!aRIS-`p1;%32P3 z`GD@evv)&Bj{;Q}DhSjDm;GfwUXUyGNGtjdmDBgxX|cLk{`dkVPNjx6oJGeTwDP%g zgR57N&~sNtgJq4n^sop{GmcI#0`je4&9?LB>B%2X(r=dM*_s7FFd2V`|Q69ii}Nh6oVt}v~71;#IRIWxo}o? z|9wT~$p#<0R1z1sn|TG>GY+1_35j?-N!{l@F(Z_BD>Y|YfTJ55zIr9g4!_Z(Q> zd$DWDrx`fD*`^vFQYh_Z78Vd{#k}EVd#CLkr=+9sd1GF;auoZ`B7G0!L9+5wA~&5~ zw&HF}wX0o2>I3AsV<7RVE58;ym}E#?_PcC_IQ3*~xwi;UqtJk6m!c75JvQ&9Bjpcc zwJ)`UD<_YA!h%Oyt04}u-CH(X-h&P^FJJXLisi2Lqo#M_o3b=z7HuH=JQ=0tS7~jT zgEhe-(I#c$ezvbov;xEFWkDV2SZC5T$2&VmcL#SbBmVR}{KLsb>Te$rFZ0LBY}k}7 zEjyi)_9T(z2V3L6fbFZ2mArf%0CwR?1N@_DG*yC@ivruEn%{)gD0c&B#&aMYBsX8g#)sukWVBy|2FYKgf@+^%Iy`vL0 z8lh=Ct30?y$6_m>ak>f6lIvX-%~X+Y*oiL=ZN@VMZ_(~>?G z^5(Ut@zNef93&0O>Q3^0j3r<6U&(y&%{I~xc|l`)d<^6nLlZqDs$-M$6CgLvK#U{`vj$ zUC~nr`o6Z>|2~gRos ziB94GvoaF+A4rTznOs!f+!WYOD9MXGZkgsm8e5hH>tW)RDajV?{4enA>)lUpr7md@ zuAm;qw4L<30V8$*FB03CKCRWFwf+LovraucpTD~L$3Vi>1oe}L1~n}GnAzJje4V~+ zNBiWKMk~#MilvZ%{bS$*xJj%oDS*n5wamBXBUT^$NP#3IgfD0wkvydGVa!WTz)E3{ z&h>H)9A?wwt*QZPE{OmlGv?mR)`ECqy-qGAZ{3*x*lPZT=!(71UaTCBI0!N_%P8RC z>lVWtk-g!JYX#0d>1cW>OKHG24-n@Pk$m}FG(P_xdVRdw>gTAQ6$wYY%lU?*=30Sd$ z!)AEri+x`GNn&0+_a_u!ZL|fK#dY?eeyyA9XCoOTq4#ETDtg%8HFLpy+~4h@1$)^; zkIMEyT=ZFkHlvvyuV*-i-lpInm&=*z$1@)E8B+?I3kVOHVY1Mm*<6ZVC|(j%wHD6D z`NlRxT~QU~!_OKIWQC5)eTh9HotX zFiIIaD4`m+=BU&%Rmhff70@zxJ+5~3f@VmM)L4|B!%8-2e^3m=uSfP4D+m_==gJA` zo9znc-}r&SVdiFGB1%zM2ypkBTl6Xl!ce8@A_n5;LN_4hE$<{L z-a%vNnz+gEUs)K$@q}fuo^nc=z0az1-CDexgOc|BR!P@i>--H#MMI;wh-5i$NS6d)+KppcdAes*vKLtfg3VN4}%C&HPIMKrp%E zaq*%5Kr;xGPYEkch1L(qABYVqMb3NV`(Wk9bJ_di4O&-&^&zlx8@V*eVYBYStwT3( z349Pt^Of@#a=trlZCbSDNp_{0)cTMLUiYcK#^THzO|~T-e16>$$**>(rdn;3sM@VH z9>yqhZ>EF6G_<8XsO9_!bxSEBhk&5cC3(#qAH1&|B!5)Ii&lH<`gyVqhQD+m$>@=n z$h!~2?9Zp2Jx=u1i+7R-aL6kC-2o@SEEh`4Ar=qW%zJ71$UreKE&K)<`{2b8mW8CX z2Ry@)ZA)8z`c88Uw4+LP|5&o(BUaqfCVP~A0m`@393(j9(gOL4@XDuVtR$a))zx+i zC2;&Di}6YLCQ3nImnykFCD+73bewFBcUbz2q}b^o zwV)@?w=Hd55_-)Am4`e>e35K zU-srXIp~Y^=$8}I-|S|(Eu``NMlXC_1tK`996D_uvVCqs*+n6g&l~Eh4{(eaPjw&r za@a{e+1x?iT-V2(=5-Pxo7|k_C5=s9;|q{$H^gk1-PtlLqJkMCSH#OT@1(3vikWEsC^sq`_ z>FPy$tmi3PF~2IH&d6FiCa@Jzgch{c1y8szZ*TwvfL$ z2zqF9$RIW({b%;Fv`PSdko&tiM97>j*cMM5CVh;8OU-G*4{&BXFwbmlQ3LUcQ}lQS zAQ>9ES52V>*$rl~`>yFPxFIvzEv~Gh^%7}TM}7JN#KjkNvEWlHvLyoFjup)MmaP_X zKCJp6r=|7YO)N*OXRl<=JM-iWE)n#^x{8chcuv3_Vba5>mA#r)?0{_Wsab$FB~eQD z9y_kjPc|gy770ozg#Nd};xJY?uko6O?($l4{K48&#IQ%RC@SazKvWc6_IddG>ykWw z5?=^8_z%RBDG}YmyR9Iw#_F=Qp75gI4Qv|-=Q=#&>`teL&dz50*;lWnE6ifo<)X2IjEYp9Qv_>SlX>X*)2>L-{Ez-^VFrkw29f)S$oX?>ixdV|LH8{b!+yDBDhy zC6dl>dp<0?p>0hpEVgt^ryE44$%==R1OfFX)YnKe!Z$>wyhOHO+p^Ncx-LZP-ofJb zmSBM@b+T1&xBsZ6NNJ5AosR$Uji|dR!8A@$cG) ziw;uCQpy!ib=-4xy0i_G{w$*N-i5GZRrC1M>R(u>P#*M z94}{kZ$r6Y*EOIFqD9hKjK|P?2BDS<+q>SjmhG9;4*a`E+wzoC-+$Mnzrx>*8={qg zf|E|)uMCqTX19T^#O-V2?10m44TuQA-$PWT7{c1?1H#yBju#myf%q6y*~w8dR3L|I z1U>#Tq&>;mYkt8eG34q^k>$gkFA!w&?_}u$o%FFQp!)z?X9B$i6*frDdvZ~Dt{is5 zS=J!E@?WwA%nsEK{hZL89&{)b-~bp(zhox5b{BTa?x_||B9rx1inWxXaUw3QpNrGCX_lLArvoq!I&1wX7C}WXhjR3Fmmx+%EL_;KWqkSql|idw|`$2yjBID54Cj)v)C*EA^*967#4n>+`|U2W+*A|#1G z0;}6dq@6FR-O8I)+GgS1E3<3C$6lb z?#ezB~@p zvn>@TFy&VWSM;WPi-|iNlwxApO9wP0>9kV)9uf3I^AgIM{5^cB-KvD?Ods|pwH|i4 zDpgQBIU?6@EtD~vC>Z5d_`ku2-rqtR63PKLCFVcx4oX79m09%?dVtD-;mqpKVRT&; zNa)wLo9YeURszTGzdD+E0F$f|3y$xTr=pEb~q_o zRBFfbOdpn;PYR38%~R)f>Efa)yap#kZ0Bt%GOvMKimW`OSYg3I7bDg5#JZ5%b6fs; zrn_Hv7Ix@rmM8hb(Q6o88FH_?IB}1lMs2Wyj72gK8B}+Qk_dl^fE-~Zn<#k+>~cen zKAeyCHZiaSd0NYtX5fT18kE+ClRSMg0UfWucke}G=W+h*oJX&qVd?76y<%0a;y2>kSbM{%Suy-=S5M%m0Swb%?oi3;7PP_n>;N^ z<)2fY0Qy0`xUw!Tm~kS~V)+t_){qOlx<*<{bn!A)U-oyz9<`g2-3qSsJlCtHqDFUE zGLPp*`k++6#DKAo$NE;k_h9=YDavNc;vRFJ^2{iMp(#;1sr5n7*VMV+r%St!I@U^g`w__CZsv@+t2 z$v=NDNznNH+MbV|apq{9mD`>v|0a16{(6%%_%+r*uaZz-;-{hOqfXwqdH&n+*szfj z7njx^B1=*ik5C4SRl|no!lj^MH44=fb{gm29$Njr(G5tW5W*?21y~lC8|&*upJG0u zC#d4OCKkBDdgAuawZ%lwr2f%4bc2YVdFuW<3|He9ggOf+t6r#3@P8mHdD`~Hz^X|! zAO`Xhy2AH%@63;NER@cXXMj|Uv; zgb3A+6}f;&)=k`a=pDwsCz|PtxpFvvoBMt-H-Cu|>K+MYt9Lj9oVD#Pjc?2k8g6K= zgJ65IAwm%ALd<8oAs-Ia(oPoo76FT$@ygWbzM@yAdlXzWJIKk;^iZbi`9h`>tdrU- z%te0nLmh-&8U-ZQB}JAfI7~`YuEs3*Y%@aJcIAi07j5`xVvjm`B5%~YH-!i#d4d+{ z-j!BK!19{gSda7XyY9vYm63{@KJ3SP!Utd6POG-f-rCm5wrjgTY>U>ks7F5QR%L~6 zIFQ&9!x!t}*q1P|pV5I+GxHH~NmLCdto#wDzSf${(!*rcWiq~GH`)M*p2*|J^0J@r zQIo38jZR!TDNe@kdDe{=W5N`e;H_9W|76tmTMzg@sd6>liVpvgz(O)o@(oMzXHsTh zY^j^fm{{6vraeDM!l*!tdi9J3@O96=e#0f>=Q-rdIsPHqeV-&@jPNt0w_F)j54lo8 zNw4Usnv2@Na-H6^LJ8^VZ`P8a`t&(%d>=#O0~n_>joyStWks@kP8{L)%VXB2HMnsJ zJ?v^l`a!eZ_#5bY&rC~EL0+=l!j>~_5^+%()k6^LhMctW>q9u^>N=uCVm|3XLJux; ziZ>y)J^fZnu-B^8X-V%LC~Nd6(uyza+OMo5p;1`cKE@*u47?kSrLP=GnCa(xQxHF8 z=>+S+9z?7{@0DC{sl?+&lyp+tuI7uZyH{&!(3Kd=M=T7%uNRZrZobrhj&WTC$ygd> zCou0588a69skJ`wMM$>g8a@FuU)Bk^$fE0}>zaI8;_RH~ z+2{w7gjE~*SlJ+jBbF~<1ML*PY@3;N^*#`*-swKhM(?w2&@pd_`x-I@md>2_Vu~yz z_sC56snQKT0+j1oe?${RZsvCU&q)yt@S#@5OxQpNE4WMmvQVu^R14#mzZ?{ookcsxrTi02e*y$afRk-r0?1CcSd%I@P3KrIuZ{qXLqP7lf>C;`0L1wY_fwIzF~ z4lA>}Yuy8)gJbJz)ee5vCxsXGLn*e>-OtVSjJEj@c1ZBM~hnA4D>L>OPS@cAV$Sqci3kj5r z{Ua(uUnqk(Z!1<|B8K5N@^G)-JOmjq+QZKiFbo!_Ry&B(RFM?JrbR>?uj3^D>NI>e z-P=GWkA`_i^d~<|^|hG~y$d4RHH|}|_=Q?gdjTlX)QS-9WN$YUx=H+Nt#77^h$fQ6 z1__4oPl)z^oSOBFSb*8z=5T_9Y5mi(?>dvhoOn zG~Qka2H*1X%k3LjC^KWqhHq)CZQFy>Ai?)cZth%YLHN2Z`1(Eh8W%oST3<>=8Jw3{ zXx$~>nPbct>$!1HN!VIWG*OSkUP-xYT2-JN5%U4!J|D=^1B9dvnlg~~i;TU?A4u$s zynZA-+_#NrJw3Yig7(&iGOP&OyFtJ+>pFq`9K+anX)1YBAND8LHKC0{^1@2*Sp#SH z2laY|Jp86cB{2k=PuBD)XA%E_CO+{-aSC?RlX)F?QBW_B$%S5z{KD`!c->{5{y{rX z)#&`SzLj$#<}K1HQo{q*{p9`^c|!d$`?IHw-U-eA*@>lc>U!~=73CJfo{jOcS1)dn zEN+v%U*h_<2uF-vkoANC$gmVADG+H^k9e@=A$Cu^$)m{7EDf7N50v zt4=PdEuzSMu}c~`tdo87W|r^8XQJ^3N4T3{**0gZCc7T6uuAan<6B=02le6lgBo-jG=IDzlIUf!cUF7K_X33{WayY_%XZVKk zfQ_2{m5%As7Xl{r!?`%xB>IcxQG|d6%jX*cSmu2_hL&l z+2+BFocS>{)+V85MMQcZlYK3>q^kV(AsCA^6jd*vGs>6!U0Mqsx*o1qTEj5c5TaOz zf}*ceXd6B<{&xIr8rI0lRU&P(Ij8|e?qd>KxJ|zym?ShHJDru(f<$Hb_L*gw5PB8VnC5-iqq$ zkURxMGw=G>4>FIY#EP7%m7RGbPkc3@U{||4krqjQo6+qsc2!83M4*6O3(&Rdw*7o& zIC-;k=;NW~{wN&SMevxoTaWFMe7qwk8rcJ)x6r%%_bXQL!CV_Cy6t|%QMqm305jiy zM9=!f*DAf_WMWto*ESmiD5zQOTstheEma^v6%)J{o8vhV^N03fGX1n_UsA;7=gPj3 z;&RewSM#%xy~q}Y9j9l8w}yFBm$~xs#8JNLqV(cmv9-r2ipw9@q?O$|?_I~I7!$>S z307%6HuO!G>fj@L(f&h`1wmZ@=-$m# z#Si^ee@LI-c_SejrjpScA6p2By5iD@ZoX}>l>*>ppIR0B-5!3j@pM0J!w$}j?HfjQ z>8$ozGkcruf@w6#)nv7t27bkEDb85uWr!BDdI?}+>nk2NCh}0XNQeXtiA{e1ZKlt7 z3oI(0EPOe0_q~QTefRXjFq{*`x+&zW*22u$ob5U7-JbU14|l31*7GKA zzDDxok!%%YZsk8TuqBf1| zkkOGshrU^cPr&$mRg*ku-8EA#n>thiqE!IwjuH)l*RaLBCyr`{uxjcZ(9wiaj&Tj?J5zxa-ZtBd~EpUwdupq@1fjrtz>FnM~o& z!0|AXXVR*N40d?59{18D)hdYhy=_|-pJy0=t+a}9D(#GL!OKSB?Yd-yIF=!nmW3d6115Pyy5Mr>)tAUCuS9LKyGYdhSl zq$p`G-gWgw6OVRQl_FAJ)y#z#eA#A=f;8g_oDr|>8*jpw$d8rjp*~bctnX%Q z(C4bZ-+M&|lE~-&_~*SGdB0}*yzACLAD;kC2Pik_KTxsmA@jCw;0P>H?LJOcrIdvm zFz|5Id%QVoH3_#G(v*7}w|eE|$e6k&Zlx>bM|b5x}juOgyv;{g`fRDCL3 zm7aIwKTyJ_B^$EHLt8n!z@61^GozDqlND6G-<7XU6q9P>_&i%rkE;?`I24!EWc

    lW7Fb=Ds8+Eii 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 wwjOqfj__WOF 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

Qb#){LIT9 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 z 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≻J4Odsx=IR}_{zZsNgn#?05O!J>G5FWuKpKu&X> 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%0A&8c%@L^FvspDw}ij`0-JP5Ijb5Kde?3j=rpQ)(- z0C|=anCFO(<5e521nhWEj&JqE@rxzB#L7O@+lc5_i~;)BYw+?- zR!u$Sy1S5p0RR!#Ao_dayr0GTu^$pHpub ztzMdi80c_Pmo-Lpw~sK;^@mhq!X_Y&hQ4RK);wH3DM(b`;k7-Ar0@YCV;w&l`jJ%{ z+I41SRv>ZfT=?+shuZn(I&fz*vyI0XInF>+kIYvs3X!LKtDDq>rLlWLp8o*;6|Ik& z{{X#c+b17){{XL712G%7Oqhoq!x;za0H* z&;B5tDqn~?IC-Kn{iWEbI2Z%Ie-FaDe}@+Ku;>?s5+c}gcAkKY{{Ww@dXuFX&{DRn z;ZtsQV- z_UXk!&bwpao{&= za1T7!G2-p-@cU3ypKj#gxCa%bKYB?MH_DqNk6+Pb)EYI`AUKBV}a4 zT=06=%en=H`+P{!{{Z4>OlD=1YMk;2$v@ZYUr|Nn$DwJ7<;F_s$&xq)k5gS3x=Njr zFriV@xP-PjVmR&vH+uyfry%1s8GOr}e-E!(VI;eB`_;hN)naco$UG+A--)5=-T_2pnXbQXB@CpW>!3Svn4SQW)~`Z3KB|kJgZG+78J6_j64kWw}m= zr7Jhw19wdH^rj)@*sB5t0-OWT6G#uwkr2-D+L{S2zd9Z|9<&Vop~eBh>?z!qiFc5> z+aKl4FtU=_jxJXO?e9+KX~MQK)}m2~e&ZjL(~5eH4n>O#n|O%W0Qq^%J%kt^=(hRC za4Ch~&RlKl*p8I)3o8*DbmY(kY_{e%{{TX^BanClkHAwAZG#Xr+Y!(A2lK^BzzCdR zXK!kZDJ_sO^JAqKYpApy_S$n0PL*azar`2gELN@Zn@BkHPzU5Vt1GZ$=H#vj0-<1X z&9rp&^XRk5+b8DZrwKczx!^wzvGHJnAa9(}zkZb@H|o_bWOJNb?M zDCd`OD zmpr?Vm3BDoZ|s^^hHj*{((U8@**QUuM}D8qy!%D*ZlQPM1c2DL{CxqP+5VN^_|{8v z;kg!Bwy2DfeXGkn7`iw7EGZ0gM)`iBd)K2znw2*vxy4dWGf5u)n~jyL9A%Sn;~#}N z6twH|0rOO!FWDf*;qn2F)JAb4;T&=S$geg_R(7YMgW|V_KLuTvUzA33#dwd0=F+72 zfV*s_EHAhPa6RkUd|uGEg{}ij8mYDKUQ6L?T`+k0m}=HeMaM|nHR|D7RHSg$)@b+G zv;B*wl1F*AF_&D99`FlbZa@S?cVwoWnenrNr?d8=w6RC3MFmGXnsk7}xCwrQC}h&IX2P7mu`DjFR;;{Ya8johBqCNRe+Fa-c| ztR7CCs3$CbW7ej{TwJc#;PgGIgyEPLJ#$F-NZE|5)~ln$(|@jRz3DrJ(LPS*kxOPgT37lgpg zW@DGbuh| zwYOp9;D%5^{0?gA%FQ-R)zyyyv6I9SHl{zcH9QNfadCYP7lxARcy76<_>nAC4eiio zTy6vWY6~mR5DyDO{{RzP%=uimy>vb^p3WPFL89BX9AhNauLmWi){hvIPEZPT_N}DU zW6fhYrroY{npOV*iN>jV8usy&CO{|is5LED#VM@768=Dj`9pNiUY~UT07~|_v~39` zmu{Yeb94C7CWW9%GS)|lg95nfKA`dR$JV*lDj&*g>?Pc9haTzlYj%-|B{R)+Ljl`7 z9A~9vMI1KvNg7~{pl2MMboa$fuP)D;QSJ1u8BKFL9dV9?(tocwAI1$dZ3N_;`T_dX zdCT%K$3agaw~v*GCH)j zw2_*tm&<(Z$@QrR`mxFAew33bq3fK#Db$LT91Z}^&b3?z+{Yl`RO9_xe2w?caw(=u z8pQ1*3)8MSq(=Sr3XEs2dQvfLp1*s&LQ8FIvU46eBBimUT$9W4nKZj>-Y*k3Kr z%6TMaqYR~=fH2q@^c)I`hbM*XYPrVdB(G9xC1(c&@#~sQo$Zn-xd}v=AC@u{@(w*I zz^?v_(9}wBpdOewraAf77#z~LTIGUB9Zn8MN`oiN=sxMB4Bg4=&pm0QmM3rU{{UK- zB9bH={{Vj*fa0e-M}QllAB|QY>wk+G?@ru05!#w!XmP>kG~G$s}tcVztJ@O#ui{Lhi!sHeGY%g!+5@@gi>Y;to_#)*L(k=SN}a>InE zfjbC0dV0`$Dc$KuFj$*RLHpsMwqlo;Dgxq(r#ObNnQJbkE~r2>$?C zijXh*DjX-@BZ`RU<^@OHpkik%=VAN2`sR(xxw$?1PyyYaGx)z6dIdAL%^=KlBm+SC zPY05E&@vf#C!W;v{<()I-1n%?;UOG-<4Kf-b{iYz^y^R%OxNn%HvPnYbHuB2y%&VVZmkyu@JRc^j=d{F)C6f5OxP|CF^}__tEbv0 zn<+OIaxg!27c99Q{{Wv#oLO`g>zF_Sew(D;e6;t7un1kmNT3PD%C{ ztRD#(*T>V6C)#ax?JJBN^!zJcNF|cO>-{#(PU1n&AXh!2&vg%uLZ3EI-;ho_dWwc^ z9b$GqKUHrCYRD&)WkT&e@r-}5PBHOzruW#c{qdZ!^MHM945>LU`vO z_cguyHcK{qbs4)2<@nLlJqZCv_0E0Ct_*6{C!vGZEd2%22=nd#0NfvycaRbGIR0L9 zir_SJ`|B=8`hi}70-qszec$u^s|dTDGDWSwFKSW3oMWvfPSp8Z^QUBIw+)esmNW#m zSLOVwMGQ#CUJoXP+EQGW;Q^@u8;%LaJA2Y7GC@DPZ}pu=AFWA{LS)1~Y~%nkX`7pI z`JWl@P9c1;m}B(W+5yEPtS zo!JF5&2$+8&#Lf;rBSeoB8+fG6OM6B)n`aNKXm)Cw$08#!5IX9`m3Uh4CeWzW5Iqe zYwK&_?-2#Mw_Ua@pzp^u@GhAZ&ZY5w{&@qq`)TXXb6(By!&;JUY3-Kr&h>5B=NS4| zDc~(SR1mvDGGGTJdh=cue~RUgB0omqtQWGU&ep8;9Wo_{Mz&C6{{T|8+i42lXjj?C zlt;Hp=Me|mZNNtAGm7r4VO7x8i^Kk`@!q~O4;Lxj^gaAUl8>m{n}!l1iClcA0B{Z~ z4p}8>-1$Te?ZWpXxvLivAiaHtP9GW0e=2l9mqG%b4{CMh$z%06G>6`Dq@vpbd?prA?S8%mj}0W_u3U%^BPUQ6*pKH-`FFzG zq}%2Xl}32=r-I*RAcgzq>CH5R%S|pcz?)~l>FHT6XA(pLaAxc6?^h!uG7?n$-B0wQ zKnMgsILTA#O^IkE1;y0t6*8`bsW{4?N{;G8Fj)ka;Dm@MGM}3m9Zz0y%}AbMkph+t zx2pBW)}eIUd2X(U=8=8*rELRClSHmdt1d7O4rbN`GIT#${ilS}+#+se5JTH*L*R@u=EN;<)uTTa(Doc%qAVF+_f=^NH zP{vDJC}fj9S3Gci#S9{b$$@Pf{_h@Ea1C8hqT=1nwYhJyvB=Ii13do#Dxej7)Q>Jn zBZKMdT4p(flPE$|TgXS?C_yGRp<9T%4ApT0{fbay zKyWYxL|MsH@`~axIqyxeBu%yxEQHOHV#a&@DzM1aVFQf5eukcYMm#rOIjGR)_ACy3 zLz;zjq*x zamXO@Dy&WHXGgWH@4tV8lX7AY+Vy&QEH$ZtVI+ z(ej4jZo>ptW!i}>;|C=~>y5*ZeGX_~wKU@)bcrJ~WMJfz-2VVt$KU>!amgUB0Q9So zu^L6%nHX+TGwE3}hY>Rd90OA(Tq0kHm5y)Tj(zHktjyttIG75OGXDT%(_ayg_RqV2 zI0u1`aaQ1tA*edE-2LH!@{$H>R;4`x=WF#qu=T<3St%6wR#~M5aq|pkAC72{M3^Vk^a*bNf_uoDK=UoxDWQI003nA_p2y?3CSb@00a)kqo0{ANb|U0cy1VsRflCd zRrwN;CiMpc1Tf_L;+4i!gaN5Yr|(N6qUg>8g1nhG1w705@92+MhBtv&uUi!7u%TrKsm)g+d&!R?>XnSTDt{euq6)fERr$6VbmJT zmQUO%`#`~BI`h||A6l8oBBGlnMci&k^6};81annoLQ#og2bwSsQHrvR!qz5J4GhF9^?5|Zp{|1nPdxR)18DTK3LDM6q_0XQwsCZ9eGj&L!Y(aoT_N48D8q#wI582aLzNH**MURc!dBu$}TpPS~oLOJXQ z_|+xcZ`lHq`<7xc*a~a}RXK6x$ZH7pU$O|K4BYA-o-@QN}ymkW7?&ZhbjHZ1&2HiXyeP9 zBb97^G`n*T3Y~vSi*WK~=Q)^Q&=wK0N_?l@l>On?J4oiN7l+G>3ER1=kIr?mhFL%{`}J$i^lnJlbrtmPAT9G!>>wRM#&otxjlVphheu~ z8>t`UQ`E+CH;jkqXdYR)U_#`9$*N?Hi+O_YY*FDP8?pUrNQV;SHgndNAC>hztVUHq z!RH>7ar1TRDv~9!jxfiUrQ>2?kGbhhaB$?G;_pcqI~(TDUbL<;cB!|Wum&7{G^@&B z4utfmn79rAWBB5k8$M4RzgkxrGp=7CsHZmLY>IZ^FHyxVbI#M(nvezZWB?9V+MS8H zSa3e@KQFCE^LdA^H~LhV8DMkweJK%fa6u>T)RaCp0l>-XDa?P;1~NWu(>7s=%JM0h z0&m3AxU5XXWO2}u#W9Xbe67=v2VbozE3+UDR}|0*PEYAT2)lzElmNdO6)Dd6@K^Jx z0-q>@j@;F77?KPB013ww0OuH0>;A5hNHGuI4c=aUQQn+RW0*6K^<`HA>B+082h8hhXln5mSE3}>&c zM5Gg($%M6^;#|$$gH?OTGfU_AQ2f-C)*n$`mxh@CPdQ}kIfzqA} z2?-2-^+m2kqjN$Vo83FbmUl7AJfkv)Ui*E%opE19_-|E)=fl=cw>!ZAXC3S31=}^G ziov>MXV$u32I`X8c%AI!yO={PyQ5-81Alk%uWKjAx)V`vMtt6Rma6pJlGPtjmR~7J zextQlxwlu-pXE=wK;yMivzSY#+7MSBSmcnCiA9 zdFRUhFG}HWt+tlLSRndWx!>Cch!Gxo=D00Z%jTQ+Eu0>rw44-?H0pAmRVv}L&su6Q z4hTI>ZQoi*vo;ay5t9?Q%0B0%amqGlYL(4PS;v@wpH6G5v$!Bd`|ddJTnd~K$mv@a zlQ1!j>-VamD>EvPx#~7|B&vflVBn0`Lj=pU2JB#0o7vnQ(PBZ@xvrkp;1*1=uBgc2 zoGfF(kw#S#G#iZPX2hR`&h8X@1Zp}K16>`^+b{fQKz`u!zd??Y6UzD6z+JZa$>m~ zbG1J@Vs8%-0mnl?4{?=THydh53MsnWIZ{G#>N@=@2?-3u6OI10Fo>HgBfN}~P5|%I z{{XK@WUaINDl!K?a|6fYRpV^SjGkM)ND71ty^qd%(*XVG>ON`)hq{52$E_JEmgMfk zCmH&hj#9!xGJAtjks%hNZwj~{o@g0hGNz$$0%s0K;ZjKCL{3vXIK@XAZo776VlsFJ zpEATFAwzZOXaX3+F&UA^UgT7~ERB}quOgVa^2QKTWDUJ?2i+e^0Go0&gXS0~ikE1{ zMtU3%!-|Cw#j`e3mj3X~NfUnQ^S3*OJJ14=!5IDlQ96Qze6>67`A^Hx8jRy<$s8y= zP&8oP+6GrDoa3;rPUa>?akaQ7wR$;RqvWW{^N&iw)Ga{-h`O((1*UME>|?NF7$%?u zhd3vL#tt!Bw-D@^zE;NWm6+>^NXLFNNuxw%Yg%o#p`^Yewp)d_ZKb+6^fmLhi8M*l z`bB#wfe+9Qe=7P|7F&Qu1{$&T-8$a;Oe~Svi;;!P=av5efQCQ#>8ibf#WV4tQ0$E7 zxePJE2eJ0=$E5}-_PN|K$K6xKN(%X4`O&xT@(4X?;K&3=&R};8ek0SKYo#1TF%QhS zEuF)lW7;5U&!PxS`?p8o*- zdbA>hM4vR79eMu%KhB$l$if`ssO{^T0I!A(kuc&V0OO`QnqUpI<=Z<+#&Q79PTfUa zF{>jHAXB%@3{5#6qq}hpEK|BNvFO%#KbOsZi|klmn={ZvdS_W8}WmWQSU$w zLZqCB;9~%Fsheu@-k_EZ_EjtgX2&3PFSc;k=jQnueIaF4`1Zan^c(-74lS`a0K{NQ80 zI@A2T+q>fENlHOs+sVA6k%{DZn5fT=w_rQ8`=>nR*k!rzC(N z0tG#CcJs#^=RWj+W>`qa57)W=v~D|x?nZg%kLOR5aG8lz!IL>X{{SkC6Xmzar{?^> z`qag758pg5QOAGMlP#UQPu?f3DLknPKh{!~%LBB1%zblA0ySW~u5dHYr7pr`kO-w2 zJ-^Rt0Uk;H>vipq%9nZEaL1FLoDXVZU}Al$aVXAt$7*RzZ5d97rx^TcH%v(fcpJE< zWc;Hip`eB*mO<*keNW{~WPRfWcs)7+QwBVIje5-tzt(~>*&Ox!DK?G#PUI9|z>$NVy#Sy?ca5jyar1W` z)ZoJz22KY`MpP`IXFa*3#CYgF@8?WI^OXd#KQHM_+w&Il&T@K+e;b(XVZg;k+ps`b z{^#pJG%v}$iDRAzQ~asw$}^Vl*P4K%2x9%?b?1ze?maO~`SJLkzLW^Hf>;nA?+oMm z)R9A%Odh_K9kf%tpaoNcJ6By}Vm2WlagM!z0sa)?u0-cyEG4S=0BTZ(whxc4#)0+2x2KZ{?Ys+GE`3iuirEa5R1e}=<6rF`z({CHaN64fF zK|sa;hop$~Mu*ZJA|W6h(%q#J(xb#NP)a~b=|*C7cf)|uJsN)R-alZwu3gXe-E-gP zoX`0rA?PE#5C0Ap5I2};VH_#_xaY)B3il25KqB^Jab}v;O!tGWYuJz2B5DjONKnFiUsx)a&a0=i++V8$R z5sw=;1!Z^V_N-V}BJQPERTTSK7h}8Y=hcUS*+u{#58UN5|?MOop{UEj{n$c z6V4+4R$L3Pypx@T@VBJ#Hl>u6-R+CTjD5eg%w9dq0ph5={u=JD2B+eKe%({`YTG?LOp(`ri@<}Qv?Cx-~-d2y9c~>nOSz&C&VJ2UYxXtU5Kz+X;l5~?Lu5XC9BfKh4%sP%xcYbHBi96$ghx6QbOFk4; zas7N`(Dn0Q{{iBqWVQq|8Ko@AQ8-XIaTqLEcVcYSUCMIm=UZ{5<%k3sd-vdAGvrbV zV2$|0$puYhZeVMdj07@^5+6CQ78t+yncxw9uS<4zaLO%tZrDZf1A4@2!s4Okkjzz( zxE6kDza?~6^$S&th-Jdsy5&6c)c0H44b$v6r93(U1o}9K2)!XKa4-4mD(0+6Lj*5v z%a`bTn?k?k1`QrP@MFa0O%r+yNmnZT?!YAx9B@%2PhMwNg9xSpEF>xp`1)%NsCtYF z%ExNFYKx`3Zx;VqgYT9J&`sbegyJ-c%J?(v4Tg6K*)DQ7nw60wTgzRT=zpqqb=y9j zD6SjE1`hd>Vv8e9L#)ir&k8>Lowz%kDx?=EU&PP|++&XdkU@5&Uw|plTb$AYa=KX= zJKbKcECA=y0C4q(8Ef!X)3cW%@tQ(8?!G{xu!s1^r-cLsoE#fW{^u)WD~F&M-CDN5 z>^OdM^%ip+U?(vFadxQkh!V}v?YAdcTZ8h)0l0fHuhdXv%Jqg=sV?)t@wE74lFmO?+ z`I_GiSJ9IPYb-apYxrS)+-@g59OF-ZRT3V6*)|?q=&t?uK29c%R_+LBa>T=J-w|jt z&Z<jO)>kuGVJOEmmpdz{NdImHJC0A>^| zW;6A7f+kn~z);JZ6eU^1(_~_lc+FH>5Z#FtM#A3NvWguB5&J!*YB3iR4Pd+h5fNT*y7R)AaC4a zvAzT=Nf7XI`l~XsYSfC$@yJzVA44;XC#r~%2-bI^eG|V4JOF38?d#K156SK#+>EO% zpg|iQWcYaOzm@j1WpG`}!#Gz+?gqmgRI=zeVv}0?;bPmz{{U)F^T|su;uPCqpHnKHkDR))V*aZfhHVBoEs~(jWOgdE&HEeT?f6 zZGhc57N@gjoCqRTyri(I9mmR)9U-KUUKp%`AIsXr!M+7v{XLx)uAUF+_yifH|-%(Ti0XBYwI!FZ2N|fDuzA>Pe zz1G+3@i?)CFXf{x!G$izpz%)&Ub4?UN=LCJQAveZKq?&VHFa9AUv2a|t zLm>eZpOb8;;8#M;`Oc=-!rc!7H%&N?)3ojPh+8Q-J*8E=o@NPES()r<7mxN{1uCor zOhP+8NIAE8mGH1q{d)3JF%8D%c~LGbe&8)^VZ+RVYtA^`S2tx#OfjW^V)Nzee#|F) zFW?$0I$_@2&m5bhmfP=vn(PZL-KGx>b^nCT3DB|D(c17g(A>=jwNZkx$i33Y&gDk8 zr14f9na|mndj^J)&4z+@$>L1lbs^O7q8fEk}cD1>+zKwdJ6B%6=@Wl(bvQqkUh?8P3qY9jj9!WlV9O zn$7^D@QF-^n{)#0 z=I}~CLHYCk4vNFtXbnmoiU2Dw+D5-Mbq>wyioZ8<2U-dviYn;Rr|y?jrOSU`%NqOJrrb(E7{s%cK=y_v7_ai(XbpWzykO6cUwPJA)hJF$>JU2m z#@PAClOwK7QL{t%tR^j*E-X;;X;|&vPzPiJZu&(murgLdLdb=^r#ee+s9zpw_>xv8 zEP9x>$L{{sc$t?h;Txt*=Z6@AX0X&F>F{-OHpjYGJ<_RuhvA-$R>!4z;dUyaoo7QH zVKJFPmfRYe?!|fz-ijj_j~8qWp{@br%&epC%bpiS8~jn0u`8}BC zZ(_Y;1{z4HDgUFMj` zjo25mnSTG1fQdEN2{g^>udS(+tURaB;_s|*>^+|IfNuw|r8nzy%Iw zTFSM#dF0V=>2|=SZ_sby<2gK*!q-0}=F`PCNWJ|u0+_&q2iY|h`#db$*M;-s{~jn0 z0*E&vxfj@f(KdT6FjSaL$LOG8*^B;e6(t$cTb|vdSh_oUd9vHDTBsFYM<%m! zq=0u@JbgYKV{sIMoSJ2Sj%Qq>S-{f*eKO{=u;H5@@b3KXsJgeKD{jRrU;I;Cc<_G! z*>Q07dJFM{gnYnS@0$s(r(fi&aZ!YY6%W%c7Ma~se2Lb!Y2iTef;}gV;!K{Rz}O~B zLU+YW1JYxnwKYCKHV;`Bn+|PI!#xZau-gz&?73;f=NV{$ES9#UMfBM`8`W6qe6ouj zKFK8|ois9e7i~m#8iJ3jOGNXq*;zvuP>`Z-o^3ycCo~IJ`nN6BE6kM6b0G}f2I{Fq zuM8Dg&bN5BU=iODm`dhS&rLI?Sl19@d2*D56Sn9vy@k^4wh4%W%lD))q~xqz3}v4E zl0rQG&i=QFkQF}&n?o?9A8@>@T9xRIIwaG4g+-3(vVp`uJtw-_8g!7)SQ$<;XDj9kIA=gcdkf0@dxiiO6|0OHj0hqfAhWH?@$i z*Ql#~_bKzpme*SU+y-#dkWXWvoB$K=swrmQ%IZ1c2SOR-$Z;F3(BG>GT^k+UvkCyw zi|E~JgZIZI;zhh{Cj-j@h}a{Z^rHu**Nz_P)E}&drCL`_t0g%F!$Ue1TVU@S-aSw} zi29*?qj(E!e(fhROZo3u(+pco-?uRvMfnMdNlfaI^&X3GbzwdyaD-K z;^x}G?U?rPa|V?%_a-Ena@(Y}V+Q-Uq<2%9<9aFY)U=Bb{bmtC`*8=dG$~X2$q9Ez z^0nI<5NbXMlmAmi@eegCk1O1EwSh_B1UGIBQs%^|q1jBtnvo!dJ<8ePsixNUn)HtD zd8BV5dHR^Qio;bE;Y%U)e%(H>AnzQ{m@PxblIGaF$mMfKL=pK_mA zhtTwUA|Gd;H*Yuve`h`k`;2e1B@1jh!g&aR1zBG>PV1gw%fRW`EubJp{BS{R|R zJuB^dl#b(2kVf@L$FWeYo3GLK&y0vvbN@3~m*11PV&Alo=-6^Ek@28VzzPXg`pH66 z6IA~7x}VeaT2pz4e_S3?{NGk359x2)sb(@s;l24vl#xpoWO?=NQ~k6JhF<4pVo)aB z;cAh{Z|!IFVwIye?&?uA=qd#{R&V~im-L_T;!af#eF0wugc<2RP_7(~1qCrQbYg~Aw0znhV7DThFs7yNie zP*V*StI9^Iy2KuOlVHn`wQQ<(N#sOO!BIrS{I(HYF#A+amCnbcUh<2QhPtDc1m5e~ zxrqlUujq%A$^^T-uU|ja#Z_?0bxqPCt0y%)3&vD+k#0qdHG`U@j+yz`ty30#u&s=5 zxwcb>W(v;Vl>YJ5RCGmpueq(`*8r~He5*fVs`<-X9;-XI4V$`3H|?yXN!i-R?SbzD z?3rV&sO_dH$lD0ZwJ!|{eECb~J6!4ad&u#Vl6J^(W8mso{&7R(M5@`oAqf!IUoQVI zn%eHXnBL6+R)&x_?V=yMhlu^55jgI_dGM_;48x@god<4>wGJORcu)GIf(1<-Y#rHI zeV9{&k9U=1aa?ij@b;*e!K;M_A8Iix(kkXkQTfJ#Z!Q!GmZ!?zfGk6X>3C67xWZAJ zemU5+-j9sVb+wf%tBCEXGLh#CCx;9jc{Rln3wYUTQ>X}D-2Q3r-v?)rgk6)6*r$q9 z#&@h9=Ly=GyArf1W2N1GCvDJM$LUh*H()R6HKvR7`@|+sIBqFbuCs3f&h}xB6VC99 zyXhD<%V!PbrdBNK@>6E>CyUWzT;G6N_*-q}kj%F&_#>YF86ZPko8NI)jmv^XmFaI# zaKpNil&r*);>wX$F~H^*MS70*!pD9I>Qy)K4~ z`#ro?$r=8>#eDa@9%t9B5eVxb6`5ZfB{U4-(5}%Ua@P>k9~9UA)1Y`#&iY}0t|?M? z1Pixm=PkPeeLj3o7xDX$mu>LhKoc3hqe|6>{@jTuJ)B6ZthW(we3%@Z%A%uuvnYr| zGUmqtkep3qAg1@H-i?jS8I_^uMKoPpxjoa8V@%Ddjj&3dNL82*8_%!|X zv?*Kc5tAl3m$n@`JG1+#EYaT*(rV=M9JqK>R{&3;M=jE{^)PrmyX!!W{p{M39N;1S z`(EZ%uD5ZbW{q^I41AXU@A)tOMuJTy$GGGCuFJ6>H-dlQA1UN8Kb|$!DmNT|%%1!D z96SEtJGVqm-H^w<$9uh_roK&qQd`R{UEu^a1LnqU z4M|O4(vTz<-ap({a7prwdidH2PImF$vjU0o?+b-$d9D7Ds%E{(Z3)_W zhEpRhe$6~8Z?wntkKro~=?gaEydZKN$Si*KpnR#*h#m18ou`n+n;LX&p+Amx!=%Bj zLuZjKpBYx`hj_4}Th|5r!k8d{BW~)mvq^Y$2XWV}#1=_mt(mFNH9!Zu2Ud*n_*iU# z01~Pp$t~(by$(KOkexq?M~pdZ_jPKY>7yX&kO72O%Vgv=TNECiy+F- zngWUG1dt4SQPhy0_tmuJI{!qHzyXD0g$)LU6|K_l(l> zqruzvhO&g%-}4h&;TIaHt}e~b?6Hh-rG+OlI||V2$G0Vx=N}zy zPA{kUqC0RJ7|dd3_*V6qvsA>@RPEk@b}+}SM#5&#G23$u-}Z#f`|PALG8g%g_K(9O zi;!oIkE@(7bcHJ6#l zUlO(XZHeopMEfp|X*OzF-sHd-u)k8!$eDc&HW#MgO_CG+kW7^-5y^fN<>=uDjm}06fkx@Z^cYhMYghQyw>0Ied z!M24{CC1tn$8*(G%IvRps%yIy%u`JU|Bi-5svkS0Z`QpqjATo8cib}ORI&GtvDyaEUQO9YdpZ2YE(kwRNwJ74 zdA%4w2Rz8!Ldw;Q{_MG&5j@2H9u$)hOU{X%oR4Kpq5tq16SJ0AZ!w$o-rADCR`_nX z4{!?jha|>Ir3%STU4z8&HbMVSb}D~1G>b?}6m13Rxl7T55$LdOlk$KpE<4k>$=s%e z-;u7QRV%o5(_-UDqAtCJZtRZmtLknvOPRbbe9xO@3Dt|MCO3nZR@JX`l|6>AMDBXSmNFmq+b zStfR(@#bAT-x+BnRou^+Keu(zGijCAsisSI_x&!7djmd}+3A@v5SHo|aZuK!HD7AC zB==s_M4mxl+g%P8x|aQ@w@J%nKTN4-N3y6`Z&zf$h3SV0jg~7hJ+4+NOm1FAPkQ*% z?u0FKs+#_+^V`?ldE0L}9zI-Bx_^Fst@k0_(Hz7RLRG?@V|E!O^j&dgBvdol<25f~ zz-=S0>%6NoKVCz_mq{9AQ`YUn`Lq9~cJ|41-AWqMFtkLPb%G(V?&1ZL-3kwEVeZ07yRK{`ql`C{z zhzS@zwD;_v4GYa;EK_`(={A_Y_*;AvvHoc?H~2}$-J{AfTUQ^#h;+395y0>9OO0OL zJX~UY;5(czV4iI~L1rF-iI>^Gr@?1oCFQ5?lhN*=nB;~D^ro9!$pi&)6*Kl)7+fj@ z4ax|ff5D6H3!#*e-3rzev=ZwLyLphsV3S0G$n+4MiC*2~5F^@iE~d+O$RoR4<+tf#%-WPKy9f*dk;cp=DKC6VQ=j>iH6xuW@-}0S zd#jKGhU05GOoWlA_N7GOF96&k_G7(@zk!WQdSeMdsyNk38vBP$eT$Qc zP9`=QHiGQ+6M9_YWCPwGk#*8q!3eD3nvCjhCJ=H@d#q~h!2=t z4X??=%Vu+C!0sv?hvdJ0RX2|qt>dsr!f_TtUAj9m73NcPA8mN_l;%28qpX%;HgX{( zrNV#f68%0)yDvjc@y?`;G+yKjWGWOahD_ddOyH_ocYl$lOt8{B6Hq89VM}VhJ~~ZS zMWkk6i$yPCJWb{Jnr7Fcp?n{ zIKX?X`U^y2!Gf8aY3v*(ui&HFeU^@$%A zn~~1$Dnzx;wlI*|lI8`19UP`bw@s$cYQpGXXeb?5mxfVQBZ&}?>Fc3|wDI_p+fsnsEN zw=z|*fAQwikxS)7G=C8%8OGfi!X$V&R@Q^sb9ben$II3%bypP=>iRT@%-y_C%_ko@ zc}a_uaA9aa+}{hBN-7`e_O)qXB9C@qUtwUy%SIr%E^Fx;!bF*0MeE~oX{*@ZR36nQ zdFBbGva~M@$Xp;`Z+UPebAr*5@HlKk_Q1@ZW+{hw+O0>z`_943>t)7_YyElgsbY-{ z>j)JcUPIVx7KYq6;tLA&6aWYgrI7@W{*V!rTi_?5C&4+CIZ(qny8e;30|WZ=~q03NeM8?AkTc>Q?HLsXSpu@ zi5-PBBE%FgBx(q8jXB%J=s^hic^kvGoYp6KdjjyNgYsf8gq-d!uN1s7!q&&FL)(v95bad zJgE_5nQ|Y?UmisQPV8b^XLI{c8?W?Y-9xD{@te6afh!NyY@dDMNzlG&$BFWTB3guP zarM_{Mf|vt-Twopm5Z5&+M`~^izl|5H&?<>_idP8Ff8~_ewknAoNE@>!+V%4MISf2 zelE=GpyN-{7TQDs>E;>*4M9I14nvw@(V~PjCj0k#UmFKpGTh`Ig9c<-u90UCHd@^M zP(-P1{H#kkiIQAVN}yKAFPu>hR>121W0~!4TxkL297>rk33Of`?}`0ozK@GjI3)AE z=%KsD7~ z41LZ^HD}S0G{J17X8no5(1Uf$AIx?1`IIl3BLmlHP+-mnkH!NVvZ3MX3Q#BtmiNpu zfw!Ju>F3+dsC;<&#a~$n$L78=b*BrSwS?kW-V3{SHb*u~$*UqSLOx3uHWM+?KAB>( zMtu%jqsgjdh;3!~+0GlbIU8Y!r#~jFIxYL3|7%SN8zp$3yD=ic>|e7DU2haYqJi*< zO_qmLHu?tk)}m55@GA@9$^nZl_-6}zwET$^xOd8T{z9(hkaa-v=Z1?OE_DdA_*DP1 z%GOB#B2){_w9BVc6Zg}bs?2*T#a+BJ4=rvVFOU28UM$&7X~<^aG?wqjsZ!6Ds)K5n6BQbD4t!YjHNy49rQCTNb>9aJu7}qrK^+aurXHRL?N9xX65NFP2$dP zpt~8uWS9l~#&?j-*f?b`K>Qun9YTUmF%pM2h>5Evli;r2M%(+&@z1UrpPu%Zd?DEE z;b?Z68N0w;WxCO@d+PPm508efI189N6~!qnEi%RztYuX3+$ z)-4(X7^m!3;tD`Vu(CQ847t*~9r*;)N$7`-GbZj&P=UMSc=5?GU?OgGuI9HR_^`of zOvpTht5~LIQry+h`NGDiA+Hysm>wQ^_>@qgui%j~t%U8}ql66XEoJ#w=FWzcX??+& zUKIwd_O?`93L3tfX);$+e=}& znXOf^>D+~knhxtPEqH`FfTjLcF@@d+kg@t^l|hxp7q$ndVQN)F#1k8rw0ImGz{`@i zzHWl93z(R}Zz3sI#=ug@>*20P%==XT0e(UJ4+y;_1F*E5Hrx^J)I}4vwG6Of^x(75 zz&#jqUGKWbu*z$*tPuYW${yN@c-C1n?eSP_kt}1#M*8BR)}OdrGu_21tY`T;fm?nfJRpg17g&@^dFV{jM}*lQzs3H_Gh0&aM?itd%c7|FohPpHfz58=&}&`1kK}!* ziB zL8X_kukiV+?~j<=gTLiq%o#!C&8YG#YzcsA^zmOpBqPCb5v>PV{byf^kN|LAa24x9 zSsmV=$U{q5dyf15NLDK*U#VRpO0J*L;+e0crsXAx1?aU$Q=&}U;qF{U8L)^k;95)7 z++FIp&?!VpFdk>avj?zt%iSF;RXvpJun5OpsjzD)R#Ji0)_k>_m|BxDvCaVHk&E_~ z>+$ZDq`1-xk5s9us z&$+V7&cIX?Jjza?@Re`>h-RLiO8htzY_RnZ9I;f`ezM*M0!742GyBI$Ph_W@uo%Vp zbK69K0j8hKnIl5U=b6F)sQlPFe7{L=;uozHI)g{Zjtet-IsV`!RiSZ_=T5n?5N#9tP&Kl1H@NEN)Se@}FFT zn;&Sp(8TU$tTT(28dpaIj%IV7gsANN7Q!wGQp2}zcT${fxn@cWW|hiaA?THAed7*An72<YhhW!lM{Rr1 z)gF_jK~#$>scE^ht-gwV0PPWr47Z3Ac(@Fzuvx*+c0FiyEahO4<52eyVeSRS_BjK+ zFRmzqBTI!&`tPy_6LV(*7)-z#Nj#!sH*cWBwT= z?&_9F{7kWrl~EHmM0vHDh+zv~_SI#49@*!M6H*dsXEYvO#R13*H<-8pQWi+5Ll=KH z6eyulhWv0OXuz}3$5ivWBfA?Ok27L^@Af4S0-)m+#wWbQv!G{RxE}p%PR8E{rWVUXt5&=9n^m zO*LXmu+eF^5++}~7}r&&0x%!yqIZt?Q5dQO;1l9TNa+PwX8wr?eI2Alk&;2xo!vyx z!hOx-to?b_LzTuhSnlG`V2rg{0I-6yb}EhZ=!UvaIw{S^5`@LZPi&ATbxV`DfITy| z8->Z!qDg}>_eN*EL(g!JE<-8TS(DVI>1Q=tF=$A)Q&<%?Gx zKME~btfO*6=`gXki&29BkQD(EnL_3acy)6(?4aKESzHOX#UySy^e2L{vTLST+E6x& ze|X+-Hvonf7QO!iKo#X!ak0)HAgl1+{|A`r?b);uYG&}$y&Rum>fpK6!&Pg9(!e*9 z>h5;ZH@I`Slij9o9bSonq};`|@;t^uL3&q~pXe<|#vh7bgPE_!+g%R%uKT?4ATYEL zVE8-RaSw(a`R>Zx=@4dY%6VY=XPvTHgJE$5nU;-w+~|>uK%OCz+~Ub;?awOVF!UfDfyK-rV)BN)RjFd zFD3qB2W=&(JTD|L-4~_?pRp*)SR>^tlG|}na<3bOTjvSW)k+t~XU-f@Ey@9@3>3yJ zAkbifdU>@ljwuiDG7^H3Siv_wD<38$EB{CNm9D&6YW7zpc_Yx$Je6b1?>cj>Low6o zT&$!~22=H;@oj6$;N#%+#-wrR5KxjwRLQX581m>F`=Qh<3$og~PdIW_p=M`iP`D}P zvxc8HAlL-{Hjwk%F`aMuG`<`ZWP&9~~N!l&hkYcg2bI+iL#^&$s zi{86$Ykv1yW4^fNQLz%+TWO zfdhsU3Z2*eIxxalZyohj{;K}CHpK^3jf_CI{M~doop80WD4O>BY8d@RNkDq9hSIuc znUT_xgEKK;|QS z6-2G+X5NDf&ISJoo;sHGyX!3d7<^X&;Nq@c7@P}V0APYX&Ub!f1=iqZ4IP}G%Stx% zmGFp{?Y6~RP-TxK~%0CPuTzj-9)2v(H8TL2+`}mW5@%N`K-cw$y5cd zfg#UxLNGF`q|4z#k~TkvWOnwXmLbPWy3v`!p6;`=qN#mb^6n`z;_Nr|#6&fN>mLO4 z%A@zb`f<4G{qf(-Y_3m0pisBodgMLWQCFZ=8TUm#_Y;1W_EJF?mM=~@O_U~md560K z)~@QL<%^ie{Gh`mNcvv~oT_JpqrTkWa!{as3z4Kypyp8l|NBscEtB}$^2&#Ccx-{? zW2vDX6nA1?v<_^8IZoRHmN8{3n=*1$ zf^PoiH{)qt%ztEU8Fx8%*|u43jPBo(!su*&Y!aaAd!sRAa%GaHmT2yb-*tqU<1YCZV_{_oAVSXLB&_ zcEuICYm`m?Ld{}Fy)lwm^g}y+nKY{ZmomB}gh!_V?{(-;B!%1bcDz*ejx>3x^Uouz zK|y6W@yd1P`T8A_2{I7b{1)TI$8TEPlkGvUYf8GtN8xZGC03vM1Vf~MXeOglNyI_$ zej;t5{2>0G~02q<2M*F#u=W*)I0ibFg_cA^6 zOXu#p7FOfRZ#`lW{9p{+?5#|`CliT8qOPiz!a=OqjhmUkCCJe|P=np>!y6Jt?yPs~ zP9U${rIs9XJnf9SzX0?^e25WZ^rn}RfY}=pbF1qldn-w%<)G47p1W9Cp)};S9Jchg zh0e%7707rueO)q@RFSDd2z(0wGy6#Jv%F#*2zGv+&RnTefsMx zI5AOEbDlWY(DgA!B-Ay>Z3~!hd711+7b>i1J|9>BTq?L9E69>9dzUR-eRsS3JVk`PW6?Qc&2|etBQ83_X#4XiXYxjHnkcA+QoeQSGq>UL0SMM0= zc0%M$Jy!n%&^n~wbKY>(ra-_Nqjx+7myr_ka_<&Uv7&(n4_0OK+lw{58ic!v1pF?p z4T?VJWwFmqtd(mQ<$c>L{b9Ekb7;>fs6e~`bQ@G>hv!#UxiLaxiYZ8r_)R)4OVq`x zqF(N`8IU{-reqo}eZCx1HS=V7AdM_J=S#Xh%Z<~v$)LWl22uXyXq5}4!*uuzz*w4P z-Q06e%z!cPa*A}B7Ui6&evU>ew?S&4_t|ma=aVn6%mgWhAGrGO4(@nTjzqF8Bt9l* zSdjgwpji)`9M?#8bw_UMW;dmHOT|Wo z5Q?_7zW5JNKh$@ziT* zE=!!FV@_PYq)m<5$G^@?Zt}pE>-y-jE!W9LKm35i!?JEosePLFM&lI`V|4nWd-)?N3zt$Nd(Q|;m!+!Ah8 zw>Oq$*lkdVmYQ!!29O-Y(^EO`G0^1WTQT###Ml|ESnmJtGQg94!gjDYAse@KU5?_j z8Xep;c@_~HENr&AGxMX*{Qk&123u5SM7D~o#(C(I)|s)v)%tCV6BLE``H8U#@(CYF zB4Hm+5*o*={S@si5g+M?A=696j&JDx2_A6Zcu)Q$P2Z?R41+Pi<+(pz$Jr$BiBb*t zLCJ10W0@C8g8qW-ote6~x?i0t7x*+wgDCS?t9V`j0G}*c{Qp$dGE?`H>#DoIywYaj zot|rn9w!q6zWRaY*86gZ8o4c$e!&>=jG= zrSP)vCNyuX?^`Z&ousA+d6w>$>PW4W(q+^&0-m3ZBg0N&Tl+cg}#+R-sS#0QqM<-1yyLWMLdE5 zr!&tCJurNIujrU~hHj}y$)8PQQeqRDr<$4C+88~;9m!Oh2JLPkkFti`-TndzvW-rp z!E)s<+Ekk0sLSb!*EqCfd#sP}+VUgw7B`QLUd{*c-gk~4DZ8y>E>)3$KUBuJNyTa( zE4&L&xJ+Gy$MX*RpR`6ME}~ZFsaWf7avwAtXJQ%e{sSDv@7N+=48AaG)*kT=Tgkku zDnBL|Q2a=`fE0gl@5sn8b_wNkoTjBZBo0kWBW{t2E;s z&#dMnl6eA+syTDTm>ES7_gAhmSI1)r!&lPYvKJ1P=mBZ2JUyp{KNstzeo+V3Q4=B> zKoifOuPqVWXy(#q)HoPH?532g(8-=)B8;hc^0z#Tp3@(ynMAkV;UCxf8M$yl>+}re z^)qxc$qwV4;q4pg08;T5Py|8H=!|ZR0nu>wt(!}r*G=3n^kXX!7BknV`9RUVfi`Vh z86xQjKdzo^Kc@L0x1xdvGO%Ac_a6Nn9oIU!ekW#-9~07`NdDmPZ(UMr;6m+n1?P*v z>UGSnhBaZUexiW@%2gnc=#_Q1ZDB_L+0>A&w98$RY{@tYQOb6N7gw6*X zQ~{iJ$KMyTc5BIYyp`DWaJ8x6rpdq@z!%P3v^(ey=1Ww}S#OJdP##h)JSdMg5GgrA zn&p@}x?kQ4I~jShe%8=u!rOG)pZ|5L_4*H>#6XWmOwDVv;+VHsrBd9C%uackW@M!x3oTH#FRU7| zRjpZbc7I4O=}Q<)YYwk=-A#-rLecz^O?8h>t_RX?a`o(OC}v*%IoRy|4>0;ek6waO zW#GuBylin-wAX_vNe*Lfy7X)uz8IQ*EmDpw^)$U7YkoCk#_<}fo-D0fDN^Z!>9kYynwa9p$O)BG2GB%-NGTFgyq}5QNCog+pRH%~B@4PLo zOQ>it$oBGvD7(1hIOe-E(0q&1xK9K{YPt+zB()%@AS>tFwR!}BcCQ2OAI`aP6!|3V zZ$?*+q!>Rr$MJXVPF(qk$LmXvml+*>X>sze&EC1}Yys$r$5lpccz?``(o%(dG=}?ur;-^|^`RIs7VCQfDJ?Z%Pbp-Dq)Onky1= zd6V;`=Xc&6dJ2@F-gzAb?d{v?OwY`JtutxQ@crCznot{_qlzve^C|2M|yfTCQx!YBmV%` zO)wTfK72-Tx}hP69eSF!+cy36=rM|_a%6wI`BITl92I_F-w(V8PtuY=Mt)!p{{Tur zK3}}q&$TOa3w+P%B0yY<@ARkJt=DNoq&NCk%_NwnB zgx#J00P@6vpGt{TRxU8RJVVFohg7&=tVYra9=PZA;=GSd$=5;_Z}|Hkck5okmlq9l6*WA1FW5>s995 z*D3_VcHH*u*XduOtr7XQw>ErD91jGtBfwW^#s@jBFGj)F7laA6m9^_Ne=w6KGm6cD+wK-_U(T%mw1Eyw~2V$I>Z$AD106KXL z6mZxhibMx?c>B~aK%|KeGkxBDKmB!MsxA}1b4(cEe7$Md9;=M@sbvm7>d#NT-hf6( z2`An>=9&N@f#`iHKm!9KIqyhfeBqm?-R7RH^YRaFwJzU~5=Y(dNEJ-n5IS_E1mo@l zj^33$xIT1;1CDAHz)%SO5rNcyja`6~YjyWHPfvQ5S|Gh-@}&94-L6)4K5P8Lsqb4? zq;8WEH%xO}^z3mU&pA1+8tn9N@hLeb;l5G#t7^$gfO2aZFk0;fTdj0)DhpmS!hTiH zO=@)2dKuc~%0=OQS&M*v#(UT2SH*ow8U7|sZ}!GucW3KgYPL-dP~_$NA}6bR=TzdzQgC2zPrPD#IcBKmZV?1Aux1>qV%EZAmb< z%*1ot1KZY;=(}nfcF6z(sU3%Ub{A5;tDo&#^l6DdXJi4712w47s#{-SatI%VVt_`p zaT2y}vRLtud)5B{I!Cm)cUS)aU$B63G3)wK1}UPt74u~xL-%+izqzZhF-r?o^I^c1 z@q#iwznuvbUuCyo6z_{aceO^vR#a2BKWPh$bqAcErB=kZakK0tG3Juc9n5o{4?{ya z`!p;c=ko_a*V?8~t$A?AZx-VN0~!AS>r_)fk=p42ZgN)9iDo2{aydTqGzp4+(*7U5 zlvr&Geu_ZqcMw0WYQie5&YXp$JEMLv!tLy8y}K}7rI>?q7^y!gKT6S&mEL2y6aqjP z=RN-bI%yLpV?O3J3J5Abe$*;N@i1WQME!Hn)N{BSRE&P~=hPo+mJP{`>mbVlMk!lT zx)U;$g4-xCs@qTXs3eg6mT-Y{jMe#>Ue*vCaCj#?`&AiZL1PF7H+f@|+*4%5lA(66 z<)=AeoPo#J9qAxQ#P`l$Vhgp(s_~LBL_T4H{e*mdpaa_k`c&u#lPKAp*9u!bvBd~Y zmfXP5NUIxyM_iCc(z9e24Xi|}Q0+J;I6Qr7*8E>CSj)y2{zbQ+FjR z0Zh*(LJ#zn@5kn9wZUOK?LA+OwQqc4O(6Z5WDOH;Q`?+os@$LSt2Wq*rsE6%&IizQ z%_ca61g@oGcE1wjK7$RVEs_iC{i8Nkjn&ML;pRTm5Y04-Fh2RHx@K8B{; z8p_lDzTfZyorQ9Qu$H#Y;0;g=si`8neCFXt%DiLiS|$Gg({2HAj@4F3!%dN%2c;<^ zNWSHXUB1sjv8;T7}4U=ODir)Gofir#dY+%h{;j^xQ%7UyL; zMZhLN;v@@<9OwT4tyPs>VpFw7M_l^))rnpsVzH9LbfakpBrx=<>}USVl}bh(O8fOZ zAM=`{IFGn!+Iy8gZhXcl?UG0x4N?S;HylEofG5QL`Z*EkIRv=L0)CQ^7k3!RUVql~f2*q3igHR=HQO%S6rH>vOd& zPV5eu=}wUxOB!z>foA+F#isCXht5M>C(r?(&ZLHnf68A zS@5rgmudRd$s>@&_&oNlD;S|)G;ED`9&=ebrQNJD$UfS@0Dj_vbB})1X%rE>-I1#D z+b)-A#z0vuwzBuj}7ripKK1o7aW#Bp4%r>vhoO^nAU6~|m%+(|6N+_+Ll1lOI2 z$EfI!PYahhYR-o9RF$p}$0G*v$R%5^*0ApH?xc;C=VOEG(y=6*0@##iKb=!+hTA9I zKQBL8@}-EBy6$__a4}sCC!6IQj+Fz>27i2cJpu1oW>;c=-Tt)L)^yEM|R6~XdBVP+@GzUeIU6pf#l2h3Ta!9A#xk1Z}6`| z{?9|7+~i`bNv0u{kCvZw@rpf~1&T)y8H8NNA?eze%sbQlXWjKUuCescSpNW{sNnYd z-v0oFXU$OiS*W{SL7l(Gzetm>!psp7eH$l6HTk(1xvi7h1=!aujts~D~o zh%o-0zY3SkihQy(mgDC8&yM`}_V%mM%gPmX>zwzlbeCLwvuAfW9jaL6BOknMV?5JU z8Zw;(&gE~Rcu?+d^#Fd9t1&kbuwn|ZU{v#wT#8KJCJsJo(SqPA0Cw}&n(2lmoHN4M z>%;Ft>ZlGzMFht)5!1j=ARiW8}r(l6D1?&lkWAVmi^fr^`?gBr+QKsEOYz=ZtkH@TUT&94>gpD~`ikcEEOw;+QuX z8w^Jr)W~rxfZ(Y^)2?a`Jjdydha;^ap;#7;zHy!6tVjo$CL_ih1bd2&0~pYP{{Vc7 zb7Ub0^%Mz^@;7tAKGco(bGY%EdjaLHWK;6zng-u0OdRdU<=%pXN6bz#`NvRt)Pp`v zy9pR&K4tVY&{y|tv5|)v9+dY7&u^8sk|;n+DyWl;<7lYJQp$X_`m%37+h^wK;ZSJLR3uu0K4GubDos;a?r>d8Og{0 z09va?Po2vQZ9IyRqS0aJjyn<0r7#dAaftbp;P<8icQ+09ze++$P&S^tRE(IG^FHfy zpVpTQC0r={={{j_nZe@+AFe7!IG7NPjC3?GB+u>?5!`jDqxp87>%iv*pd=C3zpXg` z0I0^|Ip-ern-z^m^5)UXkaLa?f5N-30c#H({oRz(uH>9;EPAduuRpV4n1LsOOE8|> z#X5D|cXMqQAh7LSSS&=SR8maxa||M{O~vof`r^{zS~OvTGCx|_`!r$nl`%2-3 zxY`NrUXmXZ5It$Kz>i^gOY@rak>$BF!!;c_6(q&6@Aa-a>e@wi#3$V1z3Wl1`LX3G zEu2@K>b6WIoOwzJYR;uTWOP!iW6c?MvEbB^y9qOs*PmS0jobuDA@Tsko|Tr$Lm!un z^P1z7Y<9*;qk7VFy+3*0y2~4^O$J=>YtJHV75diQwBz?cw|*+&D;U&;&swv)%5gZ( z!Cg!ftLagkjEH%!C$YK5lk<+kyDLkRTTE^q=CpEtz95yQ?z76 zKRU&?aMni*HwlO24Qm6KT<$!Pk?F;1j9LK9OMLP4>6(f18<(U^i{cLM2^BP|3Hy!r zb3h`v8FA0?Vyz4W##^OD8Xq=3KZ>j}jJM1DQ~?Oxzak(;XqlO!QM7|jQdGA+O(TG1 ziw)Rjfr}mmxcQE8njOf7JO$-*#XXRS#vJ|MT7!=)0^h?x2K>TuTP2Mj^2RU`bH5a; zvoZXfZ|CX$H2u-8p$Qv`fu2S=#Q*>@oD?7x>P1Mn5*)hJa?9pCScn1zAPh>9+-KH+ z9_RPR=0A->#kVg9txDtNQ<2i6`M+Em27qreq1(V6{VK)=(bG8XQ3ZXBcOTBAMP?ky z&*?zMkTBhT?Li_bMjLVsSt>-p612h&PfnB!{%rvv-&2Tv`Dz~gtQ^r)K!*+2ki91NaE zH1q&U`-zZ?CDaS@EdE=)}{0_WQ5B1!6U~VHjxyPsB zQ)3PHSD3I0j&geE+Nl8w_sg*bI&;W8_U%#>O_CDd#Bt3?0N8)l5A<=2Q-Xp>I0pwG zTygkS0bG(JJgFGtBb<6t$BZ}1;NiH>T979wzFQud$rvZtV~@Il$(XislRu zenNR_0 zImidvffKe8)l}dd9y8NDk7}yJIV7nC!6QG?pOR#7T1=JCwm%ApAz7sP0N{4wfE0pc zA2Z_zulY3KRIuTG=S=*$$o^far>=0lhaQxpX)A)gI)m1jtVkH{Lyk8eZuF$FjYiCT z=clOrX`_NN{WFZ7{QFZE%-%R<0XWSelH&+Tbw4O4aOXc?Y6C9DB3VDzZ6`eE=~41V zOp)V%`RV=?&;gQ6D;!JJNXOJ<4k!^ZV0Pt)3*7pjZ|hCpx>14iXQ|-l>q(!K44%K& zIO3avBpk0ksx$h7K!=7Rb`7_ur}LmP`G9kPx#V+!>E56vyvIAncelS3Z~yE?bR(h>fqv1Y}_2-`<_MHowjfC$2G{ek!47^OyZ1Q^r7J&flkBuca%03B!-w z`gNd0?#<;f0e^&?AI6g^V&=()Ng(z8D!RALag}WOz3F_#4?+1LJAj{MROx_Li%(*ck7py!6|OO@DuQGwKv*Yl== z!tu$+@d5f$_8;R++;a)uaAh4ao@u*}IdmTvU0qR=70crZ^n*<^`!3S@S~D@b>w1zLJ~Lq!`IS~=XX<1 z$2~g!bR1_HAC&_e^6qT6CO#?H!TZCdA;*|KLF3w--4}-gze)gM&-af?0OWFd=ZX*h z$?HZ2(g+;-iU3@E-{V08&{7lgaLdrr{{YJ|!Rbr`_?M3K9=#1GJD29~N$brZ6obbv zjr|Wy({gu9#~yjd$d9Vxu;Jf|!>5A%vYgm6CS zrR?8c;(#1+>(ri5y(7Xic|itpy^Ekx33HL zso&kn7~9-X00+16Q-Eya<@KOrkFR4^5l-+k(>+Ie02K}pY+jXiIQ~?~S|U%JAKjnA zu135JhWq0`L)6r^G>0PXQTKg+&MLqJq#r5|-t*~-kDcrCAHCY8R$Q<9hm3l4tmMi< z@tki{&!#9la&fwt*z+5K)21mhKs*kHq^k*@PY;i3aVG$VJvqF*r>8H+5YW2BLlBW0B{3wBR_tc+kkGJb4}cM8&{lX6!v8>k%OI> z%>o*veB6>1(D9D+fSs+LXc*Dvh`|g; zCxSZX)}}(DA;KT6At1y?bNj)LY2Pl+7e7h>JNN5L#(Mr$9#75k<0mw3InGbrpb3;^ zyY=r#V+G0H-*=#go5k9{As7|INaSR z5YLzpb~ZNmsT7m>^(K`4!JK`;O~Km338< zY207$AHYxq%Y!_;4nRMRJ8JG9Df{Q{ifZvJJf6c8;?1}nr_-Nm05*;6e|f}Xsii-| z$6-mkBd>nF=s6&K!~EHo3SAIdLEnq{pK6b&{3(GTDqV(u zh|-MiIK@JMhJ613dyZ<#08{S{lpq|q+DXq!r+C0y$&$Zvu7UG?)6inE^=I3r-JFgf zJ!>39`C@BEH)qIyv^J$2KMj0ho@*W2Tdo27++!a@-o7=~qn6*`FM%yLuxU|RL7t_C zbNOut>0a~uL*I#Y&kfqF>{?$CMe=8d0f)+aoQ&6l_|?eP{3~j*@RqIusOiWV#bbbr zzc+HRlG-Lk(n}wLbfM;b?30c%b5`_bNNjE;Hu4v1c>quVabvRkfJ6a!f;1Ub5|_UGhy>4<8q@c0LM;ip=lGHYa~tLWhwA~!>Q$>o1Yx1a8&MX zt03U?;2uY=4QfIAzYb~WN^KU(P%84OKtHBUWB8guH;n!m+Q%g32rnd%sX5&vFa)2V z{${mKe2Bw+pbsiAaz;i^PKS=2{{R~J%;HyndG@(N(H^JaIKp@fca}Fxddv=br)u0I z4BX@9I2Bh#j79MG!%9?wH1+zE-2QcUg6eXA`qlYr=(Jfy-w-&lR+H zGh>-lP5=*$*{KtE&y+uZj8N))k@F1TcIWF+sc$W^Ph4j;svw_eCnJ6^C@YPp2bJz= zv9ZAU{c-E~)10vlm)wMiV%B-b8cZKgaD z7e9qKyRds6cjJqT2TeW+lwo9Jjl>hvjw>6%c9sXnDKt>1^6vM6o`iJ#s=tj5t;fV0 zKrb+$Ddg8<;GH_&EjPvYA858N)1ya{H*Lo_{#dVaZ<2xK#W_~F-VjW<1C6Bhr-pAY zZqxT_0N=a-9&wC&Q=4!p@(#ziuNLQ|J(u?{@}I`5E<+A8$>TVyM6cNkVDf_}9Zgmn zjDrC3na{OLDH0(+X+g1m?{ocX(bz5dAY>eR@m0{^q4{!GwK=k7`@@CDUfz_@nHl@X z82jhGYSp)tZzJAR4ZZ3htGB5fcEv_U9!#D#AEgP|gVfNvcz)0}g#ZjyK1+4uKdnQO zS#ntSt3zpI=lJ{6zcUrAXxWtVToW1jaf9Bcl@*ck6}aU6(SSaJvxzGdWlDYSnHi^j zq}oT#BAu8e{VB?D?sksVDe6~3bCHAc;IFL(xBx#~=94VY{{X9T z`@iB98js&6$iH;+T3mtYk#M;KuldbcmGY_c9zX{fsK^D>0&||kW|fo%Lzc$yYL>*k zNP0g`^rI!5bN9F)^X@4dZjJN$7mj;v20<{7RKp$}|3!MjMW`?;a61 z1Hdt~i4w7hU@vO<^6OH9e~Wk6hGG|03J%kT_Z9Spf&uVGn0&slgVwz~c94oVu*&mh zp<+i)Jv&pvl7r<_$7+SX{tDx-IsX9bQ^PQ2>(F(tAv4frS+wmUORUMG+q-#PNUtyW ziF0XX;Z=$qFeqz)+YwMufQWqFmAQB!YSad zLU3|@`q!vVNA`8|Hr#mtWcCzeV^!3W>IYsb7r{gpSxyM=3rN#D_1pI+7P9uR`gFNYSZ0+6f7 zEy>P3f6rR##y(mp7|>U!*|Vukk$7(LoTwWJ&THoJs_9ewM34yB?$p=}0ztv+^fmN8 zt!(!?4ct4X!$bTh-|61Gy3fGS$Kn*6MWMtMgaPw~V+}0IUtaP_hc{)YZxENUZ=Df-LX{R3-UO-E;E#}SMX8;a4%-)I_~HusRTvTXny{N9HZ1K&MsS`=Xv`SfH| zoSR&`9_^>jw;CP1!ALC@Msu8h_3NJToR+KLTeOnk1Q3=ye^0G+HZcfvJsLgFkiRy2 z_O1`&Fpc~#vmgevU`JvOKT69Eld7y;Tc{p);n?(PJWwNx%VuE5kMORl`@rx|sI28C z4UzJ&zy~AWt#UsP^rT;icGC$LYQoEd(*%w<`d892ISk+`W6U_f?OoW+OsUGzXDtd! zYgBOQeWU2U6SK8xCJ&VRtHuXh^TlmxI<=+auaz=B;G~4k@2{upUUlO=9`5hNcap12 z=K#mF@D4hjI$-^4Yru9l7CKezlbCRGhV@a`8Rr@1xvEo)p?lr4Sjom#=z1%U-^qjA z)QC9;1E@VZRRMWpCD03dZg@EvtpoD8U-o(8xEx$}I+IUh$-X2m`#8q&#{gs8R~_N4 zK54YANj%G-L$4bS2|KvYT-LY5h-YKSlo$VPH{SF@Lhq5l9zjNtRS znhapE5U4pFiOpv$Y^0jD=8FuIOqqcOLN{PW2+s#N=Dun8g9Y5aJ#DwQiUh>%S2!o> z(!P>n{{UgSZz;#_+X03v<=+`zYG>kD3k)PJ?g-nS^`>3%vTt7D7CXd24&hF*B zocq_IGP${aM_lArE8vqS{2?}llkZq4!S}AfZr=X@J97MwQCzsUdW_bFkU$0_I0v?J zDnI#b-z z3y}fA>(eyl&pmq6@Hc(qjCQ0s8Tm)$LPEHD{Ku^~i~&F=Z$Z|80na9Y6Zl&I=XEaA zuoAsJ~6vuCBTs5^RG_u zRirR@asjv&`z{C9HSE_)G!i`ea^|Yp+VCz9`-(^Z0ACeiGNIh@pDgtq>N32y9ax{o z)_^~AKL~Jh&rf>t64dWJSI4lx`qY`dvs~Q|zbGF}b6ejJ?c#?@IF>WHffzqp;=Frp zG@c+(Ad5e4Rs?n+B**e z-%i$UHN_D@b%S--99KRxoq05P)HV3|FmO$KFtn-iws6TcE2G;JR??lyjaJX5KJ`!i zqo#w8?JHC1^H+oE+J3hj_l1t4-yy(k{{Tv9web)9Br!3IQwk*XIIMoB59Tl1OR?{b zscGO0+}i*FtUnN3MR(zuB7x&}Xz_r3YshtLUlBc`Y`E1Ik;0M>=Uv}{EpL*}FSJc~ z$Nj$5Gou=7nLF4cIZ4^0mC}4as>kD)sVIAxwA-8Q8<4V*lHI`d9mgL$ZTyDiQug(P_~}pTY(~=Zal$`O?tuG zgbflOp0GSeE3bwfVDbefdw)6cbr||r zg4!*M72uE*NA zp98+3CbUeF$Z;-tI34KJbuCs)ONKgdmA=k)qh|;7tv>@zG@6NNbokJj2LNS3{Dp6C zXGyKh)90PfYf?ssfWPwVjMswr%lmz`=~4;rhHnAFHZr~c01EfOL53l;V8zgJR9??Z zdEb7SZp57AuQlgUt;s9c?5RtgSmOK?m(ggp#&X%)-1Ca>%cq#fbbStRYk~1i?ah_6 zg4W6a266dU6KUcf5X+{k7+id(s2x8l-O+V@$L72}>TywoGr z93*H*nfyQzSn>FmUH;!_{?9(&B^e4a7VyIorg;k(R% z%pR4=U-*YbEg*dNsNU*{@fylBwXrJKq%mM}z4+rFnXhxujIH6#J)L3PZ4|aR{VT<0 zpHKe)g_jyWl^Z#@Y#wo*y=&dHf~Uh#ZHNKnF926gE&lBzDjyRsAG}TnP-;aSgO>H? zm{s{Cx8BcMRA3MTp0(yCc8zBF2nD_P>p?sNxMzJ2wn1 z-xDPa|S2!szeVtqV*253HX^I9Lk=HbHwX(w{{V5lRBRmzaH684dFrIiHFv%m1+3)qP z8^;pH7Ufz9zywb_DlI(pY5;yo_r_8B%w2_;C{cF7BQc;q*jaauG z4?OU9_Ir4i@uPuP6E1$g{d)HWDfTFb{W9%dX`|?K>E-5;AzZ$4r;pCJ+gj5V)jrsu zgMrU_+Pmj>TA5MzR;NpSvuy3BL%!erK!(D zD99(&4F0&RtzIrRwR^#q*0I{f<<5k0ji-!OFO8uOadcJr6Oqr?+O+%)8%L%_ z43Q8v{d$_|Z}%n%e(?1v0o*@!*z(6(MB)DKa7`&?5f8ohX{gQT50P=@@ICpjDDSc5 z{yB~r47-{k@(?!)&hRC?5#F{M5N1D*yy zD(rkOm^?dXlUzx?7m)SU{ifWbAGJlp9^G=|uTtuM71X4%ixzRZ`M?l*kIS0&BXo+c&y$sYjTzz zClTPiGHWdc*(9@-NZbIy;0o@J3%AUF3VS|rmggAf{AvUD&rEaBRuw99yR&FRl~^(J z9$WLIzyid$Hvi$ zw8>~wjGjMAjE*1WJgKP;a(+|yk3&oZ`WhMVJSO~xvf2Ue{F;*Od^7_y=m27#L+1{fd zh9Jtncy$!^4ar2mz9-8+omnpv+7erxwMjuIIT<}UqaP{{m}fcWnqqN7TbU2cfA6le zzr=n{{{WRC04X~Y?{mdFBjr3}H3bas_fx5)?0#d^_ouctw*!VfDT5n_2d||jG#D6e zy(tcMo)@h+;Dlkn+IDs~)`(w00H8Q=F!J%eq|c*h)5 zcR$jdaSJb*fgUr|3T_$+NY79g8aiE6t z9B$~{ss=C5TE(kWN=^Qigqv5!p;b*JO6Qp+PZEn71P#!hOKnEeZ0xJ?Cr&60L z0o*`V>;6qnw~TCIM!A%PKQAQl^cg(VUQm5*&Ah^+%f{lTKaZgFuhDOz`K#Q0^fA7j z1_9wt?4GsA>1iMQCs7Q>2z-xTI`Qf0U8U?75b2i~kw`uB$gU2-E5^lf{~lf$;^T{HGFGw6F%9|J;<+5^Sd$lYlR4EwPIk9-=ht|i2N3%709Zz+y( z)CymMO*#FbykVP*Mk{S>rMh5;esT5P`d0=!WOrb7x%xSxaUHiOgdC50^lLNwwBY^9 z^L-4TvgyY^^2Ar7+LN{(K>2wU&rIu)yO3epS^12EPyll#7;uz;wmP2u>2MBuel#&6 zNdExV+H9Ovey zzuw~`afRvJ0W0RB5aaUu?*}u8X zQ;+^W7v4ZIliZ|X_3a7MZN(C$>$ZhVl%0yOa|}Pvf}}4 zE&fsWK*z7vx-B|1D2HnF^skLt&d0k_S~Frp(-P!(=h)Da=&B z#|i$U^{C@5zlJ}1pL)-!w?g}|-98j^5|8mT*5pI9AOTKu)|ru&>)NG6&A{M~l%MnU ztq9m7Op}t|cji_+9%;@1p7pn$P!2{19jR5J1xbYo`#Buu*~_NF0@ zZcVxbR6A7d$;LfuXh}C3aAlYVjj_1nk?U6Gjx!)-m~A#>jlTZ1HMoVZv_^Y3ZLu(5 z8;@_N;Z~wUo`p-O9V9Fr56TWf>Hw(i%uN>GE;2$AK^3`q7#8T(IgEK8w}DYP(tngUrMcXB*FO<5fJJAG~@^EA;S!LMB$IO6*Q%HATr^w zI(0w&VN5nm zK_L=6Y9YR2r01nrAL~cTqxZe(iy&Lba~|c%$N*&V+dhV!a!|<=C~SMxBtz7%c^qGB zgpVjQ-8olM3BdmVK9trj$8KB}I0OU7OoQp4@@hh;(?Z`qRB^>zm=S4e!r8X7e8e6B zUs_fhm53EwdHb?3_yf|V3Jf-ZppY?7;x!{}`vQhzmVLm1#Y6L%?DEoQZUZ0cX_I@6 zjZV!)Ln$pH6$J7?H7bHGE|X&^MGRB}&U@8hPuk?b87w}gv}9<7wbQZrhSgF=PCxqe zAZBZsKX`xXpv^=b4&^>(^4xLkLC4mmEJ94dQ?SA38TFykA$e?ezYs3c4}87zLaIlI%`>`qgYoC%1g`LUHOkQPgOXZ!BNi z2H*arvQI*It4v-sgBZg{nu_K;+eE=m?Ndq(EX^oaV_{x;r6b?R> zuM!8)Cm>2$7p4z2p7=@SjS+G7WFYqM*0iB4d^vc_GlHScJLj)Tq=h3t_eETJF@u~Q zDXX}%s8QF7e0v%cW)g1CHDWI&0hFGrP7&SLiBuE$5Zv3lSMn$*wL}B#&Y-j0Twjk%lEXko2t^OB<<~h)Dn~-nXoD zs8-2K#0wl8*GU9%Bq*x_jw!|mJ#1Qzh{3bcVN$!nI6cK~$8T(q0JhQn?!vF4o#1&V zuccgDa!T=zMtH7>(3H9!WlUS@%+yc2h=BS1D&a@+WBt!;=M}9TnG;)*GBet?O}rz^ zA_3C7>1ECDp~X`fXSvEOE>%fJz{eP+Sf=wFd1r>lO6v`)Gbs>YZuOyUm(FlwUu^WP zzq3sKx^muBxO~Llbko)u3?S_zoL5k7uw*7N^aJ@*mKc13Hi5=@^`-rqhxNmm5zMhS z_|hzFRQ#j(x*F*Px_OQJ*2Xx+A7)S_~COD~fn_w?|wt9%r9TX8uAJw+4(d*cCubXO%sx7UBt1M0U5_Lpvn;<=EB%7_GA;v{>5l*ErQ~SM7wOqHEgvSg*vW(`ssa7~@&^H8t%m*i) z^&>H70Ce*6n$Cfj`$`=7cru=~rykKXPFukczeDY*BJu+&wCqU-|llY2vKi>WvQV>BrHa8x$-ly-L3863t$N+8~=@)hw z?kCh|rCDT8vvfUr6IIs=WaRpEp@L!ne9%J?&#g>b<{xzPQIfa=Y5Smp2lb^qMbz{i zXiOR;4Y*?>qmwz?fxyKqU+WJ59E@U|>UO!nKRrFgu^-LlFUo@r)CzHI+w@%Ib5n&= zW1O7#=BbiH50K|Hni_W%3!DJ*^e5Z@0M}C@=XfRg9S=cGjI!M>Q-c>d^{HYCW+NmW zjC%2!5)%+^Sk7Ir9CA%H*c1bdb~&fynVAfE`3oy{0CuPVLnu?`f>dK8jD2abL^lj5 zAmG1HdS;|wEn{pe^HC_l$wxoD?^82J<;R2vC#E_2;*uK>Ba4tI?0H5lFJ9CtaUsXK5PzVQ5M@sZ~?Nyl19WP^8>a=zUuf90Nc41_ot>xy%b z6#UA5QPbX|%7wwl*BJci0d+>}IUncyqaz-*U+42%mdJiNsp540&4dC&ZXty+w`GpO z$S0?IOheR?S0IilTXMeb%lB##yL$EJr*2_UyVI>hK$#?N1i*z{W3_9(*>pEj$39WU zeQO35UF-;6N&42Uxgk&o&Rf=}V>*#KuK;Ru+W3NNIbK46a=i0jMroHxB#|}_3UTdU zB23f4t?Bm>#SVvUI*T!l@?5)LCO*{>?=gc^E@k6vSpTIjl|}?+f%X)4Ztud z=w#bMteb=~V$Bwny=F zHN?|LWz$DNX>h}48@TITg@w~*98P+3UT+hDE_Z%)y<>1eAHE#`09LS-j(NcKojP*b zmALbi0>E=!518L*eXa8DBOglh8!MydKt!kSde>uXbd0d(Wf@R8u9(W_B;a(wl1u!# z$tT{dXE8>5h{zyztoU9bsq5GBC`ny!qvFcw2DYL3QbbU z`&q*G#aS}C!k8nkJa(W2aHdHx4mX@p5(98EpK4>X7$zVwJNwg>nPXFf_c@>m-)Jl{ zdJ$C7LA#tC+$lY27G1{YbY%d3;13#V= zS-$X)oFLtUp55u{{G)F?fHAmn^%?zfNT}rywbPH>U1@b^v~~(oP30{kQG0?BVZi+_Miom z{c!m(26}a-hZ!Twlc?#8Q_BMqeB(Xy?beuADEK{(6a|M8+rII}?wO{RJM;Ep2TX+& z+#TO|WDpK}dsEd^oEarQFX2F}Dz+8RI2kMe;~Y~#RZ;Rw!6ytm z`uC|euGJ;emM0sBPWk-mzSQ53Hc!2ZKuCl%^5Qu%$FTP1kK*hPm`TX#?Ms2OBm2+w z+;!_tRYML-j2TE9yP7Toa-mdwqN zDnsB&jGv_@)1;~%_ZfUt+NtH$sV-*14y`=P_+qee(2{P{c8Gm zz`hb-)^f{exZ0Iw6(RL<d!0p4K)D4`BtPq#x|ZD53MnJbnV9<4$2sjl4&(XLa(50XdBNtJ&hCrSfsW+zLEsuW^8V=U zNAUIUNfe!TJX>!Y#$!}ejZ#`Fw$`du)Cxgs)?QVcXw}|()7r$Iv1)HxBT8zEO;LLX zslCPiy?OsnKA+^A^E~Id@B6yG*Qs^wrVJU~n8NO;bjoQg`G-Rii*D{!XU|{7>D~ZqOa^gLH7GMDjTc(Du z-lG2ne8HRD@JAhtyZV)0k82FX-Z!*4;4sn3I2CX{-~`CqKSm9AMwGgQ%p6=# z20DqzBw)1(^{JMvE6g5%u>O_YHurcO44S z&OQg?MVc~FfyaG8Dn9dWaVuUY#dIAYCgW&;lT_3{u2r(`&BOTw{*fUjNvd)2CI&yLn&9JboS`Q4Qdl5Tqp?x(#H`{V+h?Qk)nj8xxcMdwOE{?3y+|n z>l@1iNA5Xo1xXl2CMt^lA+2-@n?Ot|#5W7FWSW~&dT=h?03ZkllPk04(s|bX2l)Km z{u_g@CdFVI?srf)8YHcB!?(hcYEf>Jjbc`POX4c3YcQf%*W;{%`m{t<8zc$jlQ z-A&_4PQcwMd}{TI=hmfIi8EpK4S5a^1z23lveJ79lwRVKl*(l$5Ou z#1ki3l_YyN*rj>}ci2`i@)VlKX{wRGKW!?`;GT$dY*6pOSutL-`<;y}rqEd;SqAV= zT=)RTUf&nJY=5qW&BlP9L<3C0qY;efbi5ZKr+h-&8j^K^pLYavQs(e!9S*m|Qhs#t)kuthp;Y{gSAm-|q?#K$cDAPGWdXQd=`2cNAWwQ8vU z8)2d`W)?Z~+AMJH{#n?tcA6)@N8;W2z}*8M^+spw%N74XOSV>bAh2Nj(h|vcMQ|H% znHRt#I9Kw$;Os&D%=lK@kN(Vi0Xy=E{)NW(X~yyRKxXI>=z~5HnJio0Gk)KVtPsdi z-aMy0yRWcIV*F~*b+1{gHkk28suPk5=BlP(DRg;qM9gdSO!X1LD9{P#Y_YveFp|fq zXZn6{x>ev0Epqljr0vExe=5=(#O^+clNzvwh>d|MaG$i1TWI9`^A@&99>xK-K03oz zEd&(GaqvN0)hW!1ZU24WY}2vM6Zo!BCHoI(e4pxJ@?D@uhK{h-CU^a+-#v^ zpV7l_$l7ePbBr~V$tVAcPI%yYE!VUB`gm$MKe>(p}#OHc=iU5@s#5R2*i8wm2~(Y!l2I z=+>4ky*2%IY!Sk>{BUQf~U`j-AOXSrr)_ zjCRf)E{jwDK%?Fa#9K`vDG`G9xk`-IZmJ!)iCZrUq?<&f`<1*zXFdbL?th5&)&H8* zSL)st>uTFm8N+B0FG&*0e?H-lO4b>CFSlBcju*S#m9OV_Ei$KZN^fD3y_|d*c^AJO zjy`b*>0B9`=jM!vKcY4_fO;v=Jlqk&P*Ba=9UenXJtof|LXlc+CW52t`WU5mBJ0RUsG~&^l>fS?(^!JMzY4nRzRA)0Db;7bl0@O z*3Y|)FXB;jF~>{4)V8Mh34xXvI!xX@QsvN5&&5-M)bh7o-3<6$F4Zy3Q1KcMKB2bn zfuyT+=7@aJWkk=9-8aD~zeY!DLFfWfWvKX1+s zz12`r=d$Z@+JvY70lfS*-KL;3Sup)$zG9l1$jXbP5l7<$asUtn$3Z)-hg!^x6n`tQ z@o*fn-d>iHA;FuY>)xzwkJOJ}*37H72n~rDRDBF#Kk>uM%2q!%EtT41+rtt@O);hC zZN9&ew!B`{@b4nLxuhEto*{OLN=_UTSr;|sZZ~_rY{b39#AlhuMLR0Bvs}EM-Kr*} z!IzZo$;7n-q#JqPF}fM>nWJ`(K4|78{$!m1Qo>UNJq?)h@T90hwd{~qy}92bmz*^C^n~zkRbmD{fBAzx@?Gc> zzzB+3hZvQnOJ7h0Okh-ECXaLWO&y_F!|IL3^jM9!S^Ho)97v`P_Ddc_QR(dY? z&v2}o_7VOu!2t6)+eGX80Negc{EOA)g*u~8Lqwnr*mMOg9a47vyFF(RufisKc?a1- zJ6fxNC2vD#f&FufALOx(2H%=*6?h%MJGmgD!cSwPQMSmOMsaz`_UN8E_c>H=-3yS@ zvuAON0q8C!EdB6B7rT6remPN)G^ctL?q@D@80wO&1EaJ-zyJ9BfT37uCTza=Pu4%} z%BLRM4kwY_?lb@(Z>TS}#&V^{Q1z!ortBbklTCzm_nOCaglT>sZ@VEYLM|X$54yPR zWzLj%k1ef!TMleFLY^by6mC43^|^O{X!*o#M%c#pK@Up?Dr_DF30rFgCpaq{+@I}?7GY({mG*3vdfJ!FMH9Go6QbYq zFS5JcR!YCWlN5wCbYsKGZFPT7KL3llh!vk(jQ!kl2XB4uRbP@obUHDlD-5ZHYz$L2Z=On zbd|=NZA^u6LS21tez3k}`P}ygJ?;ma{$;l`R$$9n$le`-GOH%q8GhTk|9Dj;fAjju z_;afD;WE}OM)>jd-4}Q%mz!WOdsXIY^5t9YAO@2~2md!kMy1=P+l6z;+7MyH!R7Jqc>%Ag@h9RYU4C0XNt zS_`-Cpxve{>AmUA(z>hf%ggW#9=DtW^H4O_AI8rwj`oYV7`BIx@nz>xj<4Ourhi9S z{XDg_ba(_s&=rql3M4z&x>16I-9~)hKR=svd)=%=Ve}+A%qA) z$?onv@GYvn&*5Lbxi!ZCL6gmWyesD46d+$oaL_iPtzN7z-q?SD3WOjHeACl?A~gCH z8%4v*w*mhFd~wXpsQ&;9h{F zDe|c;c5cME47j^94URR!n|?E!7_KhPFIH4BZ=OJ2gi0uwaRMj{34OdFMCA+nK652yK!;>X9*d%QQ4)GZCrBwgpx5lP5 zK8=IEfFnEPO!4gIh0cNFzcJ7I$3K1Q-WS-zLpA8ox98RJ1NM% zRc?vJkP(R=8b7&|_;Vh`A-KS_Y!!IQlyP`H<4MwBv-C5aQf+WrB9D|>8h zPy`+AS#`mEqpTCUthz9o;U#U_oWc}p(2vBsb6|0HY5RJH;iIWqb{Dv!H?&(yo?L=W^!*+x58IJ!NTq>RO4`5x)*=d|Jm4hP63AX8AlMEu!2~{WVIHS&Z zpssg40nO`54K|J!$5oeB?jLt?Q!CeY0{z`Ukt2PI5?>BR`~G1or%xcMGY{$@O2eSz?vn) z60}tE`%tLTL%3AzrpF&XRmTug_sN%&Ll{dpSqmJ&VNXi)0{=3BT_pmARXzXBIOH~= z5-(y_f#%()Gzp>p&Odh~D6*YFX?y^;E2JU-*^pCxe3L;5%9<0HXR-oAl7C<@}3rivE zq8~Ze^4X-Z!2j=k@I=EN_LDse*?&l`Q7d8uLA7uiTY_5_D9>5xvnv``N;a}$-2Gwa zxv8`Z@Y^LDXQ3&~d;xiH8Yl4(`O`AvPQqh>#D}!yaFFO2$Epp~X$hct&=rVTIOIYs7 zqPV7d<;6#F+W-jVq0cfHnmQ|UdgO?BH0F|33=D5LYnp!-eSang19|0ZL@6(pQz7n!8YyrK8Loy1@W6s!y&lEG#BG&uh_;X_cxp!BL-84&c3g#2l30GEM30SL=xuB_L$ocG3bF!to zc8T)8Db+4=S@AuiZh%f)&|jac*x{va=9Ri4{WpbDZZrl*qfGu(B!(u)oh#)8LfT?P zC_6l_jB-=jHgPLU@U^9YP%^fn>Yr)f+Pkof7Cc{=I=6At-)_dzV!-WH7Zfy(WqLd4 zVS?`md)&)^@zL@n9ZK~>K2E<&o@oDEM4OckO;a`uz9I%egCB@`9jri>A;HzQAKa8Z zUw@-{=Gv!YbA?iGn2a039qZ|~DuaeNeH?AA#GG20G406DnT78VH&P+*}{3l_S z$v$WBK@!D|&N|q5TBY>n%~yQSa82*)^~9$y#jGZ^xMW}O%Yhw0vs>b#W*n=^ZN!(F_kta@Nc-sYx?Q z^Rf%u!=dmCDvWu;$g}8V-!N3^@npMctpBFFeNB(~k1*$Y#)~TMM^>RY$j$TpQd4e; z1J}`%?D1nI5jC&-C%j@fCOV(G*OS|J6|4jArUPsLz|3A^5|K+%+kXJ~wDY*|gWpMJ z^#wr!=Bv(<(~F#mebItlZva)5x;nGJoLv?Z>k**DJX@{F(pp=&53aYUa^Qv3H{b(rUU+;1+&cfCO=DJR~=nVx%+RyUZ?en47P=bsS$j+oHqN z?iz9wN7^iY^KugZ)?#cm`B4eOfEc^fIa%malx!hN%rFnlJQpE*s(9+d=j>}WN$pH- zbZ{ZUY>`fjQ+I_T*5YOVJ$TDS_G%&H6O5o*F&Po6bEmJ4w?8$N0dfu+D}E{c_Ec;T z4RE=hs(bpH=nx@oPs-R=tOx~sPs z3$=9F)`12G2IA7>QHxBevP#tJ9H4;dGV#|;Dc67FDjcY9Gbs~XR;p!pgvKYIKkqmg zmEtOfsZtE);bQ7a1n3nxfJfp=M>P8>vDc?Z+Po znQ*+gZw~!GikH&(64Un*b1*H;jEc0h;LktZFHNc*D-F_?4$|M+c{0S05hiTxT_3{? z@1FBEmS4fAtA^Wh+JDyQ9!dreK(GDu$@A)Vqr$>(lyC1MY!{~fP%BZ)lk78Sn5i<^ zc1+@`rLW%~lS6#fZ9la%cuJa4Dq+_4%eXgUKI1xlKLQ`(8gb#t$NWop`<4kXb>G0o z>YC8sbQXvNhbRt+O6R2@AH-JUalS{l?DJU9p&&k<82Y1}47RgoIY{fIB1qme^NXy; zFX=;?EeQ&q(%WunrESwxn;sXBo754q_?E1&;5q0_qm)Aq(^#!I#hKzXQmnwP^kAJgn-58Z zavOSSt%2ZpH>HQVf!heFZ{VP(Qn6Spo2x#P-J)vx=oFtVSm^0-It`2cpj;Z%FA!RrfCOx{Obtx2KKO9w z1e(#X$2UB9!hc;O?|=8qN62JvB)_mg{QC6;{^?jAk%bl7vn^NiSK;t-253EiY zce8!k3@E+4C>yNE6#U6j4o#u)7$w@fEd))MMU)HbaU3)=?6}sy()^5fDx0%(DOFLK zQ=Nl(fOBl>MQ)|{!!~G7B7vaOL;Xmb=hl zfF%~^Iig*rGAl_>H0Ib@jdK=5kz@+{P8Wna!kHUPDd&|pDL&?P1K4|mDx0_T`wQ`; z5zVxRy6-$V>K7va92oN*Xs}2BquVF^Le*;}(;c|Cru@%Y-V*$URng$n_UuHjNXHMS z%ue2BF>1x7hdjx~Dkb~avW~>2_%Ozl#5YUYynlof8cRH3YwW&3hC{7<><@|@+pv|~ ziiwo(n|UtGi4NPuiUSmf3SK2H5>qxiytF?ape@T)4jkQ^zz-ZR#AUeZe*~xMb1JNu zx~XzA4n9G)NFsmk%CICJ+<;L1ubyAILgv;>5!~$hC*B>n*41AXX@ih?XWGCNxXwaSRQcIS-fW?-(87{j-fk8xR7QbU8FT)zFmp_-rvMHY| zO*8>G-+*3E%GnokGrb1MPmzoh2-E@?!V6j;+q|*(rlLHFx_x>1a$Sr0i&asyvDA5|gPhJ5C60V*n6245 zoct*Tem(P`d?mFd<>71P^KH`?%7ax^u(B=-T`K!OzuRO$kK+64!E=my1i)sRv|8cj zXrUyt*bjDv+l9lV{o*4!#r<9_eZ-%0EZCFZjS!%{z`ipMb`B0y9k`X5qM^bP$t-UW zjHas|`tm~HBu7Syt1CNu{FG;1=`j^G@xsBieokX$rn%Fry!2?K%@@-03=|c>uBN#` zb|Y{ta7zPZiL`%XG-8*_$VTrF%W1!{8e=gz(YU`SYc)t3ug(+rh4np;qGk`38%nIA zl9{fPZebKs?R$3QTsnTT8J*IfHZL+yiUT5uml?bh4UP#$YFz3Rw(1nY~_RW!Twa2Nx*3qCdOj?&`B%0jy-u(OzZ%!+~BL|yJ_%MhNiTc3XBq7PU(&V z)L@#E<7GL@&JNX170}Zu6(uj!zIU^3qS6H0&nqV7JdbH_mf;i@`cL%)LO;q&nLOvb zWZFU5c4@`NQ5Qd9S!4XzPp~Co&fJsVi69GUYluF(H@H zKa!dhM|1obugXdlKPt@zUWip3$aq}o0{EbDxnXDH{!IrR|34^DZf7=d_3evj)klS?|wX zb+NRSUu5P^!cISUNTh@3ChJ{4*yEj|r79kew2Y!r<>l?3a`(qqz!3G5nSO|{_}MGG zU86=f`XW|P6-tWSWrZk@d`ukcFiJ$OCMbWD^ zV(b*}`#BQ2$o~KacR*l%{_0hLFF}?IDkGn3M{?2%@|rBPEQR|Urz>%vhA?QuoSdSV z)s5R@S3BqXCjzkrH5LKDV{)L1R;~|6?qAMFbx`2T!v6qiIluG_vg0h6Kd`i(NRF$j zQCn{bq|yF0AG-~u2XR3z`9OXIVC*ObUd+oLvtxt$jOq9$^FTbtmHy>{nTM+1!qSd7gYaGxf7n-aV&dU{S z1Prj2biV{*qe+td+m2N*_^&BIUS+BE;*t?;G;dxPhTqy6kg<66bohR*dbjtosk@oV z{h~}pi<1zk3@4a8{9I5?c>d;-7GK8Dw@tUp8O2p8Z9gZfkuNi3AEQYXbwy5k3e-_CZhBeZRLVBj6| z^HY8jTYQJ--Pr%*mb;O@s%qJvu2OQ^KI~)$1lbVYq|axO{rL+yl5 zeAoEgCP%jd?sODi82!_UZ`$}^N5VJj*PA)3`kv#kJn(zr%Qok72OhRn6g(U4Lu*&ANu&2U0r8$d&^_QLN zbNzk>pX-@4gQfL#e1_jgH5XkwFDE}qI}N*-2qvP$W_PIH zTN#hvyh~^EL1m*RvH(}a&axn!A=af6mx*>S+#_i!o8(>8k@QlFJwwB^5v0bP7%qLx zu2Ux}PPgGZQSe|toLnEw)ED0IaIl$y9;uS&2}wEcRb*A6>wgsej{icu@4yQns00n; z3cYp7LEx%qw)Hpkz!gXiYvEn)>K=1Qe<`4yUtQtLb4Jh=#bgP0CU4k~waBNAD*zPx z^Stc0Q{yokY#FqAqQHqS_IF5>3_f|(S@!|tqMg_zt`3QHIUf=A{qdV(`0*-Z$^k(N zNIBd-V$5`DyBdK`&bOsq zc0qYDXqu7uyrk}*GZMwv;3eDUDvqL_py$BWAFY`BYT}0n@y9QhA|>5ubTY2px4w6$ zn&m_=6?4!AWo)pBv1UDa#d)__nwuAa8ziSZslPNnSiL3*^BUh=X@yIs0a91TRh|Nbrucnjn@1&UKur7~5XDe?BY`96)2~?H_29ZN zMlB9Ux7a5gy&TMsp|qX1T+VOm7GS0V;)Mj3n-P?E>Rb=L=|{4Et;BFkL(`zB(q{bc zJ`leyt_LtlWUI4{(%m0!1D9ag-F{s2UlSqY=fsRDSS7taR$sG}L0ci>0UE zZGL|>XZ?bfavkf#{&Dr@4XEOB+zg5P`Sv%(iLvie#m`{hz-iOOiUb!F(*iBMIpDa2t=#|{x6aiGKG$w3$?FNY z`5otDroM#af4&peyA#P4z7dz`B1hJ?4^2Mz`UqZo+p7Wm) z7oK0KDsNC4_Hkm$rMvBgk3L0U?K-9WX3D(1D|AmPgg#MSqst; zY^t7`>0PpQMLjoAeh~^_(;1wemp4fyZlM8Fu5qS1uqX8b5BgkrmUveio9m;+@*~Zm z%x>z@Vl0huRJ7@j;vWd74P1?>Y?SNu?|+llQkI6g`syfe2FgQ3t3@wi!$^wh^aWtE zjzJa$#{6c<*Sb+SUW4rr?{d;rmJX$17)0+%ZS9;-NH-W-JAMx2xYk-|#whhR^C|aD zCY4t6DJZV2dgKFL8h@J;2~ERPIk(SoR1inp#7!7M3nVxg7ue}x3Y1Oyj+I5A+3E&5b}N{atTLzVtdJ|#FLx@x{PVy+ zZqYtPRSc4V8dQb!k^ViEK?ja+a6zekw8 zt;@mev?v`#x_`bpK4V&nj#I&&3d^Z<0~Vdd;`0cdvoTv|;1C^XpBk|2ns=xTKU5W4 z4Z9roo)_@T0tQknd4z9cShCMIo}J z;!@oGeHxFn;19GqB?D}B$M1OF^eK_-v*isYpt(+;+icGu2@d#|cD40FVjMt}V_H+% zl?pV1?UL%Qez>iY$V7`{tD1AB;vXW zwuq2U89q^1(HPbayBY9ueJz;$|E7j|$5Mjg+h{@#w+LXa%dUcfTZ6~00x)Yu)K`<7 z;%4b#KG9Q4ag^y?A$#nmFIE_xa4C5_pWuMOT_FM-3){Zk4R@o@b&gRaHq|hJyL;q_ zSioQUbTD>37!OCq_QmF(n?Aey0={`}mhEP4oi{TPO2OoJoc{o_0~?e$g{e5tUA#d9 zy=<)|gBVk+PJ;z=)#m}KCxKYQ1P8W)Ro$SK)w_A1f|%GC#1N1|nucki?xM(YR$MN4 z2)IR8gVMzylfr;cUw2R7pW(`{0~U3osrz0Rik+9iNoxp>#9sk%O70-P_%rLf!iiAD zw2ZO0qhFZ(%5J7e$;Igv4PC#^v2^xPh!xcT5+ELsQ=CXhJ2`G?ft8}O0I}A?j_OkK zT!f;e(%53oh4zAff$+%%T39kx1~U;7YqF-V8&#dtmBmUhjy!!!2Enc&>(yX>ra=Iqo z-`=r{8cdQUZ^LoZ&Y2pZ!YfE~jb4W3QPZ9~Eqdm608iuJ!uL*dKRb`BEBdS^Q|p-6 zpOMPk;^DVQN$}=961h$MrAh5Jnc!ynm-wV`sOl;g?SKi|k18Q!9_bXno)mdMQ1VpC zWVWibMSAJgrbbM&khZRig{l^Y54}Of`mdM%> z9En9ozSdqt;BOfrE096tqb^OMTji+eobsagY{z}{xT%1P zp}!MFxPvT=P_B?*p}to0NM)24{BEBL0DWhCeh&8B;W_m`K^E{ZSoR${E~s3wasIkMsh+WTfDxU^1e zOv-Qi*q2VQKgbW;n$^yz|MsM`RHgXw2E)igv7y}Z=ZCcCz3wuD&{F$QXIFz8UFEpy z1h?i=!!(C>{vmkjrruOS;pW?$f^J6c<&E#FS{vfHK)vk|!m_B&lTLJ~uXfyTt-G~O z4q7SOH>S_9VF2jVU*f2szqNGqxH74E_`xtX2XVU$LhV(y@?u!K|^A?JfQ}N zjF`_aEm7>QemWvSq@5lffTueMfK^Elxti}&_4|jTxY|L$hH(27yPIW4Bfas9519M-z8_x^U&Rr+|Ve0}Rz zXm`)?LLN|!uLbYyQ{fJ>0wua3YIbb zsNa5D$WL?6PZR|@G_urXP&8Z~vqh{MR_-0wcL6*!8OmRbI(u{JEY_9`nMlo^`X&4a zXoCpssC(WAaEHgOIhc5C7klSrM&0~9dp59CIz*(jO)O=11@|8au7=#&1V&yAp?o^#w!zRZ-y93UW0=R5Boo zRYOiemKwH!zLN`mfvXm!xJ}K^#_fnx&%p%m#zm$k_)PX4p)5aWA~c4g2OIx@)&2uK z4A1W|4W6;Wh#P;H_*QTuS&QrTPb-NN(4Ci5>En_h2gv1CsRZSQ87?R)3NdOrs>hFK z&T{hX=NK}zB{nwT$(Mf@hMb~XgkKdIgXub5Ab`fu%3H`==vc~ z`pP;&EMw2-g6w-L&IZ3=!mCac-PZ50tQFN8u9tJl7rlCKB<$p58)EM_JS)5r+xf4+ zt$7mvetM_xW21)xuwJ0#Yx2K_Sj}VZ%VMaqqj)l`>v!~X$E2Tw1D^ow55&&yxbw%` zaFm%zdVDLM*fe^@RoOe@GK1M5F0&8mZXF^cioJr{67DWz{x#UL;YxT&EF3m;T1^Jg zJ|;fuIq7fqc{(yR(`@`Ni+!>@iC(|MyuI}w1sXPyD3^EAcJjwpTQ+ zWTuP1a;#-FOTTvAr?EUFdNo*il~Nr!bF1*1H(G3dBN#t1_x!+fndwgf;9vM_L8ww}9;0<;!l1YAivhQ0^V~a^`wT*7AWu zqUTrK0diOO*>6hT{B1$jrVr|DrR-t*`x+B%T(2ZPEng)wLrKJ#28$I!%-UX+#_~d% zNiDsjPL&Q(+wUvAfAs;EyV!Hh0E2U2%;6=^MNkA`AySSwj@ zyTUR2T>Ok0nFE}nFY^%auIX(kWbxGGJ#qEDdss4m*{+ebpQ8R+0$1dbbfkBQLOU+% zzIyT&Wh^*J)n}C!T`Jr&HMaIL5BrlVk1+N{xVY&`%(a;mtl(C|iy`tSANP2K`6}?!=y`T;uJhpCl9W zCg0^eVfsm}rdi`v=;Ufozw4Qvt<%sR!OmO|TlHOF5d6nG6;@xHB#e z&T#0DFziVl<#Ovt#cLduJ+BW|@zzMmblC*vpZ#G1m_}Yr{Nh=8B~RIQ9`v*5^z7yj z-w$KYbJC-N*4_lOtpxm~)$6Fdu5OQh);0bvd1WJY^iS->bxKf1vFZDQ!z0`0P#((* z@-Uz*a8CuA zN#~vD{p;l3N$w>?%sxkG3mOS2^GQC(K@5fQU%p%iIqfZN4~f)MM6um$L|@$)PlMmn zoKbdyzVeQVOZocgq7068eaX6sd6gx!ZYDtCPO2`~&&@QU*8_*Fc9z406~6G##itGe zqNZ{3wpQcih>PYLpv75OfnrV;bPsW8KS3`?KXyDE`3 ztKj2Nmw`JQFzF=IbmC#ezZ*^`ZjypZ?aO;nuSnAGU)yz zQ}v6PI98ZWeWalYRlg6433F6_T{LHA5VhADiaT6LDV46vDK8><23{Pya}~G65gfI{ zPRjSdMxds871d5rTx2@i88g$k_I1?_aGKRO4Z!Ybj6n)EKMFY|b;J?9+f1xH>{7_9 zBl;vm4g@t2#|}Su>l{!nV(0g|xlaSa7JAOs#!n=s48KM(eJg$SjOjD3Zf-=?Mpk_* zvKl3RN`lHW>&iFzJpCVFIFt+R&_jyLT&ocCkzb0M#=$02%$8=AkF}sTWc?7^e(8^$ zw=S@0(iiNIE(znFvq^yd&e~tS2`ozsHi)3%a%^SdtV(diQ9yXb^mU}m$$z3CMvr& z^4A6ou>1#jzovq%^*BN+`12DRtP}JTl79GVGS|z|6O0qR17Z#hu_h1Scso+!_8LXg z)NOQ5eqwKg>CNZue%{!#UU9`CY=qhL_XoFq=-h_AShUZm-Jqfm+ha>Mmw&dcj$~5- zr{l-b+6H)|frhbe0vt@c5CZL4cBL{3#htm`>FP4c$5zSBbY)W%oK2VahsE!PXTkhS ztmR)=CtVmt$-~MkVl^_H{jSFe{(m5JR1*%Rx$`~EN;?q#rY+!$E&p)Ya#qpNv`#KA zooaz|fSpr%5k)Yxnr%s;RHE=sU*P`sW~s#;H#9!b{(N5xlK^1yf}r&*SkygZ^HhYe z-j2v}vHqJSv8_~<4H$pRdN2oOh}L>Lt48UR2{R@Yq2j{M&1jVQW{HTFpfjdGU!V}%1#t6!9^VanxwwpJHBNoUkk>#o{4-> zwdgxe2$CQd`qOgnE=0h2HZM8;(Z4hDhCIdK3A-u~$J(H!v1dw2kh1;3_%#gcr!(X{ z8#e~bLbN{qJMb}FaR?WVC5;hP z`mR`_o0V$i@RMuClI%Y+T>qd^ zR5!ViVLyXlir;=>hp;jD>CnY@iQOQIQH1{OIU@BToi{1jgkJ%mBk4ooBXS%>6Hiq} zDE16wbx}?m%cyAhm`rRQ=l*pvG#P3L*h7OzoWwae4nN1gb`6RVFP<{*S-nl7Ff@iA zDK6f9>3<*t&PCJZTd|(B@!Oh)TR zS|?e312+VxXD&7U-DuAI#@cR1PB4>mt7R7%5= z+>6lBE_I<9TK%RPv@OjKc96s+R9@s7hE5M)<3ZQk0t=`^rc%25l7iq9b&46JFjGcs zZbQo$M)hp<9QQ;Nhi{b`Nn4U{x)P6olec1Tu!SMqZ;bt~`nSFk0=)^Hq@-+!oVx=I z61cc!X)8AB$WCJKxKhF?PkV?7bMsUZczmq3AVb9p@Wq$wb0C6U{T6^f>VXSf+c37o zt)<uSBi_`?P)Bg zSZzotRO>!T{q$S}&t3uqJBOce`v@W_7<|_&m7BeOPxyY2Qg9*|y7g~q^MB)I;KUr% zz|+Y)FMnJVV#Kr^j9JKqFzuRR(TW7dnlg}p<7|<=_t}Jy ztvk+MXPx!^-S2;Rf8L+>>-l;6L7dF~-h)F*bKp^sx!jJ`3@^F#eS zzX=w}VN!l}K^MS3pEkU^6)(TWbnW2>0u`Zv6LU~zPZNCFLVt{5M^os6O*oF5M}ljq zWV`0&Hq%dKbpt*PpuX+e_WD&)L(XjWONTU5yqNB*dn^ZzP|;P7_&%}%)!Ylrbs9II zFJ$UNo@x)$5!PEH;P`dI%6|Zq)%sX7|5Qd67qZ}>2@Z$yh(-J%yfcX5KbGvJ$3o~OWc zu}ozDko$BAVEF8a>3V$6wE4f?=LaMbq1-~3mMl|^x2~kFYuw@=hzph{AYAxAwgGop zOCnBsVY7tCrGbr8vp|OTAk(; zfBYC=7YjKmH{Cn?Ktluk((TQeLYpZ%!PCaIL51o{J1%8(tWaJ-9r>Lbucn4++sDJ~ zbLg~m&D2I0FLUVV5$iar*Xr@}asuh0V)mLuNzbVDM5({vK1%$q*-K!twPDhhDry0g z1mjPZy)kz&F}?Tk+bX+j_sd@iM0m(shRBnLp&TK3N|n-S3b{xr@!uyt1ldcwoJjS- zmW$sm6@DQSjgp#=yf5sy6dtBOtW>vu>vp@RV_@*((Q)>JYYH^7OEzbdy+JgC{kcf8 z`L-s|N*EL05}f1~YRRir55ZnmX-f&aT=u0tN1S|+3S zvZ*&LV_N>^TJwpoIQC4rFQ>O^!!AF|ghJ|iVNW&<>u)JFsDqU>X8+g^;NMi@^2NG! zZmNls(EV=m?}{%renB`r2mN=R9sZb9^r>WcYfJcnhL4rTV|5IjZi!h^mO;{TNvy%b z=#^SzIkEJHv6Sxew-qy*?^IusA(shv^Ie47KU1PTv9Jtn6zB4TzrTYLmvAhwa%g^c zz0>Q8=_Bbvu3N{wdjoY9RxB5!OZ)hTjd5ise;jdMvu?j)yMvMR=5JnuVb%#0k&qg| zW}Gs~QMT+o=_21;c}T)aM`%JE5`2$}7`G9?YGKhQkRiz5kf*iC*G`T!lFs04W0^>j!e;lo=)`IME!57P1k-OBP^-TW zSilPQ?WSa1zN&~?Y@ZMai=Khl5@U~wvltL3srG}N_Ema zd-gbilz!P9t)Ui40}eGeyD|00?~`_d^po8IOE%-|{?#@mEJm`QPm1qRaWdBozQS!O z#GS?j+^j;Pqau&3XznY)t(x6va2V*W{?ixt=h7XXXlovFV4F2wI4;jtGCL#rx3SjU z#*yjgAj^I?^)gr0utM1uqldG4mo}{`gFP@2ku3+vrAZ>VSun8GJ^X9X*&6?-9g@x@ivCl}=5h zB?>X-c3<-sAGp`-%`b=S*ugGrpyKwr;StL_@wxj@$-%auKES^S0WPF>o+ZenKJZ4( z3wR7ViWsegk4VAW@JbXGXs`&gl%+^IKMxx?1EihclTBz&F)+wZO-^26SG*iK=1qD+et zokk4%yNAFpB2}FBuXO`?OFbsn?lRtt*f}f!M>6&(_L_kFwvy zlqRb~6^1t|*^*NrVXIPc+wNc2jn2{d_g4nuQ56-Gtz*Az;`L?AIKp3jq~9;Mcyt@Z zS1V~hN0wLbHd#oU+DHsPfBd^^bF=-2=2yihVpr4^U+>7awpbF$wMk#fEz9YZ>#a5M z{AhGoJ9oA>-GeY7%dpSHi;I|6w_**=4EfLNBd5lxdKK$RaZlqVN2ND)soC0iV(6vw zFQ2wd;M&sLEbx+3;I95>m9-*bE9r5Qk(%I%%`YPye_ZiNS6KrMXa< zlKX8?!_g|gpJmP07`kS?@zsL}2YX#q==SRLQs^E8YQ@wYNr>CZ1}oy#Uz)zTTa2!s z>s+CP76y?0BlDPQKf^W}z5l8=>V4enS0S5kby#5W4*z=JkTh2-e9 zqTwT*?@_TwYu8Pp@w5=sQOZ$nedOeOSJeC;-&k&$)-U%iiqlOp!{gQPVugxdR+*iy zvfj*u>&!9eit5IwxeYA(h+o{^<- z{6T6+C@IOi#3cQpNas%G>D6b%4JA{C{%NnkZi`3_aV@wu*5~0XND;uaH4$4w7UhHT1qm)HjaGR%SKacnEvG`}U}N+}^ZE}S z=qw&2vW75pP9J2}KPh^J^_LR5iGXW~lxNLbj>E<>|Gl}n;ae92L)vhxspvj!6IpdA zmjiysBwwf3J{{tYYc0YtZV7|~coN1E0KM|Byms~EY2VTN5?aS{P&=B_;VdWl4>#)c zI-R88F+Y-Gn6vb?mD;3vLo{5&**%JCg6mnG7PN_1+?b_u)pP8oVV_kjEvQN64{|B) zr_aAHA=qAbpiXv(E~u_y|I1#+pGKsSvwr1Z_FYf_Ir}Dm z^pMhv^tOC5beiYA;)cG}!0s4htIXh-bu_exG?)P1DlJHD>J!Wzv z!H1&13v>|OoxuzkyX(uF{{ie^0)7E`;Jc5_*lVCTQT{PbNo1!qyH#H3(jV{pz=Osp}i7xu=uuo9Dh4}B0$#LzwdX!Tu z&W&TaZJ&GtDX5kLGVR~6f+JhJ-L?bghn}8tbKk2wZ>$oY*6C0{C!1UfLj1; ztR-z!Xv~~$jbwZEmQjBMaqh&~U9TRugBtNlptK`C%8?O=*TYKR0a8ZEDZiE3^N_}$ zyT&2(eEzy8VSc$BzxS#wt!3#`TkK&`I*EQWsxX<)>zjJnk7A63UfRC@IHwd7zxr@= zimk}xp=N-`vty=p`8PY?1Q(f-yr~|gdX)RR>bShvd#N=Puc;VWQFmNsWWWc1g0;lj zA746k`wwD!%^N-qNw5i)XsGp!BcoP}HMa8f&e7zh2p#08IJPg6`*I7-$IcpV^4A*n z1Ur_!_1BvH==RiRH{1-XO|Y<&z9wpVREB-o$gL6NM6+a*=k0J$G-Z|etS8+G?ivDe+Nz@+%re_siHDVq_@TPJhU7+=_rb0ro$jAX!W}V3$e$z^zn78VVy;MTB-&ufwEM^^TzjA&DOf7thM~tzxXXQjs2vJo6cro z?|G?GPtsP{)qkRt_#x)gMVH}J8%-ao5U?}vTg zW3o@$H@?;-c#nI}w`(~@$1J0;(IAXl(}1}FLI965%^`2DX(TaIwdq}NO#Qj07;A5ptj8s(wPC-h2%CTFD;u%%BS+;G~lI7rFN_9k&7r5zajq@ zg5AfhI6-#FE|;DK4N@RM`jDKI8xhB`ksLfZ@kpFF4Z!u&Rqalw($K_`Wy0o*yZ$?} zyD%=^%*-ytm{kB-zebxCN^@vSpZxpLFY&vhn=I_~vFuLJ`PouxN$Na=I(ZG#1+W@% zz&zVIn#m1bn0t`A1Q(>mv}?Rhi`+(M#f(U2*e=#dx=0BC8j=QKM>7{%-&BwXLG0kO zbAQ7ZPSY-0|Fp%X%AL*azXja6C`NL&_6OYsH4tu0xK2g6Edkj-*2~FEyu}f{HL+kS zUMlqd_5a z@P7*P?ISn3Lg9ixO35bx$bW+sK_gKL(V@djBAcVvpOY{VsqEBQ5& z`t8M1sr)&0j=Vgs0vxWzJ=)UQQ>j|V{?l{aZ%5_mGC=y9w&QE?+ zkJVbqPzN_K=qlaqPETIfkl)7>c$?XssABRH{fGm&$zS3RrX4p&I0|KgDq#)Un&M6G zm+l>WJo^uDR&bULim<1?RXd=QsxuHHXQv6;wxnE0Kb2ZI%-+vxKB+%D8>P4qlB|&o z$*9X;@$HkAtzWv=F1{tC!@HM9p|~~=9}5Hk=sb`N&(dC?WAt~@m@{&{KP_HFc|5p5 zymM(wA?32T^DjnHSf@YHf!wcnc%0uVn$O^#&ET>2Y)o>OFM{Kl9&rt!$Kd9l2!J0u z*TG{nIPB=$$fK&TDK>o5oPZDkFv(C($FNhwpe z`@&%d1X-h}%!A!(Kkz%a&O1PiRTo&yyH>ZKC0uykQuQ6qy8!!)lAkD&U++K22zwnZ zO0DcR|MKB?up-fQ&hT`6Ra=Hq<4bEJNk`XMmn=-gVVx1l{TkGgF)f)BN9)^ZifyPB z!uyfQB$p3~p|KoIbbk^oDQkiFT@(U{8%rPF@MM$UINVj}c5pcFNxgWd{>Dt~q;ZUM zB3uS03`@-xBgVJ-An6iAJlMECA5o_)b`FW~P%$8`-0Nf~e7BY!dz@gbE)pgK@3B~) z>D_K$m@y653u-P9Lk*S z&Q5!7aLQ@I_AD(r=-owT#u_;z(~5AJg9OdQcnE7DKx6{+<&#^Hf9sqyG4d}o+t2IC z3;&yRvaNN0v0C)==RJX7kozTHXZF(N1`SAQ*{CQwsaK0HuRIJwQu zYjZYDo$Fp;#O)ynCAnY3uQNuK4`{iE80A*|TPuw<6VsONfA3j9vDtUtT!`C2gO;;D z)OgZ<#x83i+XzqnovJ8_kdeKZM2kwS2XqVZ>Y+&pC!AB$a);cy)pSQ+kQ<<45DeY?;J&XYFkpF-m;)u8B&IoDBaj5iDnstRpx4hXEcGNIhazDO# zgF!tFx>k3=H!W?Zqh+hoPpL6i$L8ODy|q(rEd{*$dcldu1Bx+^4rX zNmjQX;V2KLRQK<%^0n}Iy_w{ui8h7o4&T;qj*%E`5>Jm9phkF>Bhp3>9v=zYRq&q* zzhb-RT_Y`@SzjV2@^cCk>-{3{6qaXFpL_tiZqB@#_}~{m&B+p~zhM1sOFIpKR)vB$ zYLYEgE~c3$=*YwnrymJ;sD31CIKn*l788EO`nII7zDbcF%6G-s&cYOA9d@DqA>FjF zUcvha_aC6#;3%AUC26}?rlp$SnJ}V9111^Ym-j8$8OZT)x3RaK%#EoCS!5~K+q17b z_29^HsP2^J9nXhRrX8G*-5;j91IGtudrE9I@Ozt4?9KV>Q(i{1q3O0n5k&J8!r0jS z$nQ!obF=P${wLba>*V(gt9}N7Ys@m}usk(N?pWGy`5>!NN49W%iitHVeJ87RaV!tC z$?SN=nO~#k;A@F@PIN{8A!nC&p!w?ERbBa-TJ|4%wTH#h)?&h6BwKcwf*SY&{qf|} zh`%4Q6XV+bqK-eCirE$EaZ+5}%LG}WqZ74P1NWJykLuA{MI zI5!f1mYmXEYc`9_#E^sCrY9)Q3t#!kyYoE4JQN0nYd6|21$wp;y5$`x9SQx^6vs!q z?+#s}a-HT=hx8gls!@3_cM_`j{x}BW$Upl&@BGqAv3Fh68u*|g-}$Etyv|?J>bg&n z_Se<>)gIJPa3*2?31m3>f|sW)-l>RgG)D45nF(^Dey~eJ%)|pC%cb41+!fo9q!PrB zrJa4Xq)##vtazE{7(YilHJm1g{WA4(0`KKCEPf^5Qf;*OYXAAC`&>0S@qu;iZSvWA zXJNb6^mqSM$;WAyUSpqEJrN7`X5t?Bpa}bfBqDvL22UEWU$9?3M#|4QokVSsJReLwuw0vk-$@CQ{fJITFnV3;+2Wi4UAyg78z$kX z^qf*~f*9KX7de>X+51H87|;dma{a)TUH(u;U7GXa)lcC%KRXZkP#csT$QH@x?}8W) zaQh(bb#`Vva)ybl{$f3r;1fUDwe)#&v799FwB_+1+#Y8RRCMRAYAp|e=U=`F3u6V% ziS5eOo5?$oXJr$=Np43(&<~_xK*-NfS=EZW?|(Shun?++ z3tj}gi9B@Psl+01cH-IjJpTbSOm~mgKD9eHy)97o%r6YbJ)Zc+^^wl?=%m>0-)89E z0!EBQR8aF>XEDO^KR~Hc`viSS*KyNBB1GKJhcj^-V;_iP3-c;V|2HrYMC1JHyQq*? z+l9>4XTW|}>t5{0HLTE|Myt8GYl<7xVyJ%1h9PwYM6NAjJ~)TXl>Kl$FyN-`U78B? z1y(l3zZ%zoicVhO;CsE#Q>*X<%eY2f% z9@!7@fn&tzFYJU_5NSw{0B zI)C9k43#C%8f5!og;tfhe!)z4jFB?dRd2bC@)GS{X7%}!rvfy?K-tL5OO<6`Gt(U3 zq_`UTErcs!3^P6Q^D@^bZ6u>B^u5&Q?U$WsH<{y-_3C$z1T)eOxFqQ?lf|7Sc5brS z=A9vMH2E4)Db#dlE#zDsz1_6;AHX9vA#AxK=1;6d2HQ5gN8%G;W11gZzN}x9lTQ^` zeUyD&RL01E_+1p?-2gc}Ag6%MlL#W-``bQjU)M5DH~Dtj&;N+8Wt=7U)~rtJ^y;@? z-z!j1R`b;$&KmV#0DfZbp_!6%5Yvw+`kg)(VX6bkTIZ%|dM^W?+ zAJoBMKbeOeKyBZs=|DzyG*`vrtn}g2$7(dys1WTdzC(&BE-tCsl_91=ag%caBda zI6>Z26yY$@OvQXrWL7;oMoeAA1pQS<4EBCJMkb{lybr>?jF(Y!d0C=@t0SW;MR>!Z z!w|jYnsRV?z#ID)Pa{=~OenWp;zq7}iMPCqqKuaOtG!6FuENuC!$b(;Z4^&E=^M14 z;SKrZ@drPkg5uwj-ku9BdY19=RE@uz^2lQ8bWOtSK(Wb}e*B7cVdtHj2Knca-FN&F zUY^TF9zStWT&re8cpwv?toFh1g;IE`yzb<;jF31arQ!CYxl9%PZ_h{n9=P|Oy7n?Y z&s#fCpnTJbL><_#&s{sXF+Vr1;cJO%p3~jVn}a_1K!&?mP<^TSU1o5|q$fwOp=rAH zC4M2PN-B&afKrk65qng*qdfjx`d9?NcNIm<5od7Gp%v54Qv99y#9(@cT}AEYKFMen zLiVy`=8~uqX2t*5o!rB4_AoxXpTK^Xs-~8bxsAE4h_dsU`Ef~nzz-Xq%bE4|$3_FS z$|QZ#0s`lnGpkeQBYg}ND5sr%{#5<`1w;-qq|O^In65qju~v7*63jG>CKZ#jq&XRc zlS(&rtpJnw4bDCfX&>hsf3dX7W)p<-AK9FDYp(uyOLk^Pq#bf>6+Z@?V`HJYx4FoT zKI*c%N2#6RME1z)$WE&E-x_OXJ2<@X>HLY=(tB=9YyZkL}YukZ6Jw+h3sR#e z({mOcVT*fxyS@*>>uzcf-`^7QI?M~{h*!^3ie%`0afkzDAX*L-1~Bv4T2fNIt)7P$ zg??_9XAQD$^f&q&qBm3It$izICl0R+_0BsQ64UZiD}V3B{Ke(@c6of~ThY~}R!S<_-Yv2}v>jA)Ni7>$?zC(WchBwikVRIX#B4}22AFO4mIyh7vh zAQ`JoMki^Rq-s*x7F2MY1xil?b+o?LUnPbHd*ANO$M38Kl)9y(c)TN$S!5@jYFw9n zw(cab(W2gr0$6}#@Yx5icwooAm}H=jQ@ z)PgaK7xP)mjMSv$pR;aXc^#Av7a;DOk zf%}|QA@w)qPhL!#)$*(__oecfZp*28mCG}%ddOG_2z(1o9-Kb6k+sHvY_11*KhVA~ z4pT4r^zz^4bYgiwAz^glEcw?z8b?NTF5VI39ps+I{2BZm3NQvu7L@wLIrm0zQLqCi zEnU#rMR{$r#}#G2S3PyK5>2GM_w$H6{PA0TikDB)9`vQ0 zhn0#I^eIgm@PrsxPv-WGU~AeY(H(Xx_=Z3SZeOVUvd|86totxQ<}2r7#8F{2h|#<| zxAE((csJ_l^Mq4SDQ^(jce+)y0k2L7F{1XkTt0c}y&L{nrjbnE{rj#*>zYbP7KST; z|L~fIZF`Kix~Xd#K7+g;w$({BHR5$crITfvUsR60*-o2Res(yF&eDkdAA&#kK9z@# zOElAfB(Y!EEP&sA9oop1p?bY=O+&XLe59XmF49}~q;cx{@{qmE@-6k_Y$@7ZJ-Sb= zBLhI5D?%DPcWJ9b0vbk6&PmWiSp+5w=I#b~0mT>OWIRjK1RWghX>@9-UHCrJM*R51qMNaUT{ z10w8n8w-ZD?d_seZ3I$pE^9st_u}x{9IGQqG(&vV*>4xkh8&xQjVMTSd{R|=$H;fRT8eo%7#p?566tviN()IE>YaZTz` zy}#UFKv9e~eBCJL$s^~a2##UI6l2;^>bW9o^!HgIIypNs>Ur`7&T%Ny^RrMkeVGO& zvD8xS`SYMy}T^PctahPMr|;%W9}c% zZX0OKJ{rLA%J}G4aEV@lsegk1l)PS<^|`Y?I=*Q5tK&aFSI6w7t$xDjncneTP_^3f ze07V%`G0_Dwixr>&k|-cwkweyP3j^cv2NZcOb{T~y$e~^?qXJ67v3lGmb)W5mf9#6 zc*o~m&7f*|=jEaU(^>3Odt$+31;CCbhH*M~Onle_RX>>n1z04+3&@pihrFM|nB|Vk z|NUsF(Gq&m_L<4U@5@V1;TqT8OTGv|!9psQiK4OATJ>DX>FlU*ingLg{Xc-|QK75V z<4_hNThP=|p+|ftVVhi@Z4ofPV>eAQA*Gxl#x>X;CW<&QH6Zi>P}5&7^@S{&sDj~T zU8gUSv^g^Njvg|9@87dYZVTH)b(TVYt@KiuRkM8ln^7J;J?wR6U~Mu02mrra=y*v( zF>vt{lzv)>lYf)Z`S>Z)Kxpq8O)6v}CbjrR-`B*pFF`@GkAJOL!A%t}%tbaQ(?b&C z$qv^$5&kls`5a9bBk0V<$pqzk7THYT{Q9~!6+=t2or~=66K~)V=FUM6^)NU$c1K0Z zpLnu8dPs>J3pznu{zhjWMqD}@_RRPj^t3%Ehf!VZu6MKf*5K*W<@ic;U{Zvalw23M zkRch$)jp8tSZMp#tiy7oLG!(T0ZRdY@W$qE|-~X+1nMV+idU(>O#H%~A&Eix= zYGaV;qg)<%hXGXnJs|_rYVYrsjoR^7XEdeaa&hy&WWj$P-Z^Y-=LH=Mz6!uCD3SA3 zMM;5%!+)Q7=1>Ky@fGBUS8%TWPJaW?Ab;7t!QrXwUO zO2H%M!F3=(Ss~miN@`*vYDGs|>fpAd#Jh5$W^6rfGA?3pk?(>};QJa~SNXiDO|#f3 z8Bc}sB(?>?wht%%s z^LJT(C)ZKz7b=tcXXp2^qobSWl^&S+Vf*^w<-pUn0;g_4b&)R2xs}SEB(C3>D6t)3pZKwHb)r-)U1F#R2y)69ONN-nZf4jUSVL5>Xfdzg4eo&s)I67 z&3gpQ;Z=SkO0Gp{3_p7yu|ym2ca2&K;- z^3im%9~DdPL&mcMgu$+0dz%owXV`jCw(B$-u#VQ}Z$aOh2RDn!0f4_AFBH?6!GQ{! zCU^MdYww#n5pNuqCFq%qYbNCgUS%&?*D>2<_-eBB0Y|7pjtZQ5ejCdP=!Z&mM#gk# z;W_0#C7has`cIu{%CT~VVBR6cNdFq4k8UqrV|hi5LT8?mgFY))Ae6vy&EA6Ka!IO! zna|yT@@3Pyo_g&;)+*FkIla^w9a;pAr%PHl%5oD<^56EhiTFx2c3^z%I=g!}A}q(Z zu>C)PzNzaoyaRn*mR5k(l6%4E;re7go2Rf`fyLdIIekjZ1SiC3z@J$7i7Nlny+aH` zlMBNC^YvH(-$D86CvCAdOq`?7-N}oVPGT-qo6uGu!Y~Z%)d^}gWX#Cn$wU~|8L)Fg zYMUQRf3t)&T1E{vo{zabLrhj8XEc$Mf|H&aqKJDYhSM8Asv&tWfy@%D1s_v2M{0lQNu#RV+3q=yB}A!Zbt(JS4{ zaXwk`A&$(>ELE;80xz9SP?0fu zN8Q{l+-UFjs|xVUKcAFL&O@EIE}@}A2|k%>ED{JdmcCY2-@U9S+;j{%^X zGZ&X%cj%xUZpUuRAX7+a-lai9^#n+!Mr)})kxpOK5?by^)gDDECo7jP; zU77TRG^y)3++QHANf#AJF$jnHvT+vd?`R#Q5X>(shFX6e+pn)1x-ET2G^;1`FEJ?; zY24oidh%Y8wo zV_{Hz{qA(!F+L=tl^{L_XZ}!pq6rGq6Dta*D?M>FTv!vQ`A##F3ji|zSifIbIX8Aa z@U8+g^{<7s>%HFe{Q;NmJ?93I%m+R5O%yYjsP3kpuX~Sy)i2V0SRXOf>kl=?@wCZy_+d*P>(gDyYTqrZ%ETTGUdYg_(_Rr$@mj~c?(3)Ox=8%0%Q->hrf z;1|^U9Z>Pe3b}KKlV<(xL1l5qtO&b1SzI~#^XcHNLW%>3ACwZ~I>)LPJuVieJ{V)E z9?K@v^|b!=D_R6RZDt`?Qj>~r$ry}@Z`eBJa!QQ9?%}H3oKdsK%^~nq##y`PXiO!$ z($rMoUWhe~eEpieSH7hcw**pKekg3tf1P{}SWgZv^6x-!2|;A!K(`d#V^Fj5c@ud{ zTs9x`xJ-pM`0CY;lGH1VEM0J-w-V8L_q9hz7D9=l9zdJXdez|Tqukox>eKwN@?{J_ zUbEfpy2@IQ>-luci8iS?zJcv6!y{zU2kD}~+DvWJ=!qMqabV_EQGfrdKZ1QAoB7b& z)vl+mdeq2*Re7*n+xog8hW~dU&9a-Pd%jR!pzg;iIJ1n^oTBW#D3y%lja`w>8qFT1 zn3VM)eNm0y51#@SfWX>P%_`8-{gVn!AYTQ0EWehrr0lN)9I*^!-eQ%k(V0IM`UE?F zT$)~QVxn0kB6qntEb$3-m@1_imk_H?U0?iX#Yo-hW`uamTa$Xo`bl1%H00i76Z}=^iyA5Ni6h=6rGFNX5j0>0ZEBadTT1tTHlED7w`9PpU;K)!&@3z@tem06z1RiI* zp!WM&ssZh1i44KMYpM$xBX0)mU>>KoS0{*)*ZsbO{wv-|70%z+^g43ic{3MwVL31P zzr&kbjv+CZ_7ar5s~qb38l9mss25jt<(+edXY# zQk)|_y>R+nQbi!R2XeMrDqvwu>%F`1tG_lQW8@?V*leOl0*RbgJ*MMMHm`zo`kP<` z?rz=uNop9UmY+z$v$>u*HWXU zhnM(&fNx&yAgy9zWCd0^B$IFHYL7k(x@BkjRQtwU6XK|tVQF49V;XYL;PhPSf{^5N z$v-V)BzvD627<8`RDS%bK>v;3e_NfKZ?T%%PfTZlMG&x+d-XWU#L3Br^N-Wuh=J8h zmLrbauUypo72C^#Cd8zeaqN?ZgAO!p`UhFfrp!@ty0dD9U7Pp$bu+-ywsh*J#`m~2 zzC92aIcquEEc;^lq(7H&$H(|i>FdP0l%m`9JPZQEuB)R*1x7n=y<1=xO9BtxhU?Gt zBL~Mpbl*hWYXY~;usr#fq7@NG6uvR+C%wLFvJtbcs>vbvtM}sz6ob^5_-<3Yyb)O*(VknY^g{D_=^!t72?PFYFC9X`nzs40G8D5(Y|M1Jf2%wLh5 zJrEtm6T3fI3VdPY{7v@UT)DSJ_}I8~x-oO|>(+8n!80dvypwuPU{8Ad)_#^L80?sN z4J)q4snI=fxO;TPIq{Ds>k~f?=~-yZ^7Z;28(PZT0r4u<)J<*L;g_3O%!M9BgmdCG zppLHIHKj})(~wNXf*Y2y80qUMC;lxhp6e261$~N{RyvNtoK|} zE()U~80orF$i|4hiGu)tx^ab#=4a!N;#A^z2^aBro@qr$*pZy8jJXVT2z~jpsYB` z`{3me=<-D4g@cdOKsfYZ5B@SBGfwfU6{e01}suEs)B`RSBEuGTK5tsN_ZJU3S4)a3PQ=cwN_ zFR<9)Q#H9rXVMz8`lo;$%QJ>Gke&%sXJa|kL}kXO9=VQaZK_im@$?aycbuE-B|+=2 zvE_(&B_@94PgDa)_27zqoCiBmnzTK>bC8+TzL88-(%H{KUf6m~V7Yju0h5CBl~pfj zZ60a>XEJdwb2=n$sdL)wq<`O@sec_8x+CdoHeNIh=VK`IrnD`Wo^WKbPB`HOw+N)= zfh#lKqB|r(-%CH)MI_fJfaI*TVuaXMF8HE`naOaKT@%6E3m8|jby1mE3oiRqQrYM? zdUZnON!`@5;4ABuIHhR^A2e@vYMLC43&p{w$h@FWKW1B3Qe>wThQmc%`;Tr#|P%MH{W7E2C3etUHT}K?v)BamF2(Pg<}9r~i(QgcajusWdLi zF8=v{0O2PtUiLnHiBMkf|CL)U0}JwkIlnEE=X|<|A<9I&&f=tiNS`uKlH=E9Ip>!| zM16S7>kv;iBU2_;ycSDSK2Zl_m<#E)%K#TekE6qH%%4U9EN!ek$S|p`S{`BWK{2!b z$^k-8>%kMTncz6%r##N-KhoTs?63pV;}wzhhlTA5)Dw&Vniw{6d7|zrgNwiOo1|RF zBDwXHxf?u$&y=u?l3?nOu_4gegsg(qJPM7kPa$aXT6MydA<&h4RAcN|7-s{=qydLO z3?|PLjU#-X=bXZE)j%fvpoIpQ^jO$eu$)T(e~^;*ognAmQvzt@36m&*@g5h9mwiuc zeOnOa^sS0Jaqazt#XU?x!iQj<`JeZxkWN?yL@eJD zAVvm<+_R%)e$w+s)nQL1aDSY91fR#MU)PwCcOYWTvkwb?39WFxURE=~oU&3MeB`vJ zpT{rJqK~-)@2gqaRuGto0A9IgdiPQRcNu_GWzuIhPzlju<|`}HWGn~0fA-B#?yrRa zjdX`Q8K-hS|AHXP=y=-N+XuLUIfeDL>r$(t!mxu;PC8d4`A)c!;3v>d`w~&<)B0kM z4VcU{$VqJ4@jDM`H9N7RG1-=j1y@y3`m62wHMhG6*+-0HJho`TiOtk!7~uM0?)3Ln zw(#4PAt6(nSB#XD>}tze^}|3}_pRG7d>^&p`K3lLbp6WprS+@djt1v=M@kkm?~#h* z?@4JNEgrVB?=_zvN_PXa!j$@-cWI*HQ)jiGrhr88WWi*ZzfOro-nGk;<*0Ayq46Ql zoy_Ku6@^GLb&>c;_R3%Z%4x?NP%b{oZW(d{e;#r<=0^YF$xrNxQV$Il>i z&=2UW6&UoBBj=&a5ogtvWJvgklD4S&_{>+p)}AzI(@h>zW@~`Sx-Ij=UE7&YkH)laB78%{{XV%kjHJxXilGiIKx|7k96YP-s#9k z92U;#*Sjos(13%zcNHRV$)q_nuJ$l@ryj`jo*v;1Yt%jVwIo}D-u$@kI|>gL8+IMR zDfF>3IfOYl%B_peCdjy=-WbJ8roCWB_0sRsfL@6$j^4Dsar;Dps4e&8=S@wLI{B^U zTy|YZ9mKaBfl!Wz4#TI97(>rcV_#`J%eAY};lRtXiJUNCObGJ#p9UMbCyy%JW-V_% zw+aU$CaWuL>c8?Ymhr!3Gx+p{4m*5xS===4e)8!{z;DxdnVWSz1lbL9rQ5Zij6S zjZpUftJ`~XMw3ROnivHLvusTD`cPP;As7jOg`+)6T#E_-LS%&Ab2op(^FQ5Cq8Qn_ zsLY%(L_TuHnlWUJa&zq4Y5R!1`SP+c^X@B?3~WC_4;Rw6f%tuKYpV1K^!5Z=pQ7!3 zCORgdBMpHjOw9x@aucW`E()aQ7Cl2ILq!OgGQY`R0cT7{lW;dd;;q`#j<(f9V^j36>#j5jTN&<-gv}2*{=XCB=&j%3RcXA-VD>@oZV5 zLw9+z+VmS)6_HZ6)+fIS{B+CykaAnP)BQX@mLLZU3(%tJa?(b)U4JN8q2@JB_Ega* zqmPdAaNvgl{B)ubsCqQz6!}t62O~j^bNUo6QtUl6x7vV6pYgX~IRpi^_H^vbXJ35} z)!=W|Nrtf60Yc8kIA&*eE_Xt&s&-rumW(-U^tY#~0lZKwc$FaW#0+|LG$iprNSb3~ zR5^)c%=T;Q%x9E>HC}zkc(0U7XFdk_Mn(1hh6wCWCjUQ6%3U<)=~bYCW^(M89B*87 zIObhz!}?W;&E`UCW)rB%ZV-yxKPeJ|XhTk1iF&knS7wrqmNtBLEWPI{XV((L7$9pXQorierJ?ZOx=mxIT=eQKG^g>-m}|p}!VC4u^tx}l zA!C_3at*I26&>oC+c}10rY<*}{lgPMDYPfqRM3$Ha=EdY3wdSrEh{~UH0C>Z@Jx>a zD!9mUh}o}geWO7Ao7_6g>O?<8WZ%wKZe3C@rSjdJx_H7)5jTUTc~s{_k3j(J*g@$? z{Fpm-7)mIWUixLFv&bC|qbnZq9-JabK* zWIYe_-8BDi`7hT!ctu$+1RT0z6k)$?C)#AK?`qI&FoOc#7=EDWFB7$;$aV3zfog{y zWtR^(L&19zTu)p)>e<4zVqqC-ovXcs7`Xz9)3znFcOrZQSgP9avU3GSD3xU}xxfI$ zY3Oo6RmmV?lRyi?@2wqW_&YLCD$=NdG6qA)f@ers5@$k5J5Z<9P*3ae?#Ir_55cf${x)(V%E_r?+cYJ zTrQ8SEv5ZlCaWr1I6_;;e`CZ&5j;~T@|#e|JH1veWRf?x;{1B&O5`1cyh}jEg}iz8 z^W)^x6Df4`dK%Y~!SwP`x&>>8*v4mMeyR9ANzcH>}0x=xSc%)Q&EqEbCc za>wjX!WEyOnDFC8NfFs@?BGJk9<;%qzKb55{jI`u9#S-2*qB};Gdw#j}E+@}?s5d<{|As326t_42@^J*uPTA{SUTfGE{wm`W zc8J&A=d}UdcbFfpm5whiIQjz^Tc!?5@+nl;$ku^I-a_Jjmb35SIRm;%8TorUWHp8w z$x#!DtugeRjUbZl#+J{`9SU5r0YPApBy-@I`F?HcH{t{Ic@%s4S_ zU6S3Bi`)jN6}VjI++Y{tv_>9L?~pXHN5xRXFD_8683?x?Lp%d9MsfW)7NE>UE$P4+ zNLQ5wQR)92Yn?}ROJ$=kmg(4Pvu;tyb(*l|Wgeq}*eCZyFfM0wu4GQbD89R6!k`w#H;k1_6k zx&5QMS*8Xz=A-EnScXqDzC)1nXvVU;u$^}^<`bdu^-}$5Qjx}FG7@WO(L~t`64Lfh zT3L@wHH;<8gPZ}M za%uM(UWqNlMeJiCM*{?MD@DNAgJR&m4;?=WXeW0nGqiGjD=%w4=A4lzjWJs|ERuqgJ>y)u8$)2SR|{{YuU+fd)}=@C4C4l`D>30~y9BDIg7 zHNOw*he@-76=K6!iI|Cbzy~9r!-J2~pJSrx%i-P6_JH4LWh>or>P3A3>kCZTw6-58 z*Nd(30Q~vL0Z;`9AbDmfTGL+}3p>aU{a)9f7(7{LGlGtz|8^u&U2wulQ` zZ6tb9jy4w99vV-n^IKhbsk~(Co2{Q|BDFPJ-x5Xro{_wu?aOtqbQ&I!_JHQf*_(bw zn-c3`3!}>%`aH|wUDn>{dAau|F0IHhB($_+L zg!`Tud#`wrd|(k);nYYljVT>F^fj@qc!OKF@Rpo2bS0Af4mrYsl6dDe>XLYCNjj7+ z)=>WduFg-TCGUoG$>xZgds4a1PI3?Oq2g%WXbO!^=bCArA=lf((n#xd^2fkq?NsbM zQLas^%p2_>2Q}*};hi~j3y4$fptZu`n4DmGcl|1bmxL^0dDuOvB;?>=X0vsuyXaT5 zpL5Tzyi=&hs>Xsl(mb#UQJ>^0_1}s$8*d9V-d*V?bWA)&d0C|)AnrfvU!uEALoQ{#=yZLnHk>hUJBpO?P z7|ADzW{+(66P%oSeihZ*cuENLYoziB$_~|^Yv9;p)f!JMV|hG}TD#CCMz3=-RPmF+ zf2Kw!alXv3KEV45yAO+q@crEKJcUKB;Kg|(@)TeAG~8dS{hC4*2pAoz&F6&6csk0| zMYIh%HIRfEY#if?cA!`w@257Y^InG|Z+v6=Qskh;dWQA)i|u05Y>P_(X1Mps z`u_m>s{OBt^z)(5Gsu}1Y}ob3rENRlw*t-Z4?-$N@U`-301G*?A|MbC8UFx2 zl$AOl2HGJ`=f$E4|jw{-od$ z$RnU0)tG!IsmbE$BsMdj^q|BLamTmeLZe5xbJXm5Z2l(GqtTj1yo>!}frIN)TX>I8 zcu5yA9Je0B*FLr8+Q)`9cd>#b^B8@pNg=V*2BeF_x{O{IbOK`g4^LmMFJVWxdr9^^ z8V?d_QeVh|>O8BQ=Qyi39wXA0Yh@ahu1a*xc?Of=&00I@-J`qVNCceZWBJqthkE_zYj6JOT9^7^PFlo-l?->J}@lLnbbjLtN)EL}vww7p?GI^fHHyOYfe4^OyzS#~{@_02ro6-(GZ;3xUjmeut2wuH6JV32q| zwcwgwnc_w92Bu89i)FKxH3qR~;?u6_in^AjsIAJ5nf$Xt!qa_2+sSl3hKp6OHq3t0 z5Bk4aMAdAajxHcSFJs=kD((*v4-J(|tH8z#WM{2QqgnW%=|%=PU|TiX+yUPF)fvt9@GzZ9;EazyeLI(q@Q9Axw( z7^w8?j}n_n3$@U|_02D-d5_z(=reg`<{$KkyYZTk+*^5MnIhpwIj@@EQSnQ7*v6Ay zY+Pcrbsba1k?7Gz7GlQsK6Bf>OTj+j^@HdoxU*%G{UR}nQ9YSZ1>AY_4|?#Jbx#nc zf|4`He{fAv)AgSbNYc3zkR%+AhL_Zv>K|FL^f;Q^dJi!(kH(~FVOIOZ@H%$?01Eli z=U?#`_V%8sZ!Q2J)$6|zYmsRz%@xje4)lFSL;A`+@Jnt(Vp#t1JQ`JKK4NE&{{XLE z3uodjTe%%RU`{W!R zv<8o9B2kc?jd)Gpi*KXUG}yk!bC-2boy)(HJrenNFamBd(8EAEf?qn)-{AwT+u;B7f@}+XyfY{jg^r1-I--BHDHL&$l<_vVn z&lS!1u4{O_IPB{CL?;_mlaFCtC};E3ZSC9AxUU(>7xq2KGQ=}(a5Inp099{6Cp2YM z+AQ)N0$VvgAkJcr=O*_jbY*(;+zR?G$Pu8~DM zjWX3nG30k0b6r^d(a(ii6Q(@l%5?8pTKn6lhOUbT#?zd0TIv8jx@YTLzlr1@*psPl z3@16RYAfGIME(|eHj{Cq$?+FZM4sfanDvQ=>P8RXO?qrzAJgWyhE|)alY%&}FVP@` zc=lI5UQO2rKjB{QXb8}3@S`43#~!uWjj1TLW0wyE?{l7i4e73pwrf?u?x!ZBkHc0= z9K-z^lg3uM@sNiBd0LMzm-(B$ard7>^1==KeA~S=U&*GKYR^?h4FRy9Ktj!R(fSlu&;=Jp{lG{&zF4?We z&)C$>q+Wl*TXQkeKyHQh^T74%T>igjbR9Dsxbv5#dl-69jhfuxt2XyL?*v5}Tg8S# zGCS9)W3Lh5XNvP*1j6g5>FmN!y1jeXrm>B3Nm2ea=i>f0bx`<7&Nj9isX6A82NH5| zgH?XhY$w`gjFL|os{%zaCIn}KYn8?JH%$&7aP#j%j!()y^(e=dK2g$z-M2h+!K9Gx zIefC`CQ;U_Pkjthuro!#=Qyd=RJ&!5TJrA}4NP6$c{AOEjy8e+0P3x%)900$ok?=5 zqpQ|+7$(r}1XnAcDYJvuit!U~EBsT6MlRp!MBo9=I%M`0r)vhjx-fY!E{kD_ErFlN z0s2;cocfb|P`6f{_WZdR7$oDr=U%NkT#5BO=}(!S`Jk(RW97Qx2tL(|;=5a!R_6iA z?BfEuT_`ZnVI&-s9r*wXy#85G8aR*pz<*lvdATQZxh`m*KiX*8sC`(7uJHHUhXVNQ%>256U!zkUHXCw2kpuf=o$JlL&$@{7YU!bGPv0|j@R@oi)@++ZZA7dwn z^E?*wN!6ChTz$VJgYuJ0{gJ30Cg1m$hZc&tJ$M+edh+bq(Ek8u!f}!VAmIHmifD$* z0DooKkC!{88oRL^pi>fB25*<$cSX{0Oo<}0Sjnv?? z9v!@B>|@*)4f6eX`eM8vSGsZIT{%XRZMXYKhRNe@YpR7+R{9uKhbL3kye239#J4Rw zf2(Zd^VYi-G4DI2LB~^E$A?aI+bl%Dd0ECf15$XJc`fV~L=FD{0k1Z6`KM#D6SPgq zq=jrQ@W{kQaqMfz{7WXYB=9w^qKd;(jFe(R?Hx{eIsB@Xj=QPJOkv8ApIY@e zH0?TTHFtrzUB~h5TgE=eb0+RueXCEbN3gwPWwyGW&N&V#U^KL2$bbTi!)md3X5I`Q4#%g-&S#O16oc{o%E=U~X=}|Aekw{>jXB;SG%zQpL~BxuQ!R}Ul7L=8OSaMRO6*|5!u>m z3lxPu^5mTQ*3gHu)=0`z=FuEb*L*|$i(p#NZzy-|CvJVe`t?%Z#5&vRcWTynD!rTk z0Ign@+6}*x8Wm7BQ#(bsaX1oSwnmp&M^X|uiq$m2Yb`PDhDZFI3B$#obAG4r-E2dOm` zIVvkfYB+O2>UtMHH&3{FRA>EZ;CBs)Oky>QN( zj^|utq&WBB_4T9$vVCbeEP8k8PVPYirYdYQwd|%X8d)|2g*;c5+<2KI@l+8u{{Wg? z02J_Z>Id_$Pt`(y!X@_IKJDjzq)6n;AF6AyPZJ*4} zZJ6=co|&z2++{jfIpN6mXttb>`1TdibewR0@atT4Afqh}>B+UIynx^C(^vg?8yID} zo}Z05kInZ#S`fFMzak%$9$UY!)}XVP*R@z|?yiyMjA0eJ@y|-BqU*L-a!eNT0K#AZ z+amdbO{>98Ihf#9CMojHgqrwiAbkLB7)-}st0F47OD zdJq$$vq32Ybfi@{e3r=T#}xs00XE%8K1>h^=h}kun1)OKB8Jdd2~t}rA5%@wQJ>)= zlRv$Gm2tT9k6KM^|SJt-G<=Ha^3gdDfbd8U(wA71pV2sy#|TcPVweA}4( z+yheN@90m|(^zo)b>fDAMQ%n%H6+oNC*smRUatt-jK-=a>JI-m^xH4 zZ~H;d_l-!7Rz@HnyU^2QaL@NS3!iELh58@A!t;)mU9*wjpT?#6ax#(q&&r`B{_`aC z;-XBrqEP<;=;RPOocmNPRFm>AT0%)d^A4SjMhDD!=sjv%xoHzdR2&oDke`@v-Fj4b zIVS_98T;H1%AvA}Q2p)?UX;clD`XG4X~2!Kp8J1Vjrj^lOl=+NWwA)Ca9^nCNbf@J z^LkUsL3X$V;yeO9=?HH+AROb(83c9u)-4*QdFP9Y8`X`T5)6bL-%9bDXyQ8U{7ja? zlJ&8H$2~Ec_0Jl4pWD{%6#oFnzc@VP=lNp1TJ5D@7GFBa%gLPN^Nt06i^C7yiSc|! zw>=KpK^~P7G{{t7@WeOJ8h#^|=L;q{;~hb+YfpLF)T{3xr{`C^_lD*s*w_vDSLspJ zADPKnnir5xwpRhBg@K5U7q|ZaTDS@A?myuriPjdtlb$QlFD}8*ZQEkuqv!FiIj09x zgXK8i(={VF7Utn zgLI&O=h17_EVH?P>z#2x?wPNW7mr$*8zFdH)I;tLy=mZl$DZ^^luwhfy{XE6PrXqS zj2x0T;Ql_8?f`DTQ&L$XkQnjb@~1Z{xOU;HCHuWkUs{z}ck%~c!jKP8cCoNPAf6T&Iu>2Y1%LP6s`WxO5%sacgtfVR>-xy!5RBA{{SOj1^i)= z{{Z15zXnC-W;`ASb65`92mLAdbqD!x4_cj7+xdvugh@`?nmo&G_i8X5i|4N zwPeAJ{`00d#}$)lkF^iG_qtZGA2V{;#&e3}rSP29J|WNs@=qDVWcRI4Eqt4HQaQ=R zQnvt2jiZn%>0%s=j8^bQQeuF7#KB=sg+aM+K=0O)K5oazNvyOGQMHU5sd3a2t*>{Hn-cVA^*5^UXjMn-~Gd89eu>i>M`h!@wY(+4QE7 zR=9EIHy_3ZYQe(00_oeK=Ch=fs{^OUjsSs zTd0whKPvR33acv;!G3Nja;uW+vN$4zQgBs1;^Q>t08aoI!#M+=N|Cvld3=-Jn8`25 z4?x)j{{T8oncS7si1IwX7*pb85-B0N(iLy9#7*MlMb{l_7X&~~=@0Jt$9h8CyLJ(! zpAI&OM>y^&*EoLK_`ll2-w*pK36+>W}aYXf>-ka8SPc$17yQ`XEoe#+@2!| zciS88_Xj4RAT`5{!{(bkIuJWkm(L9*(gOt;$sAHEzTBAOT;3nLzN6nG*R=pXblTAs zL&&HIm&;s%_q%ng(v>Wyd^l2euI%wpF!=%B!3KtbGN2?$_n#T*?NYEFUwr%2WUrV8 z1xn?1rU>oYr50CG;NXYB^sP7-5%LdQ`&C9xPou;Z1j?@o*WS6iR4u4Rd-s_XlgLGpO4u1-~9WHqz!^YI*OzG?*MMcV=x;s|q zpR(COIr&d|$c>d_P;u8j^s-5mJm&;g)zWGZvGX+|=OXLeGXssojtQzP&#}Wv7B><| zCnMVxQHv4?E!1*PT4NAYozM6BisY?c=S3<>3Gxl&etvBJbcCI+jCHEcz(_fMUmbmF zMPs-4c?aoUZ0B>eM#7^V#DISHKGkEm`C|V7RgMShYN*1oTymg=H1Tb#_vkb<=C;3Kp}YB-h=l~DbMq$khr%%M?s8}QmD*8l79AT z)44A|1OuFpylPfCP(dAPxZAaq_k7f=#12R}2tPU#6GV^2t(he9oYSxht`9h<+nDf5 zdvi!P0H**9b~NrIMHq-sVYuU(j%=ADCm(v58)uGskF6}*u$&&W?nQ^jF`Tz?QWXM1 zc8mt7M*jdUM$^}tvZQ^8jfKXhjuYIwr=q!K&TRV$SzrZ~+>0|R_XZl3i?kt9zh1c#3GR^|l?s}#53gp z#PUcTIIlIhQktZ{y*}jn;<=?Br?rQv%&cFGoPoG-I`^%McYh@To@2)uu6ij4n!hL{ zanD-Nh1bu7VaLk2`MXk%k5N~rbaocfFWInIa68tlCoO^H^A{OBXCA!QF>c@~`P-N2 zT2VV%*3LAUjaMRDj1+UkKE2N>qy16FU&{_kUc7Ru-14x zfxxc4&O2CQHYbRgUmLT*s|$fHH%{K=vsF|Py;uTE_4f3s7jWlnop`2+kzw-Dws!UO z=BfkAjB*<}&q6y@*U7b3RbiCL&N3>zW1Q_Zazv&Z&fW}|&owv)?C#qeg&fpOph^h{ z6oHa{wPjcVIO-ZuRPoak(gw&-l0M+|=}N$FLxbx|&9%6Rx_VO!We!2>-k5}A^PvT} z_pIxw58C1R7a_COt^y3e84fUe)>YUeRB~Oh<(A2;$mnMBj<|%-WE>GR@^r*2{A=;Q@v~@hx ze=wDfM_RLi3J@TtzsA6$2)V+Hd0%l8#64Gm49 zq#x|@Pi`Y(K>q+*1u%|d`3ExgH89)d{o#z%G%=$-Qj!<%degT*+KT`O$U4+t^3k~C zcVSL!l}=An&{Q-tYRSCIc~rA-$Bgq;Nqc`6SiaHzyc~+DZ#1biTUhgK)@NBRDuDee>R{p<^t6G3Kn5 z`B>$DD*6=9nwqgTK-RDK`<(jMS#2^KCCrPQ3|7h9=5evn#^tT=#_mDKG^8ko*lh%q=A*QDn#__{c6q5? zfV{Q=I+~&cQhb=y#&MqFnI_RP;Et6vyMwUBpr?FGcfoBd%pAAj*bTAPn0NGCYvnxm2l7|kauA^DVy&@=ht z@{l3K$Eu#Z(kc&_20x#rO^jpa9dHTfk5775+Cps(0h7OPVfC*}mIT;?bSOg+JDo>FB4o@S$Y<_38AOPU^*g*PIWQ~gDR$OBs zdh=2usExmUZNxeK>D$kzf5w0il0;)G!-2u=?@a)MASKTu?~XI|H7F^te5d|F{$cNo z9AokAMh5VAI08K6a6s#kj`Ua<^ah1d=SoQ&xWLbE zeB!0!U>xIM>L{?TM}_0&`sWnF2KL4X^GQEin+iuean3VKg$?&qobWOJ6j)a(I)lIW z&Iv=&`278902m$SMgIU)zj<yw}#A74Y?)j+183X%_zgAZO*Rn^8o>mT>_Q>LZ6p}Iu!ONTl?M(y>{LlA~K}w~vjnm>SW zIa*HRfzqANmcJ?vGe8OaCxJ#!zh2ZFa&X^x^``TY!0Ak4G49Dtt=fC(+ z1d2vZOJnQSosNF*40rwC)}Bbg!2TgnWQG|*o!nA`@}Ir;X#qDISLQsmB%g3#jl;(#7l2VhU|cA#U-Br<+3Uft<%yLwOq z062W(tqXuy;P7$AJJYzw@pYyg;2pqp!3)#r_3uCkH+h-e)rsb%1+q98=aPHkm@&9< z$RzRHpGs15BV#%8*BI;XK*a@wsy1YQh>f7ZxB-4IxFz{)LPDnZHQvvdxaBCZI5o#f}gYLtVp z@c#gG(*QXjlh%O?>DQ$+_5T2X(wuGJ(jT}u=Xa-S1O$z}4l~Uvo=!IOG|!vx#UTgC z;E~M$E`DM`Jh`W@J-bu@VaP2fUtZNdKv9Befgk`7H+QP{PFBuC=@I-Yae`On9cw#O zV45zZazEqDtJv*F!HR2vEF+t$(4M41accMW64~{l~ zKc!`tL8Rr}c&RNHV-rMK6)q+PGEfux*0+b=Q{#yxbc~4QXUA>@>Y&RyDUZULZeNm)k>Z_BJgIA&lWs@r@ zCS0$lwtcG+?U?w2@q=(BB zV(vfp&Pb~I6d`;Sr+lDp(NT|1<0tvlzBI~s8^IANkhI#oj7EKk#aY}V(JkTubL1)D zSIlO0HL>*hT|ExZz;W;Ke~BgFgwpUga1P*oK9%Y&hiT-WbK1C%0KgZ*S99a<+`9fn zvHa_}CpqV}eruUYO0%*01_llk(H=VW#@^JsPCAa1cRZR^BRD&H_pT_Tsj=Z#j=A^h z1xUesrRBcx@4)(0KQNIFIL%3%#0E+Ezbe@oG3rLq(;r`Ic0P0Iv>rWas-!C9e+4@O z%T7*q3Zx)VcS)Y7lh1mPkKZm3@JDW^+L)oq2PYe`Qv#wy8%aBqXB`b(Q7w{SVEm_{ z9MdFK&UqgucpuiK$k_h?D}5>lU}QOBI_E#FXvs%Euge1XGgjRqf%aPRJqpgu_?k8X zK5mOPJJ+E2;1%#h*t$XHuF-?Lhz~YZ_?wN~)Oh2lAnFzoUKbpi`VENXF5?$K%{mC^-AC)13R)g(ll<^pdhe`h(8_ zhviV6-g5l7`3F_2C^Dyn`=hNyz=!+940Qk>=SiSU6p+N>jsg4M(uQDFiDu?*$F?a3 z{EwA!^rhI_lwbk0dQ#MS2)@Mn6HbgS<;wKH=~4yA7z3scr!?@mN9)vdrlJjwAv~dt zzjCS*^Eb+R6Q6pSe)!~`J!&@?bHV6(fkOl^XUWxgGxi zx)0W;Ir}y?`()7_0gfyVaQ^@=(xEuo{{UKWBg!KU=}iNa`B$Lt>qB6MfN*~2N^t59 z)$i2Q!3UOjKb1t;^OL9gz$#iDz@M1?@$z$lPAq()3C%N|64HiE`VFzy(3i0Fr)ySEoR$y_<7zRk58s5rKgQAr}0G5t1usF zAOJglwWHxpGT!UMq(f(KAZP)~I^Zun9G<^iR;`bMtfAM|K?p_jT()p=*ZlRxdK2~$ zZ8MT8qq9tEUb3hlWrV*Czn=BmvZu-`=OZ z@Q{~Sibb`QfH9nQC;tGgPQUP~N#UD#0={;FL0?0f$x4QXoV5n{YsC`k9wdS_l3W*G zl&&y4;=OId3=<;#(Oya6-2y!#;^O9cVjEDgJ8*I7*YvKYFA?ce>Tn`oxh)~vyF8A2 zV1d)txogu>cY@H~jGA8d=UO(I0R;X2^^>jYR<=GJzLr@d8*PCEI0HExAJ5jcA|Tm7 z!)_UWJl7B7Ek5S+M0gb$)vdy~#~pKweihF~Da|PxP1-sYv|kiMcd4vPG(?uj!OnR6 zYte~5Oav!Vy?a-X_%Fhj7M?7I>O@73DGMrN8C(!Xe;W1DRG|UK5^We>qtyCVn8?C1 zv<&DuHSYmt+T8ksMsgQ8$RimYJ*qtLf8L<)^80ijjdEPx#K%AQ>Fq%+{r-4AjY1A{ z^B>E#N(%XT$>0vu(*bgEC(3#rl;Q~kJ*NjAn5J%BqvbrFl&VkfI)APmX#nLUNjD<~ zQp1kkl;FoLmE#=a`5KFzpNoS&5a-u(?QC2iZ65@2J!DzWna0E7K#{{TL<5YAX` zZvOy{C_gU@ysxb>66J(^NI(GaJ!wlfmUtaIiihR@0B0HLo@v<|dvd)oO)#-AknC(P z@SdZCM&rE*1MeP(y+e`m@;cII2e9L(YA!1lgB^f{hmtyu)U0yRgB*C=I*!#?Ghp}p zDf_y0`_%3PkBl5fgVZldh;7Rcv%&X`DP`P;5BI$(g_KFx_b?G?rZ~$W0B9+n|@#lw%<7W9jOG9g1_gQJiAVDr`ivj zu%?1CGJlJV4%Fk-PDiFctt7={HKDL+M(r{V4|+z2P1*c$0PCStmvK0*#$)LkJ4cVQN7JyTrjeuzVxr0qz5UwO zZ{axPDHOn4Y59-hYSn3XC)vlbh?)+Jw{IttZX0pxwAu6>Hd~Y|ZO#u+wZ1d3^2g@< zslcD_br``E{j~dw*~hWZzK5lKfg)Jf8%un{oYYbHdrep*3-$##Jyh3AkU7Wkb`-0E z#~^*vO;(q1bfehgo5J>OsjL0A%RO)qjQ+pjMbCxpWs7S)wmao;E3zbsiQUEp1)qNs zpXpEQ67DZ$9>*9wFJ!iHs|%MmUKVwO0T(^Jss!+he`?&w(b(mf^SI=X^Y31oUQMZ-dUokRcKNe10k@~F zX!_kw;p`%LW#5He#e&HZKY{QB$g#B7eIc#mED5G4hKWm>0T-E z56?E2p)O=ov$xb%)M`q!tl5=XYMQz|8&B43u1kbPD9b}y5^|2C(>sazao;Aq=f>V+TU~sk9#%e;=&_N! zk_h1OURUF}t_GQXvD{^NIq6k|jpl`2+9xOA`*t2BE?EBntvSfAe2o79pj(aDA}}k# zya+Fv>@=5?AK#u2rfc2sKFbINzUO|N*0}fY8R1vDN{rrU$r$a;aQ-v%4wEhmf5)-= zwc4Qn07#7PILAP1&%9|Y$){ZW$CYexSYfW}BU;}JJkP@x&OB=~nXWPCj;wKCL0Vl~ zjXv5onk!f$K2JI9-D^>DR;iMsd@t%6$xy+Z>a1 z7#Qh})fu?8GRgbcusf4pB_;QX!~+vekD#dJ)VxG3#uid`oZ)Mq>PCwadum+TwZj2A z@P7)v6$lI&NZVcqp6>=QVq6P#ooo&Nxkr37I0{*}&bK5sAk2T&r~Ecry|1K--dckzYZt^KjG zEkTq0EN~QOkHaI;wRf|}1dzy%r2K?^ zYnKwgcxy}HJRV#B01}^w7N{|^YaFTPIUFCun)V$N?G2;`mO@*~Qb`=tM?=zn-m-|? zFu2{uG5GPH*0!NYk^-QgyN*35tyM}XBsy?u+@IwUoB{W#r=ashA%@Nk z9&MI@0tg@;n6D(#ZX<7rx4-CY3SR}u$8n$1y??~!F=Hn7P`*hyCpG1|R62kBCs$1! z^`n=#843^euUifLJG0NL@t*5o>e>t>MS-&;@u?UB%s_sCbu|T-mNYAM41d*+Y}DLg zu+7(}wRv?Vc{9EW$d3Rvb|*QjG4AVu_l7g+-mA8EFCWEIs}C$-`i^+0h}k5V5fTq! z(xP0ICyei>WbIG&e{kdcYYR}+p|+Y}?-=QJ>bm(!`OaL4pY2b*}-@ zEx`DJdfrgaoUBL$_Q&+EpfvH155@blpEa1{-}0?7uT(}HyhG<8H{Je~M^U(n3lwLW zNAAyGYOOY*97VTWLB|}L$<$>xx@ZcKkRc#*>s-)-Z(WV$G-m$*!bM~D+qnMNI7SA% z`&_uUn^Bq8SV!;2@q5;yXg43)+KaWU{{S#7G3YCl)$Kf~lnCEGZ}RyN0n;?*-o2}`)rdYHmx1?4>&^vn9u7%{n`8I5gmvlv0PC*LR&01?@wVp{{VUC^ zzj$`i_zw@iw}ANO+X4(5qn<0-v_uP_LI6??s*k03h2Ho0i)7wppo`PSdsnz<4UHBM zcYml_=*4^p%8$g&jIk#ra5J3r6zLc(tgc%CA?LM2aU^ImSm6G(M&|0-^b6EkVlCw! zJ?nxoxz#BoRc%(tUdX3Xk2oU)SDpCDl4vejTpiJoT>htLtREQIOBK$r6=lmNGNkj; zs_HT7siqS1&z+1$cnziK(i{{TH}(ZT#IghFU)WZ8 zG4mJs6})A)*rUu`1S1`@*Ey~#s`5H1J0Z=D?f0?$DN}Ct;2baHdsHRjD%hQH0sLw4 zlnnm>yo(_YIjmG&%^Z>CemgVoy(=xi>%b!%;<3C9_JzO0$j34hlhU;Ob#!Bz{57WR z4#Km14R9so>Dxh&_Su0XJ_pL+QSDSltwT2Ek9@QC=6(vlmoRij^OC3GmZz3F= z`wz~%o5UKvu?D<`J0@h<03o#sl{x3P<3h~=P(+J^ z&sy<|y?0YvC|p4y{nF>2=Ag0HwMUBZl$}77VCSg(tE1{n&+7-cnIORZ^kXBb{{TJd zU7^P^p51H3V(|u|b*n@%%t2xX4`0%xzt`_0Nk7r2`?$zsOX_SsuzMmjN2IZzrAPtZ zypFZ!UJ=!&yRg&C!zd>|d!giy{CYW&nsnS9nY3rjhcFvQJ-OnT!;YQ5 zT6sSy81KzBkIFt!Fb8Uq$i04kj62YC!RMw&G@0j?Jw0e1qzoRPT7V;C^8v@){AugE z2P3)fNur|-}e+_-kd(;^U!ly zqJ+uM9$Dkonct}U#QrqpI0q+<-RXdmSOJ{(6ow%vBp)fq)|kiVZddT7BR!Aa^rkRQ zF}IGihKN|5$L2WBd88n5!Oc9X<$nJF-tSE4{{VT9J?dI12*ZvC@$~kk#!+}5c9D)Q zALVXa{QeYg9$!J!XB8!l<7;&3NzMlgOORjjq{tcMaw=I!L!1tKQ&FcXr0dEMR;FEl}g9CP=ayK(yUC8yFcGJYJ*6IF{HoE$vuZ7JXPN< zmNz^Tj&Wb4)A&*Ook#C7yn{M)YnH$V9>a>~-9gnM8zcjG=Nwk_o?x<-zHhnCMmYAa zRoOq`BCwHUxcmECC*{cYq}b8kY7qYb#Q_-@$U)EHTwjC1U)s0fdGilCgDybyS8wTE zz3Z!dKexkmTpV<+AHwX5`%Qc;$Y5JoEDDa9M^EN+^v!W&J`=wM@gJr>8e%m4Bjv~X zI`*$rvdC!pKk9{fPle8Ots5Z!0LO~ry;jJb@%`VqTyQ%kZIAU`O*nM#*R=`cax?Gx^)A$VfP8DD62*L39{{Tq0Y#x10dbA(%4~&1) zI`Yi~e)ik%3;|xJYySWq5uM-i%htazue(20sk$v$?fYAU{nBe{K1zba@vI0xyJ7ttm zvfz=zarsbkSn=!AtppxR6ZdLF3;msLaM>{PpIX@>8zR`s@SUtl=jrSERkk>ffc-ie zV&sw2y+}vOG8gW8R*_aAvS7$zwaSx(Acp8qy%R~40IGxLlabH12e;u>$smKcV?C** ziHLmdoF09D`qg(AD7pUtMsu~ZxPDa&ZOG>*C#6R;>Xzs-K`eht%{LO8>|l?2PDyTQ z7&d4;>ZrkrWG}5{%JVdzDSo)(wNlc7{{Sl3OKVV2`AM7ugX>cyc!2@VeEb~tKGkIueX=rS&RArTPAG}gY+wOkkoj-- zz~EGC_s7UOegd^55f}*AdUUGK85U9%1MhGunVMtbIF>$F8K>ocwc>5hfjI5ky=RFf z+y>|Cj!j;TOGdy2-Q^y%len6(DGB|fd6_2pWCRjNr(9P{~#m1Es(oE&T*)L{WH%W^o+tw{^I ztm;X@80*r3vf9R=Ar3RNaDS~ybfxs!R4a|K9mgFFKs>^~>lI1CMjY|~0N1PYG)G0$ z$(~O!lyS6y)MWANO`u6_M0f{v9L>0%Y&nXw$0wUKt6sxU!gS>&hyF+tA0xVrt} z`}LsFxVe=4Lw5qW3H8)P|L@G^fY zc_7;iyo6wMAIhsN${d*9GE@)?spCCy)0#}X3BhSf$sxtq8THRvwoV<;h9C~Os!)&K zISdYD$pCX!i)F6?E0BBoP=TR^M&=<$YI5fp!5~Y5K+X@pr6X+?=O>&BiDhLBn+Xid zoO@6{h2cxj-zWg7r6-`^wKy$R$(nY_1S=Zx zJ$0G2DR43K_Vuq?*5yUD)bR3WV`GZ(+u1zbQf^n}W7fQB-OlIMU?XIO)8am6;2SwT z{i~^ia0)Urw>8h{`3$&I*A>!2S7QA9?d@4Ld)(};BU;osxQy;>9e$YBgr_ z21|A8?_QItdA`iPN!Z^%1abcW*QlESq29gfGRJ!79G-r(kB=d~WI`*kz!BO{q_e!j|E(YY}am`3_uNOb^(*z%S=DD@BEwCG3mg!3vZNGJTb*PES zUz_-9bYta^7x)b;k~09_+VvnxGCO^KwIu4J8&R{nu6omB4d-qIWJo*Ji;pZMBtCPT z&@1XyEB=lS)83p9?v!)>6#dJwenZc<6$r=L6mf$fq){o_Fg7pUY#B_i3gp3WG8cfuCOWV~;4Q$4^SF7(Qn0-5htTWq1s8jkQq|A^;8R+i=A^ zf;Nq-*EFOPDx5I@RD|W@#@-hn{<>tgJ8mt3@8;BijgX)A&T1^2oUq16N_WfU{pG<< z)Cjghm`iTsaH{@l?qmo)@u`$-4L9mRI{eAkntWsj&J#6RbD22@6=EFc^SuGE2Nq^7I@p|;Yl9#lX)5d2%P{p##j8}xogt7*Ft6kjrO+*mcuV$!LKsc zE&Q!oGZEwD?`VgUE8jVAMaKjm;W*eMixW_c`S6~@I$Q*N0R>N|BPoJHpjjMu7^lWf?RBitN z9}SFutv`i2ObEK3ql{31j|;y%{VO{4K$sIR@Q^)$ttTs$IqFAx&b{Yoz$30ty*9Ki zo76495xyO;UpUFdYAFn{;Nb26;}wf)#4rV$8SPq#Kqn2_x#1ldRW~iJaz1X2%}$s( zB>t6I8285`zADm{3cL(%&1j4yHMcCTH|IGc+*Fa|sr}n$ALp$M=gL92VNd1IUqItpM_$Qyn}A1`spr6eAi`^Kz4 zmfJp6%|Oc7KYEqQ6o09~ZvCn=fdLpiwKNmFWn1qLtv`aDsJIQ}BJkM8Dmf65oy*du zA^gU{PW0dTWZ<6SlPNn9rZNFR_o`qC%M=2izImOp_T0a2)ofux=qwnm}8z z#}w50WdMWsyVT0cVm$N}9}OYv!}k=J(IX$6_50MBJ5Nru%(0V{6sI}pYDyiDSHDV3 zjE~-Px|0KXjo(UzmHyIj!>)h&)S4P6W0y=9r{w3L?%s=a69&CA!wume`hMXKWa&HnlOTv@dv8;i2&~$>tHZ z@Bl05?IvF{PJzGanCNTel=ETn%x$=U_XYU=?oK}(3i=bmw;{B+zW)GT{+0FIIf=Kq z*!XTG!r$IUq`+9r4`EtCl%%5_t131DVJd%zwP+#v_)*ujedHa{`Q=#ZNC+LNH;l=J zT;ifRYnAK6@rJ3eH}c8YRov&W;Qs)*eg`x!DsqDuq$B4X@!F<4N~q(lP!TS7o)1cC zM>lrhsKXZI^aiD3s~^2a`-+dPB*4Set)Xu`28i%_SDosXDkr$aBpe=<>uiehAu2lJ zu=OiMGEMuX{{U8b#XZKIj{v^5b(-f9^mCtYrC~^{4^D=?TUD|7u1J9W^UZKqmZVKD z$bS)B)gfcDobF^oGzB!fEjoUJJ)QJD_KBHg9U4*F6TQ&(JIkrJx?^uqPn)x!jYWSc#al7ow16U z!kb3{`N%ZL6<_Zir>9C|$jljWo_{I{j4BPsJ1R9%=156ylzG%g+cOex280byK`vmlO7UMZQ}$%&Of!ENo({{XW~gN7urJG0lh0C7l+gprUitJHmS{{YvZ zWQB(SmLP+ikxE+)_uuc}VDum7nu&_3QY3OWfHEpJ-MD<~j(YW?<1I5EApFE&^dyXm z5#kX)-X{leBaHghU?0qo7jg_^Cp|u&TAc!j#tHj_A5QdK2dg2B5@W$58R&CQ4;YYs z@p@yDc&amR$srq&lYn~keui>605}xB~|r&#&V`x!%KhIFF~b08YC?{{YK8<$Hgh)|GaEGbDd}FFxlV{d#Wb zisLJg`%ivpx!ZE%E0RgW0mmNn2pD4GZe!|s1N{EA85!I>iPW^5?jW8$I$$5IKXYz5 zUaikO9zCg`IrBbfvE_hC7$?_{2|oS@}!e+on=3It7@ei=PZ zDNfQ9?*pE{=d~+<{xAV_`?%yDc+E(m3l%eG_k0o4+My-9v?I#l2UY{}%{DjrTXVS2 zIQR9Za#=A0Y{0oWgs9|x6l4LC%aC}@FtQancfcp+7|A1`{{TH|Gq`OlC_gVj*E9iS z0#iBaLE*h9Y#h30VGQ&fa+A zH3mLr8%G}1FCdT4=}kQIi3c6P!Jr0zd!OOvfz7(zRDuC?Fg03L_6B#chb7l1u#6P*3i^vA7m z&TdC!V_q(a1Aot>vo$2#GErE(YZMYS-`}-22XV$e{%9cYkLL%cY5+Onfx@;n<2n3CdR&a2TlyMlI32oUk=}s-$r9ua zo}K9D0Q{b#ibdU#l6k=A(v)K)=lFZm0k|hWG3(x>`AUu>$!zqdCOpDR4jBDuK?RNg z7zF-3_@*%r-aPlE%7ku5`pjTwtusHpY!9ddr?p6OQ8E~OoQzNd2x3po)6|~Shkp3+ zkM({30MGTMJ^4K-2k&y9dyJ9%sIUpn;gCJ)aztY)LC1cS7R@}IryLC8(mQKYyQE zXwKifdXBW=yI~pbde8zAF^?}lg&5@X-?b>=(UX7*JBCz=U6#sK!9U5o@iWb9D#-48ca8t{2gclKn4fSv=G>Do<66b z#W0rMIbXfnjQqXxx`B(%?x(FO-?~i5#QU7nI4nPQd8c8x;B-Bx806c$pPPVk2&Lmc ziJ?g2N|y2>#j zz?GPV`kW81AFXuOlSy~`Rs2`EHwwuOjF$P&0|8IwfA&%iwjSLmU2ErO023o=DhB4`;9yh#0a`)=~p$| zhBm$ow#=Wq(%Js^8941-h~zIRL#D*}0FXO#->=e}9uBEv^Qisu;F_;ytM(Z4B6sgv z`aP@M_{zmZWTbN}j0P}NV>$N86{SaHXH7RN8J;YbIleZ0Kedf61Xg-IvlK_j+zU4L z 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

Y(%&x8cXle1Amu0 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 W?%Tg+a|JX){N>AiuP%OK!+!v0;CsXX diff --git a/applications/external/bomberduck/assets/enemy1.png b/applications/external/bomberduck/assets/enemy1.png deleted file mode 100644 index 7ee7cb27f1ee52c63dbdb31e788c1011c8448d35..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4757 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 diff --git a/applications/external/bomberduck/assets/enemyright.png b/applications/external/bomberduck/assets/enemyright.png deleted file mode 100644 index 45e6a861ad9c914af25be2b6c1b6097fdc6f0d17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4536 zcmeHKeQ*@z8DB1lAqc@j2r_}Jiy-;f+mGA(T5^2kE+mIsIKnl=p;q_f-OHN0+so}H zccEZKiNFixQqR2oRlB(;cCnh8})Y=IykGX&Ci_wqS)rjt?r z%gx-~yU+VPzxR25&+mO_-_8Eg;>l^V(hvlh>@D$>!GC?6mTH2(CVxdY{JXd=P@$D^ zaWooJBsm07tu6{skdP$=NqqeLsx!|YCsS|5GuE26r?*s_<%K)0d_HgfpQT@K{rQQk zbkhfp{lEVQe(T?u?}bl4uHRlbRaGxUXH9--N~`$noc3c^rmb94h~zYGX`6RB!?AwL zM?z}DwKcCqn~$B0?|C2Dwd=<8$$K~0(k8SBG~VN8$|f$ebvb8dDA%eFfZzP~t(!Z_ za}GtVmD653;OLz%X59Q)p9wtE=SLlB{umoxE$o|^RRM=$LuIC=B$4X>T&hl;nE%+l-|;N?9iK8UFm>;DAAGm4{`&bjx5vFt zY<=kd(-USoKU?+8gfl%)_Z__y+24D-`Ipn{Q)UwfZ&CM*k8fPodhp0k!|zm^ZZvGn z`@^Ti>5UgwwpZbJZ2Z+wO6F8}f1vd3lCOW2GA9uG;yKtp*Dlp`+e(mO{mR2E&>8mo`=MctrME?E}?tJjqVgmqPd zQ^Xc8N-IdP5FiXR4o!qZ5tU82Fawu`W8F+(sNtejxv&bKA9X7+fHG#rOyWfext7Kj zrJ)5eQDVzH%laYUlMAcVv?xmu@p#-Ex0sb!kf5ASCqdE#P2n_4x+jBWgbjkPjlkMG4AG65%j0JVMoqY9UB} zLSGx92H=h+%7CiW!~{@O3nJRw;Si!Q7$2>Pg^Y4UfdC;8hOR1Hl^U^RvDfDxjL<0v z%HgOH1<4-asman1StD}OXN+=&Cj!F+Z> z#Lz@Q4ujPir?W}f9D2G=lD665FqyM~R}Nw-RHBYak!A~HgzIf#VKIxgrdf#U^yXKg38qb(Z~~GC8vZ+E)oRr z^yruCfL;C?DNvF~OLoSB^E?SPBw1kpERqGcQIbT_Bu@j%J`!D3BrVRxz|tV(5po6P zX>f%uFdCIJ(mP%W^e#ZcaFW4kXMmR^z-Z&KorCg^@m^p{vb=*QwfQwxayX?xOP*f|QRN!sA~43B$a@VxKm z4CZ?$KmBsMlS`oJ*dn9icZ{wvx<C0@_cz|4+Kr6;E#Xlh|!U&95zbJP9;$F^rDco76M zz5V!0f4N3|vZ}LQ_N0&Ui#rxRF#k)4g?NifJxzs= GJ@sFi98UKD diff --git a/applications/external/bomberduck/assets/explore.png b/applications/external/bomberduck/assets/explore.png deleted file mode 100644 index 5eb50b669b3ee364bd25a37991519cf833660ae5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4540 zcmeHKeQ;FO6@MfFVKICS5~M+%n;>Mv+xNL2?{$}u?1p54jZ0WCDOP=-n@4u{ExT{C zn+O%Cm1dzR>NKrHD9yyy0WywM2%-c=QA^3RBvOVJZ47D=Dm22Vts&`syZIcQ>10~| z%g*fXedqqp@1FBJ=iYg@-cweRshg*RASlyS>MRHU+E_O^4g98gDz1XR>vi4=wVaK? zp|B#!ei2q{Ln183WeI}ffBD(!%HU~ia_?7pbs1(f``yaD?|<)&%Wd`UXJ0CB*4I|- zWKYUZJki#1;r@=?%9ca%ybRpZFs`AeTf1S8;ZcBr2Q~kD%A7$w#ojFVfdSKnl8996U=Iu-z{rB9!`p@z{U;5J8 z6~>1%tBPKmv;Bi74|4ndT6pWV#M4E!##_3^zF!);a*w|{%X9vLmG)g5wwr%tSeVN# zvD&cedv`3_v~cm3VL zagb@XUc1_5n7nNP++r+C7|Xl+pDo%kclxexdai$X`TBhL{gtZ@r{T*w^CiQ~Eb`(# z_}2;OsXy(&&%Sq-kQU}m`D0IX`hrJq{Mz=1Z!Ya?THjuI_do03#-(4}(;e-8xjk{J zcAxcZ;?)(K%4{1tCe|-F*3#Ph&atjnPLgj)jHx91fjLtH2Y-j>zc8;Sccb&XdM1l7 z9%wvjt!_3{fB0M*_IzhULu*-N#*`(G&79?2{97)4p?5~}+C`^}5?@}vuW!P;=oBxc}6R;Gy2PTldUPq|Zl>+{W+Dh;3coex&W`!10Y~U+>$R|K=y? zg{_}G*inrj;f!1U^sEbr`(nB=wxw^8!z`A3(K-8z+OFF>PvlL#^+DPX4+NF=&5cVT z`KqBdJv%!sXX2ShZpLH2$CduQ@|Rzw-{%cq`+UtSeGoKly$o8h!o7;&m4J~I6izh8 z10m3(5M(QehgiN^RAEl^$w9mR#_={iEDLu1Lnb%o4mrdsxwI}UuBj{Y@^#fbE$9oD z>TGca5ClY(h2sH#Fv7&``Xny{#+n(`!%2u*ZP!=0J+MOwi!f!Rj2KcJmum?9QXOmy z3ldZATs}Ynp6vQ6RShvH8jHn@G191neJD=TG>Q=@K_CEuL~4U78%KhX9E~Ey;S?i$ zSPrSO5`;BQmQ$jtU9Si8@L+s_klQ^(AB+sB0Q5lPYzW1T7#awmBRwK&aSb3D2~Bh0r)fAe6;oMrg_eMx0gPB@=7mD0T#uD;QDPATMfEfZQkp9$Msh!Ax)nX%Sh3 z!Z8A&B{Pkf%&di`DcWQ)aU&=m49lP^S^vnWG%5j5S#g@93EG5kRsusP+H6Kx!O9|{ zXp#ttXU)7sCaDCTS+0ZwEGVZOV0|JQ3i^^0n&3>K$7R6agEAqYT{-qVa3cOZl%mtD@_c8)`{T==tPYZ$BZPEoYBg{fMkHMtk$Q1AZZ7= zFpjXus!G_aD1N(M3klXdho;?NI|;1HI$2c&q!>Xm7{!p77sDAGXH4dN3|L0s6+xD2 z{~KD{Jg_ZQ@=`eh)~`*PQafsmxH0vVdi2Z5tpvl#O~J5yDuoDJBMQkl0at2@uVRBf z5u6?a?K)_ezoQj!NgyN(MIsyrx`2{MP(M;45i>4HIDv75h+9UpM-)kov0<^$2Xq8l zf%Z&l1usq(DsMD8RwZgx0D>VHg%Grtz!@MOfe#3V-uVzZq&VCY*3l{2Q0-V8EUFFtR@qa45$7n7CS?QXGdS+iq4Rz~ zGZ62c{0y|)om>Kj#}^rszTq`;WKK zgX^A|eG=Ryr-Vu?BM@ZF(Y_Pn%>^~Uc$eyO7vJ^wjQMk>GKWmDCSbZ(Ev`@N`hPz0O0VZ)6#7jA;DcPn LWzLqO$F}_kO43W9 diff --git a/applications/external/bomberduck/assets/playerleft.png b/applications/external/bomberduck/assets/playerleft.png deleted file mode 100644 index 86997a985eab4de991e315858b17ed7e0a981f74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4311 zcmeHKeQ*@z8QF~0mx0CKOUBB$ywxh6RdktHd zO&sK&?)m!b^WCRH-G!^$_a}-oP1cspFMM#|(!zW03)g0!TE3v}Zr#$0vELZGUb-+n z`tcmk4f{`Lq#nI*_apMPhBtb)weEi5)l-XhEletV<{4f2+r2_Tt8+zP&D`Z{#O4PM zw@BN!PF|&d&b$BEbZh?J9iP53`MlWoqIZt|$djhBXS3*cf4?u&_s3n;U$vdPIJM}q zyDDYY=lAB?FP7$K9CysB>pXqwYT(LU>tF7-mmbMD`&m5adGiz;K{9?OIh|g&(>Yi; zEO+PnEsn}J7v>#X+q1Ny#LzTt#XH_#-}_|hvzoo$C$e_cG@S1GxI257+u7aQThwu9 z-p$&5Y=`ah%NI%}J~kI^H&&&L)xG^&7d@4mz4@EID<6JzWj6Yk`YQlbF=yi*SStB z$7fQ;y*ppCHMW&Bez>iZ_(e}kOGj07+N66Qntq3?=(m3TJm0joAI?9vD0Tg#Ikz(2 z#<$#`e`Z21^UoC1X2yza^(+#y2cJd9d@@;Nf+;8?75k zk9>lk*>GujcO!;HGOq>nGtXe2vwCCv(OdJK7O>>Yo}4d}7ruS^_2S9b&g<&-hUAm$ zcP>Dr%Oy#DUY@Stw&M@}I}xw{vE2W>bp0#+9AD(~FIFD71v@k$!A`98EMs{&XygRh z4~&Um7`7;a*vk@Oj&B4C>Id~w$YHp8xYK}2g2S-N>>)g1Cuoo=ng}@KRhhvd|R*t~qAOM088ilixqo!Qq_IQUPR0-;( zU|5TSVvj;9k~l2ZsN7VKR?f&oVE7R4DD+_M8W^HH9@ZuEF*QB6%VALGX9by;1Xdeb z7{bDdem`cj5`K)fnynZ|GXzF56fZCWPf~ty1eH4!Rk#ojR4Pbrlpv2;r2P~iIgB$4 zCXD773&vP^9<$I^nimA#LYajT6w4zL>`E>$GAorzfK)U=`YE0Q7)euBjJD7MW+VIn z1F&N#Mzs3J^%e`)gfie=q7H@!4L>S;OER#a9 z#IVCB1;I*%Q`ICbHp^;%r^peXEC(D0H6>Jy9CCZ$b`m&+b8!lQqy%MR37VxC9~_WombR7>&@uuq3zC@p zUubpnp!W2VS4vShe^N80chpL-F8!2#3`p8mLQ!o~upFN*A<8v@^kPA-w2N=xLiGTi z9s}(BtQ=sVhBxBNdT&{7s#-zZQz~kLDF4ve87!!EByZ&!-WevYh zfe^d}#o^0RvHVgme9@W|u3Q~O5MzP*%}BJBH9_O;irZ6h`=^<+bF#_0?9Os%x=X33 zRh)8Ay@Mm#)es11Tv8fP^=i0k!w+Uckkjod_vQA)HkaRlpc8dnm-^T4dAzEv*jiU! zw5bq5w*DNv|C4Ed>zY*bKyJ>);=gX$e&|PQ8vlgvdH7{q63*53T>oG~`_lQyYkd_@ QKsv--QRQl1^w4Af0rThwxc~qF diff --git a/applications/external/bomberduck/assets/playerright.png b/applications/external/bomberduck/assets/playerright.png deleted file mode 100644 index 1a6283d9c26d9f9582fecd0cef9765ab2b288576..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4307 zcmeHKeQ*@z8Q*+ia)5y4BWi$k(Gn8(_IB?hw>x(PxI2RA+3tBttlv!gj$hEe0MKjw9Yu0 z(f@KYclYk|{+{3aJiq68-+6bdyQpBQ<{k}#AXA-%_G0+2j5X=W@Hg3AdI|o0QtK&| zi`gg|j)=Sv0H|CO1}KOLJc7i!x3Bu^_LJz8fnUUP+kS;;H^24s%j?g4kYWBKXnHPm z|3K#3^Hz43wbp$>qpKTTP5jvx5J?i!bEdYxN3DG$1-Inikb~*j8@qNKPi<`|Xx~w1 z_^^Lqb#`)S%f7n3j+B>o4`ih^JvLjDc6zV=VC8+9SD(V}y>VCkXjfmX`}n!<{ikf7 zqwm>cnavr`HO{S^b#z0`q0Q&&XI$O5k36@{+~2={$;PS`)(%Ih0xB8o}Px{Mi(W&9gzMTk?v{A6x+)kTqIB%Hk zuKFF;!V^of-dWeV!jXejPhWM){nCS5;=OAQxS!JOSyTRQ+uzSm+vBvI@9D{InVmII z+RU_=FJHQtlfGd-dPrLo*B18-HZI$iIqjL7*RH(((UrOApC4NJS~9t$GndbqF-L!4 z7WsJ`*>r9j*?y{>;vZO)arRo(wE630DtOC>IKABcPq9!!FkdeMlNo0qQ}-*f2qAbwn^ojP;OYM>QlF3g~M+ zBoExrL@|)Wst5-h)gUA<7)#;fM*PE7k${?xk0U?;1R+#{tCF`ZS>SZJM?4e>{6a9S zdO@*oL&^d_D%NeWDKlz1V*`QiBfPhvhjUlK5an_)c9E-6!gJcKn6f_O6FI@hs6(1I z^9BI)IISmH+`xHtI7@LPo~JXIcrP%SXp$O3R9tE+uiDP-( zU^4JH%jHqH$)`7Y0dJsr-ZzHg;fMgck`0WFN}=*8RC<~RM$U*EeO?o8AUO`F4J^P} zU^a6wPS(sNqVaLeaxoHQVL62$>jy+QP9W@04wv*!HTUELRGQR z36Bs2B{Hk1Nt(=59!={_M#@Z+M&l@G4Twmv6BSNUr_~$O8Ko=?Oa=6hmJkFeldkrQUwt~-#d(CIyK~ea!bf)CR?eIk_BV4#vLJ;i&<(m|1%CCmTJ7lNJamUB0b7xLZJMy99QE0kb zc9hCCF{s?Z5%p>ad}vgV%TeWO_(;Q@b0BDm)4tS`*;)USr5Z$+eq#3E;K!e!PrGF6 zqTlSkg?7Cym&HFjs47bfvH_+H3` NI2}dyL(A50_zI?I5pDng diff --git a/applications/external/bomberduck/assets/unbreakbox.png b/applications/external/bomberduck/assets/unbreakbox.png deleted file mode 100644 index 5e65912d53405d04bcd05916947a8de8fc5239c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4763 zcmeHKeQ*LAa#*}l_EHe7ZnC@CWRtz7rA^W{(9lqV#IcC`@ou`N$%f6g zNqff~tuoa16pDf(PzIfh5AYZ zc0^x)uxe6uafh&a*{bL7ZU6Qbb<5kAiybrj+}Rh~@;;c=`r;Jt(bwjk*l=HGc<$nP zWy0FK-<anGNJXNLodp5GUkF@7z+}L`+vf=pS>nE*`w^*Ni z_U6;Syz8-FAHI9@QDwdPLf5ja_k!=wuX@JToHp$*%~PERpXE}4zqxxy8>jbNS)SRy z@>E6J{98W$MV9Sf3mbJ^7w_-ie<6J6t$&>py|<-bI+1eX zxv+d_#pVKJ`q{kt)a>k(r(Eyd^KCp9_`TfUE`4`7wJWG}J^GHKB1mR|1eNIblry{> zGO&Wo0Yf|#ff7ZK{K9yI<%2*)IS`P-cKwCd_vukdu#O?Y7E(6D!*{DYoE^5%O_j|mkLskGv87Kpex#ChC zsV~r>`HCPimClj@2zX`JSF37-F&bmBm?35|$V$LS*lacfbbtlOhcV7ZjD!I4>q*15=7-%g$J%W5LRh^et zA~XsDQYey$f@F{KR3&kktWmjXGl_CWCIZ8Ua7TF$)}C;NULFtQl=)gMJ-5@Y*VbnQ znU@46F{C(-=2H$0(b{ zg0X^?#Q-piq{y=tUNj}31fD68l@JTJQwp&GV2p$Vi3yExrpW8I>q!GXZ1L8xst5z@ z`f@2;8y}wVNg=REWi>Vli8CrrtXS_GCSuacKFQlgbmG|?0c%O^{SvUNa6qBAq6xDQqDYW9M*@N#jUJUnHO4BS zC;)kcTtRszxI%ADY}9R|y<^ot+XYA%hEo`6^N|Dt$s>sY!i?9xL>#AW6ie7Jkpdh> z;gl8Qa1)D}N!Cn@g21zMQU^l>{x2m$P2dD$HZwT+|0|KU5QK@xO_-VI2#f+Ef!WM9 z8g?N`5VVyfI4;=&KUawZvbT;dG2f`&`3H5IZ~T$6lMW*u6>#^HHuwmFPc-AuBW*wy zFyCwW8R)2Mxde)iFES>6$LSiUYfKD`$$4B|<8+OQfiXFctLtZ@OE-Kd24VOv8-qvQ z89nFS@VI<^r0lLJf*5kO?}T_qVI4H4t8R}g{Y+ZU^)siu^qJ!YXqv9N{Hj9^X(uTp z@q!NoG$yIlsP=-tq%q?r=v3r(7WuM{bgbR4L-2{kD|YwygIHFp{{wKclAL^e-K+_( zl_?z?d^4U32Ch85aAyv(?N?isr~6iiN)H`tCH8?-F>_AFzD>vWLNLVbTHxGOd~ef# E080^$ApigX diff --git a/applications/external/bomberduck/bomb.png b/applications/external/bomberduck/bomb.png deleted file mode 100644 index 44b9bfdeae76553be52ce1c451133a4baee7bf68..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4534 zcmeHKYj6|S6<%z|5CMh~3MO%!MQ#F^?0fat9m^P72CE<}8sWT0;SYAG!)aM&4dO*36QBnfew$RC4{;rDG78SDFiYN#Jwv&HO)-N z)AG-nk#zUm?|k>1@0@#Q??%s(g%eE=nh*q;=qhoR!+&FJ8kY`#o9I&q;or4dZ-rhi z#L$SQ%1RKRdQAkNAg;&=5+SdGG+Oq zj?BQ7b)PkKHl)qI9nkMC+j;2q{r;QBzP_>NX8O53u?cgg)_sN`W1drriaf5OqJhj| zzFXI|I7*HbWF1>`=DXj^&8VJS*5!HmAKhbnDqi(8nA(>G&K>^f^rRiGqSl_C*>6tE zx>K=-f75>TN_XzK^^c%?@gYAB9E1UUb{n6%CTb}&ErB!-G_lrW#=509{4{SPSec^+jv`uJ>H`;&p z%7Y(|U%utrCs%Ggr?pwGcdf~Muk!e!RUO=hv}yY`Om+VL6<;c-*l_w6Ek3jRtF@DQ zpSe`pvgon*(=)lhm(`oPK7Y3N@b&PCcRp%(X=-EY!^9hRsg$%>{ely39BK}|wKnC; z?e%ki`Elpj`WH)2S7Ml!b~Bth{e8@RF%^$J^YyGJ>F+H3=b5Rd`?~M`=02|_>@(0C{qj`DGbIl;@rv0*06Xab^FI?NSr4_c&Z0A+C&C$ZwV zQcaulO=zAb$$YtUK|ck2a+m|U9^nZh7K`CA23IvdL2(>MkTgNl7(`&vny@a!v2ZlU zpy=apf~cq|5nWNksKF`t)GFO!Hp6*zAihwh^?6AZ z6^Ty_S)b1)3KkaQD3-=po6U-GtObsIc1e^ig3ZSY!>C;0s4j#>U{FDFT!B1Ri4p7; zn+;<~ABnLfDPf$QBQdL;v&f`iu}Dc2rJ4e}QV0%@%Ak@Um7U^z9L-rUpPhzOoYjg6 zl3l<6u*kG53RY2O646K^zd+SO0xYKz68wOOg#C#LLvVh+$K^27I5}kT1O;7&4i58T zC0rFBn(!(iuv8ZeH7To|wp$s7qDk7qaMTcJ8PKAz6Aey^#2Gd*W0Zx5$v|NRqfa40 z!VYuci!>nUs^(SIpu=p0gc_cM({8w(BtaLPf({@lNi#gj@(k&vY&=QvEH{^gmSK2R zQskQdhBh`2n%7tI5+w@PuSuBtcGOa^w(qI$F{mWA5{f1^1uuwwDMW>8ASL32Tzykw zKnVK*JU#l`b-=ECLn~0SM9Vgo!F)av_K?iL`Y|$tSt(hDvifL1*^=3#s;tKZ4b1mL z9idjRJri1?vlE4ylZ=i9fKde~7)G)f&3S2xhvLyxzhK0@Pthte5^LjF4A^V{W342I z`6Q9W7>*_d!AAKc%90%P|B~WhPgv;W6!Qq2J4txB{deU1UW1j>4ts8dLNDYP|hEQJ*m#g7RI?`QFSkP0jO7NWr)i=2q{T>eBHuH(vc!dPhO) z7cJG}kr`>8-0$DFdUNQ)ipMUv6x9pvoL~QvcJld^0sOV5X%F#bOaBF#5LfXM=ib7n G*8dyMBTMf9 diff --git a/applications/external/bomberduck/bomberduck.c b/applications/external/bomberduck/bomberduck.c deleted file mode 100644 index 3e9d52f56..000000000 --- a/applications/external/bomberduck/bomberduck.c +++ /dev/null @@ -1,647 +0,0 @@ -#include -#include - -#include -#include -#include -#include -#include "bomberduck_icons.h" -#include - -int max(int a, int b) { - return (a > b) ? a : b; -} - -int min(int a, int b) { - return (a < b) ? a : b; -} - -#define WorldSizeX 12 -#define WorldSizeY 6 -#define BombRange 1 - -typedef struct { - FuriMutex* mutex; -} BomberState; - -typedef struct { - int row; - int col; -} Cell; - -typedef struct { - Cell cells[WorldSizeY * WorldSizeX]; - int front; - int rear; -} Queue; - -void enqueue(Queue* q, Cell c) { - q->cells[q->rear] = c; - q->rear++; -} - -Cell dequeue(Queue* q) { - Cell c = q->cells[q->front]; - q->front++; - - return c; -} - -bool is_empty(Queue* q) { - return q->front == q->rear; -} - -typedef struct { - int x; - int y; - int planted; -} Bomb; - -typedef struct { - int x; - int y; - bool side; -} Player; - -typedef struct { - int x; - int y; - int last; - bool side; - int level; -} Enemy; - -typedef struct { - int matrix[WorldSizeY][WorldSizeX]; - Player* player; - bool running; - int level; - - Enemy enemies[10]; - int enemies_count; - - Bomb bombs[100]; - int bombs_count; - - int endx; - int endy; -} World; - -Player player = {0, 0, 1}; -World world = {{{0}}, &player, 1, 0, {}, 0, {}, 0, 0, 0}; -bool vibration = false; - -void init() { - player.x = 1; - player.y = 1; - - world.endx = 4 + rand() % 8; - world.endy = rand() % 6; - for(int i = 0; i < WorldSizeY; i++) { - for(int j = 0; j < WorldSizeX; j++) { - world.matrix[i][j] = rand() % 3; - } - } - world.running = 1; - world.bombs_count = 0; - vibration = false; - for(int j = max(0, player.y - BombRange); j < min(WorldSizeY, player.y + BombRange + 1); j++) { - world.matrix[j][player.x] = 0; - } - - for(int j = max(0, player.x - BombRange); j < min(WorldSizeX, player.x + BombRange + 1); j++) { - world.matrix[player.y][j] = 0; - } - - world.enemies_count = 0; - for(int j = 0; j < rand() % 4 + world.level / 5; j++) { - Enemy enemy; - enemy.x = 4 + rand() % 7; - enemy.y = rand() % 6; - enemy.last = 0; - enemy.side = 1; - enemy.level = 0; - - world.enemies[j] = enemy; - world.enemies_count++; - - for(int m = max(0, world.enemies[j].y - BombRange); - m < min(WorldSizeY, world.enemies[j].y + BombRange + 1); - m++) { - world.matrix[m][world.enemies[j].x] = 0; - } - - for(int m = max(0, world.enemies[j].x - BombRange); - m < min(WorldSizeX, world.enemies[j].x + BombRange + 1); - m++) { - world.matrix[world.enemies[j].y][m] = 0; - } - } - world.matrix[world.endy][world.endx] = 1; -} - -const NotificationSequence end = { - &message_vibro_on, - - &message_note_ds4, - &message_delay_10, - &message_sound_off, - &message_delay_10, - - &message_note_ds4, - &message_delay_10, - &message_sound_off, - &message_delay_10, - - &message_note_ds4, - &message_delay_10, - &message_sound_off, - &message_delay_10, - - &message_vibro_off, - NULL, -}; - -static const NotificationSequence bomb2 = { - &message_vibro_on, - &message_delay_25, - &message_vibro_off, - NULL, -}; - -static const NotificationSequence bomb_explore = { - &message_vibro_on, - &message_delay_50, - &message_vibro_off, - NULL, -}; - -static const NotificationSequence vibr1 = { - &message_vibro_on, - &message_delay_10, - &message_vibro_off, - &message_delay_10, - &message_vibro_on, - &message_delay_10, - &message_vibro_off, - &message_delay_10, - - NULL, -}; - -void intToStr(int num, char* str) { - int i = 0, sign = 0; - - if(num < 0) { - num = -num; - sign = 1; - } - - do { - str[i++] = num % 10 + '0'; - num /= 10; - } while(num > 0); - - if(sign) { - str[i++] = '-'; - } - - str[i] = '\0'; - - // Reverse the string - int j, len = i; - char temp; - for(j = 0; j < len / 2; j++) { - temp = str[j]; - str[j] = str[len - j - 1]; - str[len - j - 1] = temp; - } -} - -bool BFS() { - // Initialize visited array and queue - int visited[WorldSizeY][WorldSizeX] = {0}; - Queue q = {.front = 0, .rear = 0}; - // Mark the starting cell as visited and enqueue it - visited[world.player->y][world.player->x] = 1; - Cell startCell = {.row = world.player->y, .col = world.player->x}; - enqueue(&q, startCell); - // Traverse the field - while(!is_empty(&q)) { - // Dequeue a cell from the queue - Cell currentCell = dequeue(&q); - // Check if the current cell is the destination cell - if(currentCell.row == world.endy && currentCell.col == world.endx) { - return true; - } - // Check the neighboring cells - for(int rowOffset = -1; rowOffset <= 1; rowOffset++) { - for(int colOffset = -1; colOffset <= 1; colOffset++) { - // Skip diagonals and the current cell - if(rowOffset == 0 && colOffset == 0) { - continue; - } - if(rowOffset != 0 && colOffset != 0) { - continue; - } - // Calculate the row and column of the neighboring cell - int neighborRow = currentCell.row + rowOffset; - int neighborCol = currentCell.col + colOffset; - // Skip out-of-bounds cells and already visited cells - if(neighborRow < 0 || neighborRow >= WorldSizeY || neighborCol < 0 || - neighborCol >= WorldSizeX) { - continue; - } - if(visited[neighborRow][neighborCol]) { - continue; - } - // Mark the neighboring cell as visited and enqueue it - if(world.matrix[neighborRow][neighborCol] != 2) { - visited[neighborRow][neighborCol] = 1; - Cell neighborCell = {.row = neighborRow, .col = neighborCol}; - enqueue(&q, neighborCell); - } - } - } - } - return false; -} - -static void draw_callback(Canvas* canvas, void* ctx) { - furi_assert(ctx); - const BomberState* bomber_state = ctx; - - furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); - if(!BFS()) { - init(); - } - canvas_clear(canvas); - - canvas_draw_icon(canvas, world.endx * 10 + 4, world.endy * 10 + 2, &I_end); - - if(world.running) { - for(size_t i = 0; i < WorldSizeY; i++) { - for(size_t j = 0; j < WorldSizeX; j++) { - switch(world.matrix[i][j]) { - case 0: - break; - case 1: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_box); - break; - case 2: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_unbreakbox); - break; - case 3: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb0); - break; - case 4: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb1); - break; - case 5: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_bomb2); - break; - case 6: - canvas_draw_icon(canvas, j * 10 + 4, i * 10 + 2, &I_explore); - world.matrix[i][j] = 0; - break; - } - } - } - - if(world.player->side) { - canvas_draw_icon( - canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerright); - } else { - canvas_draw_icon( - canvas, world.player->x * 10 + 4, world.player->y * 10 + 2, &I_playerleft); - } - - for(int i = 0; i < world.enemies_count; i++) { - if(world.enemies[i].level > 0) { - canvas_draw_icon( - canvas, world.enemies[i].x * 10 + 4, world.enemies[i].y * 10 + 2, &I_enemy1); - } else { - if(world.enemies[i].side) { - canvas_draw_icon( - canvas, - world.enemies[i].x * 10 + 4, - world.enemies[i].y * 10 + 2, - &I_enemyright); - } else { - canvas_draw_icon( - canvas, - world.enemies[i].x * 10 + 4, - world.enemies[i].y * 10 + 2, - &I_enemyleft); - } - } - } - } else { - canvas_set_font(canvas, FontPrimary); - if(world.player->x == world.endx && world.player->y == world.endy) { - if(world.level == 20) { - canvas_draw_str(canvas, 30, 35, "You win!"); - } else { - canvas_draw_str(canvas, 30, 35, "Next level!"); - char str[20]; - intToStr(world.level, str); - canvas_draw_str(canvas, 90, 35, str); - } - - } else { - canvas_draw_str(canvas, 30, 35, "You died :("); - } - } - - furi_mutex_release(bomber_state->mutex); -} - -static void input_callback(InputEvent* input_event, void* ctx) { - // Проверяем, что контекст не нулевой - furi_assert(ctx); - FuriMessageQueue* event_queue = ctx; - - furi_message_queue_put(event_queue, input_event, FuriWaitForever); -} - -int32_t bomberduck_app(void* p) { - UNUSED(p); - - // Текущее событие типа InputEvent - InputEvent event; - // Очередь событий на 8 элементов размера InputEvent - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - - BomberState* bomber_state = malloc(sizeof(BomberState)); - - bomber_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); // Alloc Mutex - if(!bomber_state->mutex) { - FURI_LOG_E("BomberDuck", "cannot create mutex\r\n"); - furi_message_queue_free(event_queue); - free(bomber_state); - return 255; - } - - dolphin_deed(DolphinDeedPluginGameStart); - // Создаем новый view port - ViewPort* view_port = view_port_alloc(); - // Создаем callback отрисовки, без контекста - view_port_draw_callback_set(view_port, draw_callback, bomber_state); - // Создаем callback нажатий на клавиши, в качестве контекста передаем - // нашу очередь сообщений, чтоб запихивать в неё эти события - view_port_input_callback_set(view_port, input_callback, event_queue); - - // Создаем GUI приложения - Gui* gui = furi_record_open(RECORD_GUI); - // Подключаем view port к GUI в полноэкранном режиме - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - notification_message_block(notification, &sequence_display_backlight_enforce_on); - - init(); - - // Бесконечный цикл обработки очереди событий - while(1) { - if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) { - furi_mutex_acquire(bomber_state->mutex, FuriWaitForever); - // Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения - - if(event.type == InputTypePress) { - if(event.key == InputKeyOk) { - if(world.running) { - if(world.matrix[world.player->y][world.player->x] == 0 && - world.bombs_count < 2) { - notification_message(notification, &bomb2); - world.matrix[world.player->y][world.player->x] = 3; - Bomb bomb = {world.player->x, world.player->y, furi_get_tick()}; - world.bombs[world.bombs_count] = bomb; - world.bombs_count++; - } - } else { - init(); - } - } - if(world.running) { - if(event.key == InputKeyUp) { - if(world.player->y > 0 && - world.matrix[world.player->y - 1][world.player->x] == 0) - world.player->y--; - } - if(event.key == InputKeyDown) { - if(world.player->y < WorldSizeY - 1 && - world.matrix[world.player->y + 1][world.player->x] == 0) - world.player->y++; - } - if(event.key == InputKeyLeft) { - world.player->side = 0; - if(world.player->x > 0 && - world.matrix[world.player->y][world.player->x - 1] == 0) - world.player->x--; - } - if(event.key == InputKeyRight) { - world.player->side = 1; - if(world.player->x < WorldSizeX - 1 && - world.matrix[world.player->y][world.player->x + 1] == 0) - world.player->x++; - } - } - } else if(event.type == InputTypeLong) { - if(event.key == InputKeyBack) { - break; - } - } - } - if(world.running) { - if(world.player->x == world.endx && world.player->y == world.endy) { - notification_message(notification, &end); - world.running = 0; - world.level += 1; - if(world.level % 5 == 0) { - dolphin_deed(DolphinDeedPluginGameWin); - } - } - for(int i = 0; i < world.bombs_count; i++) { - if(furi_get_tick() - world.bombs[i].planted > - (unsigned long)max((3000 - world.level * 150), 1000)) { - vibration = false; - world.matrix[world.bombs[i].y][world.bombs[i].x] = 6; - notification_message(notification, &bomb_explore); - - for(int j = max(0, world.bombs[i].y - BombRange); - j < min(WorldSizeY, world.bombs[i].y + BombRange + 1); - j++) { - if(world.matrix[j][world.bombs[i].x] != 2) { - world.matrix[j][world.bombs[i].x] = 6; - if(j == world.player->y && world.bombs[i].x == world.player->x) { - notification_message(notification, &end); - world.running = 0; - } - for(int e = 0; e < world.enemies_count; e++) { - if(j == world.enemies[e].y && - world.bombs[i].x == world.enemies[e].x) { - if(world.enemies[e].level > 0) { - world.enemies[e].level--; - } else { - for(int l = e; l < world.enemies_count - 1; l++) { - world.enemies[l] = world.enemies[l + 1]; - } - world.enemies_count--; - } - } - } - } - } - - for(int j = max(0, world.bombs[i].x - BombRange); - j < min(WorldSizeX, world.bombs[i].x + BombRange + 1); - j++) { - if(world.matrix[world.bombs[i].y][j] != 2) { - world.matrix[world.bombs[i].y][j] = 6; - if(world.bombs[i].y == world.player->y && j == world.player->x) { - notification_message(notification, &end); - world.running = 0; - } - for(int e = 0; e < world.enemies_count; e++) { - if(world.bombs[i].y == world.enemies[e].y && - j == world.enemies[e].x) { - if(world.enemies[e].level > 0) { - world.enemies[e].level--; - } else { - for(int l = e; l < world.enemies_count - 1; l++) { - world.enemies[l] = world.enemies[l + 1]; - } - world.enemies_count--; - } - } - } - } - } - - for(int j = i; j < world.bombs_count - 1; j++) { - world.bombs[j] = world.bombs[j + 1]; - } - world.bombs_count--; - } else if( - furi_get_tick() - world.bombs[i].planted > - (unsigned long)max((3000 - world.level * 150) * 2 / 3, 666) && - world.matrix[world.bombs[i].y][world.bombs[i].x] != 5) { - world.matrix[world.bombs[i].y][world.bombs[i].x] = 5; - vibration = true; - - } else if( - furi_get_tick() - world.bombs[i].planted > - (unsigned long)max((3000 - world.level * 150) / 3, 333) && - world.matrix[world.bombs[i].y][world.bombs[i].x] != 4) { - world.matrix[world.bombs[i].y][world.bombs[i].x] = 4; - } - } - for(int e = 0; e < world.enemies_count; e++) { - if(world.player->y == world.enemies[e].y && - world.player->x == world.enemies[e].x) { - notification_message(notification, &end); - world.running = 0; - } - } - - for(int e = 0; e < world.enemies_count; e++) { - if(world.enemies[e].level > 0) { - if(furi_get_tick() - world.enemies[e].last > - (unsigned long)max((2000 - world.level * 100), 1000)) { - world.enemies[e].last = furi_get_tick(); - int move = rand() % 4; - switch(move) { - case 0: - if(world.enemies[e].y > 0 && - world.matrix[world.enemies[e].y - 1][world.enemies[e].x] != 2) - world.enemies[e].y--; - break; - case 1: - if(world.enemies[e].y < WorldSizeY - 1 && - world.matrix[world.enemies[e].y + 1][world.enemies[e].x] != 2) - world.enemies[e].y++; - break; - case 2: - world.enemies[e].side = 0; - if(world.enemies[e].x > 0 && - world.matrix[world.enemies[e].y][world.enemies[e].x - 1] != 2) - world.enemies[e].x--; - break; - case 3: - world.enemies[e].side = 1; - if(world.enemies[e].x < WorldSizeX - 1 && - world.matrix[world.enemies[e].y][world.enemies[e].x + 1] != 2) - world.enemies[e].x++; - default: - break; - } - } - } else { - if(furi_get_tick() - world.enemies[e].last > - (unsigned long)max((1000 - world.level * 50), 500)) { - world.enemies[e].last = furi_get_tick(); - int move = rand() % 4; - switch(move) { - case 0: - if(world.enemies[e].y > 0 && - world.matrix[world.enemies[e].y - 1][world.enemies[e].x] == 0) - world.enemies[e].y--; - break; - case 1: - if(world.enemies[e].y < WorldSizeY - 1 && - world.matrix[world.enemies[e].y + 1][world.enemies[e].x] == 0) - world.enemies[e].y++; - break; - case 2: - world.enemies[e].side = 0; - if(world.enemies[e].x > 0 && - world.matrix[world.enemies[e].y][world.enemies[e].x - 1] == 0) - world.enemies[e].x--; - break; - case 3: - world.enemies[e].side = 1; - if(world.enemies[e].x < WorldSizeX - 1 && - world.matrix[world.enemies[e].y][world.enemies[e].x + 1] == 0) - world.enemies[e].x++; - default: - break; - } - } - } - } - for(int e = 0; e < world.enemies_count; e++) { - for(int h = e + 1; h < world.enemies_count; h++) { - if(world.enemies[e].y == world.enemies[h].y && - world.enemies[e].x == world.enemies[h].x) { - world.enemies[h].level++; - for(int l = e; l < world.enemies_count - 1; l++) { - world.enemies[l] = world.enemies[l + 1]; - } - world.enemies_count--; - } - } - } - if(vibration) { - notification_message(notification, &vibr1); - } - } - - view_port_update(view_port); - furi_mutex_release(bomber_state->mutex); - } - - // Return to normal backlight settings - notification_message_block(notification, &sequence_display_backlight_enforce_auto); - furi_record_close(RECORD_NOTIFICATION); - // Специальная очистка памяти, занимаемой очередью - furi_message_queue_free(event_queue); - - // Чистим созданные объекты, связанные с интерфейсом - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - - furi_mutex_free(bomber_state->mutex); - furi_record_close(RECORD_GUI); - free(bomber_state); - - return 0; -} diff --git a/applications/external/camera_suite/application.fam b/applications/external/camera_suite/application.fam deleted file mode 100644 index 63687833e..000000000 --- a/applications/external/camera_suite/application.fam +++ /dev/null @@ -1,16 +0,0 @@ -App( - appid="camera_suite", - apptype=FlipperAppType.EXTERNAL, - cdefines=["APP_CAMERA_SUITE"], - entry_point="camera_suite_app", - fap_author="@CodyTolene @Z4urce @leedave", - fap_category="GPIO", - fap_description="A camera suite application for the Flipper Zero ESP32-CAM module.", - fap_icon="icons/camera_suite.png", - fap_libs=["assets"], - fap_weburl="https://github.com/CodyTolene/Flipper-Zero-Cam", - name="[ESP32] Camera Suite", - order=1, - requires=["gui", "storage"], - stack_size=8 * 1024, -) diff --git a/applications/external/camera_suite/camera_suite.c b/applications/external/camera_suite/camera_suite.c deleted file mode 100644 index cbe7e3d62..000000000 --- a/applications/external/camera_suite/camera_suite.c +++ /dev/null @@ -1,132 +0,0 @@ -#include "camera_suite.h" -#include - -bool camera_suite_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - CameraSuite* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -void camera_suite_tick_event_callback(void* context) { - furi_assert(context); - CameraSuite* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -// Leave app if back button pressed. -bool camera_suite_navigation_event_callback(void* context) { - furi_assert(context); - CameraSuite* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -CameraSuite* camera_suite_app_alloc() { - CameraSuite* app = malloc(sizeof(CameraSuite)); - app->gui = furi_record_open(RECORD_GUI); - app->notification = furi_record_open(RECORD_NOTIFICATION); - - // Turn backlight on. - notification_message(app->notification, &sequence_display_backlight_on); - - // Scene additions - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - - app->scene_manager = scene_manager_alloc(&camera_suite_scene_handlers, app); - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, camera_suite_navigation_event_callback); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, camera_suite_tick_event_callback, 100); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, camera_suite_custom_event_callback); - app->submenu = submenu_alloc(); - - // Set defaults, in case no config loaded - app->orientation = 0; // Orientation is "portrait", zero degrees by default. - app->haptic = 1; // Haptic is on by default - app->speaker = 1; // Speaker is on by default - app->led = 1; // LED is on by default - - // Load configs - camera_suite_read_settings(app); - - view_dispatcher_add_view( - app->view_dispatcher, CameraSuiteViewIdMenu, submenu_get_view(app->submenu)); - - app->camera_suite_view_start = camera_suite_view_start_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - CameraSuiteViewIdStartscreen, - camera_suite_view_start_get_view(app->camera_suite_view_start)); - - app->camera_suite_view_camera = camera_suite_view_camera_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - CameraSuiteViewIdCamera, - camera_suite_view_camera_get_view(app->camera_suite_view_camera)); - - app->camera_suite_view_guide = camera_suite_view_guide_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - CameraSuiteViewIdGuide, - camera_suite_view_guide_get_view(app->camera_suite_view_guide)); - - app->button_menu = button_menu_alloc(); - - app->variable_item_list = variable_item_list_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, - CameraSuiteViewIdSettings, - variable_item_list_get_view(app->variable_item_list)); - - //End Scene Additions - - return app; -} - -void camera_suite_app_free(CameraSuite* app) { - furi_assert(app); - - // Scene manager - scene_manager_free(app->scene_manager); - - // View Dispatcher - view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdStartscreen); - view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdMenu); - view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdCamera); - view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdGuide); - view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdSettings); - submenu_free(app->submenu); - - view_dispatcher_free(app->view_dispatcher); - furi_record_close(RECORD_GUI); - - // Free remaining resources - camera_suite_view_start_free(app->camera_suite_view_start); - camera_suite_view_camera_free(app->camera_suite_view_camera); - camera_suite_view_guide_free(app->camera_suite_view_guide); - button_menu_free(app->button_menu); - variable_item_list_free(app->variable_item_list); - - app->gui = NULL; - app->notification = NULL; - - //Remove whatever is left - free(app); -} - -/** Main entry point for initialization. */ -int32_t camera_suite_app(void* p) { - UNUSED(p); - CameraSuite* app = camera_suite_app_alloc(); - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - // Init with start scene. - scene_manager_next_scene(app->scene_manager, CameraSuiteSceneStart); - furi_hal_power_suppress_charge_enter(); - view_dispatcher_run(app->view_dispatcher); - camera_suite_save_settings(app); - furi_hal_power_suppress_charge_exit(); - camera_suite_app_free(app); - return 0; -} diff --git a/applications/external/camera_suite/camera_suite.h b/applications/external/camera_suite/camera_suite.h deleted file mode 100644 index a8b9825be..000000000 --- a/applications/external/camera_suite/camera_suite.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "helpers/camera_suite_storage.h" -#include "scenes/camera_suite_scene.h" -#include "views/camera_suite_view_guide.h" -#include "views/camera_suite_view_start.h" -#include "views/camera_suite_view_camera.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG "Camera Suite" - -typedef struct { - Gui* gui; - NotificationApp* notification; - ViewDispatcher* view_dispatcher; - Submenu* submenu; - SceneManager* scene_manager; - VariableItemList* variable_item_list; - CameraSuiteViewStart* camera_suite_view_start; - CameraSuiteViewCamera* camera_suite_view_camera; - CameraSuiteViewGuide* camera_suite_view_guide; - uint32_t orientation; - uint32_t haptic; - uint32_t speaker; - uint32_t led; - ButtonMenu* button_menu; -} CameraSuite; - -typedef enum { - CameraSuiteViewIdStartscreen, - CameraSuiteViewIdMenu, - CameraSuiteViewIdCamera, - CameraSuiteViewIdGuide, - CameraSuiteViewIdSettings, -} CameraSuiteViewId; - -typedef enum { - CameraSuiteOrientation0, - CameraSuiteOrientation90, - CameraSuiteOrientation180, - CameraSuiteOrientation270, -} CameraSuiteOrientationState; - -typedef enum { - CameraSuiteHapticOff, - CameraSuiteHapticOn, -} CameraSuiteHapticState; - -typedef enum { - CameraSuiteSpeakerOff, - CameraSuiteSpeakerOn, -} CameraSuiteSpeakerState; - -typedef enum { - CameraSuiteLedOff, - CameraSuiteLedOn, -} CameraSuiteLedState; diff --git a/applications/external/camera_suite/docs/CHANGELOG.md b/applications/external/camera_suite/docs/CHANGELOG.md deleted file mode 100644 index 40f20dfc4..000000000 --- a/applications/external/camera_suite/docs/CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -## v1.1 - -- Support and picture stabilization for all camera orientations (0°, 90°, 180°, 270°). -- Rename "Scene 1" to "Camera". No UX changes, strictly internal. -- Clean up unused "Scene 2". This was inaccessible to users previously and unused. -- Add new dithering variations (needs new module firmware, see https://github.com/CodyTolene/Flipper-Zero-Camera-Suite#firmware-installation): - - Add `Jarvis Judice` Ninke Dithering option - - Add `Stucki` dithering option. - - Add ability to toggle dithering options from default `Floyd-Steinberg` and back. -- Resolves issue https://github.com/CodyTolene/Flipper-Zero-Camera-Suite/issues/7 -- Resolves issue https://github.com/CodyTolene/Flipper-Zero-Camera-Suite/pull/17 - -## v1.0 - -- Builds upon Z4urce's software found here (updated 6 months ago): https://github.com/Z4urce/flipperzero-camera -- Utilizes the superb C boilerplate examples laid out by leedave (updated last month): https://github.com/leedave/flipper-zero-fap-boilerplate -- Repurpose and build upon the "[ESP32] Camera" software into the new "[ESP32] Camera Suite" application with new purpose: - - Adding more scene for a guide. - - Adding more scene for saveable settings. - - Add ability to rotate the camera orientation. diff --git a/applications/external/camera_suite/docs/README.md b/applications/external/camera_suite/docs/README.md deleted file mode 100644 index 1f436a067..000000000 --- a/applications/external/camera_suite/docs/README.md +++ /dev/null @@ -1,35 +0,0 @@ -## Flipper Zero - Camera Suite - -Software to run an ESP32-CAM module on your Flipper Zero device. - -## Software Guide - -### Flipper Zero button mappings: - -🔼 = Contrast Up - -🔽 = Contrast Down - -◀️ = Toggle invert. - -▶️ = Toggle dithering on/off. - -⚪ = Cycle Floyd–Steinberg/Jarvis-Judice-Ninke/Stucki dithering types. - -↩️ = Go back. - -### Camera Suite settings: - -**Orientation** = Rotate the camera image 90 degrees counter-clockwise starting at zero by default (0, 90, 180, 270). This is useful if you have your camera module mounted in a different orientation than the default. - -**Haptic FX** = Toggle haptic feedback on/off. - -**Sound FX** = Toggle sound effects on/off. - -**LED FX** = Toggle LED effects on/off. - -## Links - -Full setup, wiring guide, etc. in the main project README here: https://github.com/CodyTolene/Flipper-Zero-Camera-Suite - -A firmware is needed for the ESP32-CAM module, see here for more information: https://github.com/CodyTolene/Flipper-Zero-Camera-Suite#firmware-installation diff --git a/applications/external/camera_suite/helpers/camera_suite_custom_event.h b/applications/external/camera_suite/helpers/camera_suite_custom_event.h deleted file mode 100644 index 4d472d577..000000000 --- a/applications/external/camera_suite/helpers/camera_suite_custom_event.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -typedef enum { - // Scene events: Start menu - CameraSuiteCustomEventStartUp, - CameraSuiteCustomEventStartDown, - CameraSuiteCustomEventStartLeft, - CameraSuiteCustomEventStartRight, - CameraSuiteCustomEventStartOk, - CameraSuiteCustomEventStartBack, - // Scene events: Camera - CameraSuiteCustomEventSceneCameraUp, - CameraSuiteCustomEventSceneCameraDown, - CameraSuiteCustomEventSceneCameraLeft, - CameraSuiteCustomEventSceneCameraRight, - CameraSuiteCustomEventSceneCameraOk, - CameraSuiteCustomEventSceneCameraBack, - // Scene events: Guide - CameraSuiteCustomEventSceneGuideUp, - CameraSuiteCustomEventSceneGuideDown, - CameraSuiteCustomEventSceneGuideLeft, - CameraSuiteCustomEventSceneGuideRight, - CameraSuiteCustomEventSceneGuideOk, - CameraSuiteCustomEventSceneGuideBack, -} CameraSuiteCustomEvent; - -enum CameraSuiteCustomEventType { - // Reserve first 100 events for button types and indexes, starting from 0. - CameraSuiteCustomEventMenuVoid, - CameraSuiteCustomEventMenuSelected, -}; - -#pragma pack(push, 1) - -typedef union { - uint32_t packed_value; - struct { - uint16_t type; - int16_t value; - } content; -} CameraSuiteCustomEventMenu; - -#pragma pack(pop) - -static inline uint32_t camera_suite_custom_menu_event_pack(uint16_t type, int16_t value) { - CameraSuiteCustomEventMenu event = {.content = {.type = type, .value = value}}; - return event.packed_value; -} - -static inline void - camera_suite_custom_menu_event_unpack(uint32_t packed_value, uint16_t* type, int16_t* value) { - CameraSuiteCustomEventMenu event = {.packed_value = packed_value}; - if(type) *type = event.content.type; - if(value) *value = event.content.value; -} - -static inline uint16_t camera_suite_custom_menu_event_get_type(uint32_t packed_value) { - uint16_t type; - camera_suite_custom_menu_event_unpack(packed_value, &type, NULL); - return type; -} - -static inline int16_t camera_suite_custom_menu_event_get_value(uint32_t packed_value) { - int16_t value; - camera_suite_custom_menu_event_unpack(packed_value, NULL, &value); - return value; -} diff --git a/applications/external/camera_suite/helpers/camera_suite_haptic.c b/applications/external/camera_suite/helpers/camera_suite_haptic.c deleted file mode 100644 index 237a96004..000000000 --- a/applications/external/camera_suite/helpers/camera_suite_haptic.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "camera_suite_haptic.h" -#include "../camera_suite.h" - -void camera_suite_play_happy_bump(void* context) { - CameraSuite* app = context; - if(app->haptic != 1) { - return; - } - notification_message(app->notification, &sequence_set_vibro_on); - furi_thread_flags_wait(0, FuriFlagWaitAny, 20); - notification_message(app->notification, &sequence_reset_vibro); -} - -void camera_suite_play_bad_bump(void* context) { - CameraSuite* app = context; - if(app->haptic != 1) { - return; - } - notification_message(app->notification, &sequence_set_vibro_on); - furi_thread_flags_wait(0, FuriFlagWaitAny, 100); - notification_message(app->notification, &sequence_reset_vibro); -} - -void camera_suite_play_long_bump(void* context) { - CameraSuite* app = context; - if(app->haptic != 1) { - return; - } - for(int i = 0; i < 4; i++) { - notification_message(app->notification, &sequence_set_vibro_on); - furi_thread_flags_wait(0, FuriFlagWaitAny, 50); - notification_message(app->notification, &sequence_reset_vibro); - furi_thread_flags_wait(0, FuriFlagWaitAny, 100); - } -} diff --git a/applications/external/camera_suite/helpers/camera_suite_haptic.h b/applications/external/camera_suite/helpers/camera_suite_haptic.h deleted file mode 100644 index 9b7651f97..000000000 --- a/applications/external/camera_suite/helpers/camera_suite_haptic.h +++ /dev/null @@ -1,7 +0,0 @@ -#include - -void camera_suite_play_happy_bump(void* context); - -void camera_suite_play_bad_bump(void* context); - -void camera_suite_play_long_bump(void* context); diff --git a/applications/external/camera_suite/helpers/camera_suite_led.c b/applications/external/camera_suite/helpers/camera_suite_led.c deleted file mode 100644 index c4f1a85d7..000000000 --- a/applications/external/camera_suite/helpers/camera_suite_led.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "camera_suite_led.h" -#include "../camera_suite.h" - -void camera_suite_led_set_rgb(void* context, int red, int green, int blue) { - CameraSuite* app = context; - if(app->led != 1) { - return; - } - NotificationMessage notification_led_message_1; - notification_led_message_1.type = NotificationMessageTypeLedRed; - NotificationMessage notification_led_message_2; - notification_led_message_2.type = NotificationMessageTypeLedGreen; - NotificationMessage notification_led_message_3; - notification_led_message_3.type = NotificationMessageTypeLedBlue; - - notification_led_message_1.data.led.value = red; - notification_led_message_2.data.led.value = green; - notification_led_message_3.data.led.value = blue; - const NotificationSequence notification_sequence = { - ¬ification_led_message_1, - ¬ification_led_message_2, - ¬ification_led_message_3, - &message_do_not_reset, - NULL, - }; - notification_message(app->notification, ¬ification_sequence); - //Delay, prevent removal from RAM before LED value set. - furi_thread_flags_wait(0, FuriFlagWaitAny, 10); -} - -void camera_suite_led_reset(void* context) { - CameraSuite* app = context; - notification_message(app->notification, &sequence_reset_red); - notification_message(app->notification, &sequence_reset_green); - notification_message(app->notification, &sequence_reset_blue); - //Delay, prevent removal from RAM before LED value set. - furi_thread_flags_wait(0, FuriFlagWaitAny, 300); -} diff --git a/applications/external/camera_suite/helpers/camera_suite_led.h b/applications/external/camera_suite/helpers/camera_suite_led.h deleted file mode 100644 index 074947da1..000000000 --- a/applications/external/camera_suite/helpers/camera_suite_led.h +++ /dev/null @@ -1,3 +0,0 @@ -void camera_suite_led_set_rgb(void* context, int red, int green, int blue); - -void camera_suite_led_reset(void* context); diff --git a/applications/external/camera_suite/helpers/camera_suite_speaker.c b/applications/external/camera_suite/helpers/camera_suite_speaker.c deleted file mode 100644 index c2a5a7dd0..000000000 --- a/applications/external/camera_suite/helpers/camera_suite_speaker.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "camera_suite_speaker.h" -#include "../camera_suite.h" - -#define NOTE_INPUT 587.33f - -void camera_suite_play_input_sound(void* context) { - CameraSuite* app = context; - if(app->speaker != 1) { - return; - } - float volume = 1.0f; - if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { - furi_hal_speaker_start(NOTE_INPUT, volume); - } -} - -void camera_suite_stop_all_sound(void* context) { - CameraSuite* app = context; - if(app->speaker != 1) { - return; - } - if(furi_hal_speaker_is_mine()) { - furi_hal_speaker_stop(); - furi_hal_speaker_release(); - } -} diff --git a/applications/external/camera_suite/helpers/camera_suite_speaker.h b/applications/external/camera_suite/helpers/camera_suite_speaker.h deleted file mode 100644 index 2119bbec5..000000000 --- a/applications/external/camera_suite/helpers/camera_suite_speaker.h +++ /dev/null @@ -1,5 +0,0 @@ -#define NOTE_INPUT 587.33f - -void camera_suite_play_input_sound(void* context); - -void camera_suite_stop_all_sound(void* context); diff --git a/applications/external/camera_suite/helpers/camera_suite_storage.c b/applications/external/camera_suite/helpers/camera_suite_storage.c deleted file mode 100644 index 38a5f0813..000000000 --- a/applications/external/camera_suite/helpers/camera_suite_storage.c +++ /dev/null @@ -1,113 +0,0 @@ -#include "camera_suite_storage.h" - -static Storage* camera_suite_open_storage() { - return furi_record_open(RECORD_STORAGE); -} - -static void camera_suite_close_storage() { - furi_record_close(RECORD_STORAGE); -} - -static void camera_suite_close_config_file(FlipperFormat* file) { - if(file == NULL) return; - flipper_format_file_close(file); - flipper_format_free(file); -} - -void camera_suite_save_settings(void* context) { - CameraSuite* app = context; - - FURI_LOG_D(TAG, "Saving Settings"); - Storage* storage = camera_suite_open_storage(); - FlipperFormat* fff_file = flipper_format_file_alloc(storage); - - // Overwrite wont work, so delete first - if(storage_file_exists(storage, BOILERPLATE_SETTINGS_SAVE_PATH)) { - storage_simply_remove(storage, BOILERPLATE_SETTINGS_SAVE_PATH); - } - - // Open File, create if not exists - if(!storage_common_stat(storage, BOILERPLATE_SETTINGS_SAVE_PATH, NULL) == FSE_OK) { - FURI_LOG_D( - TAG, "Config file %s is not found. Will create new.", BOILERPLATE_SETTINGS_SAVE_PATH); - if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) { - FURI_LOG_D( - TAG, "Directory %s doesn't exist. Will create new.", CONFIG_FILE_DIRECTORY_PATH); - if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { - FURI_LOG_E(TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); - } - } - } - - if(!flipper_format_file_open_new(fff_file, BOILERPLATE_SETTINGS_SAVE_PATH)) { - //totp_close_config_file(fff_file); - FURI_LOG_E(TAG, "Error creating new file %s", BOILERPLATE_SETTINGS_SAVE_PATH); - camera_suite_close_storage(); - return; - } - - // Store Settings - flipper_format_write_header_cstr( - fff_file, BOILERPLATE_SETTINGS_HEADER, BOILERPLATE_SETTINGS_FILE_VERSION); - flipper_format_write_uint32( - fff_file, BOILERPLATE_SETTINGS_KEY_ORIENTATION, &app->orientation, 1); - flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1); - flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1); - flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1); - - if(!flipper_format_rewind(fff_file)) { - camera_suite_close_config_file(fff_file); - FURI_LOG_E(TAG, "Rewind error"); - camera_suite_close_storage(); - return; - } - - camera_suite_close_config_file(fff_file); - camera_suite_close_storage(); -} - -void camera_suite_read_settings(void* context) { - CameraSuite* app = context; - Storage* storage = camera_suite_open_storage(); - FlipperFormat* fff_file = flipper_format_file_alloc(storage); - - if(storage_common_stat(storage, BOILERPLATE_SETTINGS_SAVE_PATH, NULL) != FSE_OK) { - camera_suite_close_config_file(fff_file); - camera_suite_close_storage(); - return; - } - uint32_t file_version; - FuriString* temp_str = furi_string_alloc(); - - if(!flipper_format_file_open_existing(fff_file, BOILERPLATE_SETTINGS_SAVE_PATH)) { - FURI_LOG_E(TAG, "Cannot open file %s", BOILERPLATE_SETTINGS_SAVE_PATH); - camera_suite_close_config_file(fff_file); - camera_suite_close_storage(); - return; - } - - if(!flipper_format_read_header(fff_file, temp_str, &file_version)) { - FURI_LOG_E(TAG, "Missing Header Data"); - camera_suite_close_config_file(fff_file); - camera_suite_close_storage(); - return; - } - - if(file_version < BOILERPLATE_SETTINGS_FILE_VERSION) { - FURI_LOG_I(TAG, "old config version, will be removed."); - camera_suite_close_config_file(fff_file); - camera_suite_close_storage(); - return; - } - - flipper_format_read_uint32( - fff_file, BOILERPLATE_SETTINGS_KEY_ORIENTATION, &app->orientation, 1); - flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1); - flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1); - flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1); - - flipper_format_rewind(fff_file); - - camera_suite_close_config_file(fff_file); - camera_suite_close_storage(); -} \ No newline at end of file diff --git a/applications/external/camera_suite/helpers/camera_suite_storage.h b/applications/external/camera_suite/helpers/camera_suite_storage.h deleted file mode 100644 index 37e82d722..000000000 --- a/applications/external/camera_suite/helpers/camera_suite_storage.h +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include -#include -#include "../camera_suite.h" - -#define BOILERPLATE_SETTINGS_FILE_VERSION 1 -#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/camera_suite") -#define BOILERPLATE_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/camera_suite.conf" -#define BOILERPLATE_SETTINGS_SAVE_PATH_TMP BOILERPLATE_SETTINGS_SAVE_PATH ".tmp" -#define BOILERPLATE_SETTINGS_HEADER "Camera Suite Config File" -#define BOILERPLATE_SETTINGS_KEY_ORIENTATION "Orientation" -#define BOILERPLATE_SETTINGS_KEY_HAPTIC "Haptic" -#define BOILERPLATE_SETTINGS_KEY_LED "Led" -#define BOILERPLATE_SETTINGS_KEY_SPEAKER "Speaker" -#define BOILERPLATE_SETTINGS_KEY_SAVE_SETTINGS "SaveSettings" - -void camera_suite_save_settings(void* context); - -void camera_suite_read_settings(void* context); diff --git a/applications/external/camera_suite/icons/camera_suite.png b/applications/external/camera_suite/icons/camera_suite.png deleted file mode 100644 index cee545bb999eddfc8fe17d1070a27be46f9f299c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2307 zcma)732YQq7~a~JV!28mK<*_pTP(A}M7X4>7N zfT@6`1l)2ZXpn@%08L5^i71DyLP@lhA_jzT8cdp0+a@R!h!x-LWq}&mWM|&I_xeN(|$u!1MlwStl#yWLmB7C0sJ^Le*DWO5m%PU>zC@w`@HL^#?WFMlH zEzC8U<~6OkV6Q4qn3#OK|Ma{`^872K(HS?>_GC6R>N}jH>o;sGEZXn%RDB{^l?t^5F_1#T} z=k!h$R-LV8zFjVDyNt}dmA+za^BV7j^vsU0TsJlrU3#s2(XGbMz9?Gqc1dmF-$}c? zQx2_^8w>EOO^2#hmS^tV-+Hq5QrDgt-jBP~2Oa(gcV=q=+m&f!*JaNfJMPoaqt530 zvs;gK^(-lScLMfDL;JN;LHmzK27+}T-r39V^d%oiyZqaG>(1qFI(U5g=>C=N|=gM<~#ivKb;=@neOW!xACfawCMi$yO5e!N*6l|;5#`vIVB}!)TmK3 zO@~6EGiT0pcX#*p_V)Gl-Me@1;lqc6gM*JBKW@zkErsY8{Y7tRCKH}v{1bu)vujPJ z5yxa_gp-!F_9$A2y4RyPO`8JorzE+MH3`VP&7tzJY(f)7K#2)Xik;fd|7^B)ENA- zqgA@@=LsSZ2;c!4SJX=8Omme!`5Cgx6~X7Dj7&K`j)Hx-l$TbJqC*Q3f=nMioIp zE%50XF>y(Z%ld0nPdETcB!CBaAxwkT%yCmjW5&@&z?HJsAC`b5#8Ge^2My~^%(O1M z|IObMNaG|NXAJ7EQglVDQvO48{5UQIGsx#FVvP}mBj1i1p7N3+$`T)5vKG?9(vk(U zxET?%m?;snQ38j#NZ@v{lFhj79=Lb(fIQ1yp%M^&UuAgL{p_wm5}#A*u%i@CqDc6&)FbEy6gHFWlazoK z-FM1fP_7FGVP-3988c_0%rVKOK-FNG8p_NhPDOIU=cz!@71gOI9y=Ny*T!); z-pCoq7jyuHU^ihTOcLv)NSR zDN=$&k|Io8L|#+edO%P?P9+Q!x(v%3F*rcxMH!wSLk6mVL0^~&3NzabCP*`HW>Dg} zd>_u!=d1pRr$g<-N_dvuVFQZH>e5)-Ba{WVJ{#MRyc)p}#xRX=LOe}Z_-x4FjltnG zH - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) CameraSuiteScene##id, -typedef enum { -#include "camera_suite_scene_config.h" - CameraSuiteSceneNum, -} CameraSuiteScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers camera_suite_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "camera_suite_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "camera_suite_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "camera_suite_scene_config.h" -#undef ADD_SCENE diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_camera.c b/applications/external/camera_suite/scenes/camera_suite_scene_camera.c deleted file mode 100644 index 809d9a5c1..000000000 --- a/applications/external/camera_suite/scenes/camera_suite_scene_camera.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "../camera_suite.h" -#include "../helpers/camera_suite_custom_event.h" -#include "../views/camera_suite_view_camera.h" - -void camera_suite_view_camera_callback(CameraSuiteCustomEvent event, void* context) { - furi_assert(context); - CameraSuite* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void camera_suite_scene_camera_on_enter(void* context) { - furi_assert(context); - CameraSuite* app = context; - camera_suite_view_camera_set_callback( - app->camera_suite_view_camera, camera_suite_view_camera_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdCamera); -} - -bool camera_suite_scene_camera_on_event(void* context, SceneManagerEvent event) { - CameraSuite* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case CameraSuiteCustomEventSceneCameraLeft: - case CameraSuiteCustomEventSceneCameraRight: - case CameraSuiteCustomEventSceneCameraUp: - case CameraSuiteCustomEventSceneCameraDown: - case CameraSuiteCustomEventSceneCameraOk: - // Do nothing. - break; - case CameraSuiteCustomEventSceneCameraBack: - notification_message(app->notification, &sequence_reset_red); - notification_message(app->notification, &sequence_reset_green); - notification_message(app->notification, &sequence_reset_blue); - if(!scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, CameraSuiteSceneMenu)) { - scene_manager_stop(app->scene_manager); - view_dispatcher_stop(app->view_dispatcher); - } - consumed = true; - break; - } - } - - return consumed; -} - -void camera_suite_scene_camera_on_exit(void* context) { - CameraSuite* app = context; - UNUSED(app); -} diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_config.h b/applications/external/camera_suite/scenes/camera_suite_scene_config.h deleted file mode 100644 index 2cb9245ef..000000000 --- a/applications/external/camera_suite/scenes/camera_suite_scene_config.h +++ /dev/null @@ -1,5 +0,0 @@ -ADD_SCENE(camera_suite, start, Start) -ADD_SCENE(camera_suite, menu, Menu) -ADD_SCENE(camera_suite, camera, Camera) -ADD_SCENE(camera_suite, guide, Guide) -ADD_SCENE(camera_suite, settings, Settings) \ No newline at end of file diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_guide.c b/applications/external/camera_suite/scenes/camera_suite_scene_guide.c deleted file mode 100644 index 6599058ef..000000000 --- a/applications/external/camera_suite/scenes/camera_suite_scene_guide.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "../camera_suite.h" -#include "../helpers/camera_suite_custom_event.h" -#include "../views/camera_suite_view_guide.h" - -void camera_suite_view_guide_callback(CameraSuiteCustomEvent event, void* context) { - furi_assert(context); - CameraSuite* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void camera_suite_scene_guide_on_enter(void* context) { - furi_assert(context); - CameraSuite* app = context; - camera_suite_view_guide_set_callback( - app->camera_suite_view_guide, camera_suite_view_guide_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdGuide); -} - -bool camera_suite_scene_guide_on_event(void* context, SceneManagerEvent event) { - CameraSuite* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case CameraSuiteCustomEventSceneGuideLeft: - case CameraSuiteCustomEventSceneGuideRight: - case CameraSuiteCustomEventSceneGuideUp: - case CameraSuiteCustomEventSceneGuideDown: - // Do nothing. - break; - case CameraSuiteCustomEventSceneGuideBack: - notification_message(app->notification, &sequence_reset_red); - notification_message(app->notification, &sequence_reset_green); - notification_message(app->notification, &sequence_reset_blue); - if(!scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, CameraSuiteSceneMenu)) { - scene_manager_stop(app->scene_manager); - view_dispatcher_stop(app->view_dispatcher); - } - consumed = true; - break; - } - } - - return consumed; -} - -void camera_suite_scene_guide_on_exit(void* context) { - CameraSuite* app = context; - UNUSED(app); -} \ No newline at end of file diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_menu.c b/applications/external/camera_suite/scenes/camera_suite_scene_menu.c deleted file mode 100644 index ae37e11b6..000000000 --- a/applications/external/camera_suite/scenes/camera_suite_scene_menu.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "../camera_suite.h" - -enum SubmenuIndex { - /** Camera. */ - SubmenuIndexSceneCamera = 10, - /** Guide/how-to. */ - SubmenuIndexGuide, - /** Settings menu. */ - SubmenuIndexSettings, -}; - -void camera_suite_scene_menu_submenu_callback(void* context, uint32_t index) { - CameraSuite* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void camera_suite_scene_menu_on_enter(void* context) { - CameraSuite* app = context; - - submenu_add_item( - app->submenu, - "Open Camera", - SubmenuIndexSceneCamera, - camera_suite_scene_menu_submenu_callback, - app); - submenu_add_item( - app->submenu, "Guide", SubmenuIndexGuide, camera_suite_scene_menu_submenu_callback, app); - submenu_add_item( - app->submenu, - "Settings", - SubmenuIndexSettings, - camera_suite_scene_menu_submenu_callback, - app); - - submenu_set_selected_item( - app->submenu, scene_manager_get_scene_state(app->scene_manager, CameraSuiteSceneMenu)); - - view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdMenu); -} - -bool camera_suite_scene_menu_on_event(void* context, SceneManagerEvent event) { - CameraSuite* app = context; - UNUSED(app); - if(event.type == SceneManagerEventTypeBack) { - // Exit application. - scene_manager_stop(app->scene_manager); - view_dispatcher_stop(app->view_dispatcher); - return true; - } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexSceneCamera) { - scene_manager_set_scene_state( - app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneCamera); - scene_manager_next_scene(app->scene_manager, CameraSuiteSceneCamera); - return true; - } else if(event.event == SubmenuIndexGuide) { - scene_manager_set_scene_state( - app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexGuide); - scene_manager_next_scene(app->scene_manager, CameraSuiteSceneGuide); - return true; - } else if(event.event == SubmenuIndexSettings) { - scene_manager_set_scene_state( - app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSettings); - scene_manager_next_scene(app->scene_manager, CameraSuiteSceneSettings); - return true; - } - } - return false; -} - -void camera_suite_scene_menu_on_exit(void* context) { - CameraSuite* app = context; - submenu_reset(app->submenu); -} \ No newline at end of file diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_settings.c b/applications/external/camera_suite/scenes/camera_suite_scene_settings.c deleted file mode 100644 index a06b45fe9..000000000 --- a/applications/external/camera_suite/scenes/camera_suite_scene_settings.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "../camera_suite.h" -#include - -// Camera orientation, in degrees. -const char* const orientation_text[4] = { - "0", - "90", - "180", - "270", -}; - -const uint32_t orientation_value[4] = { - CameraSuiteOrientation0, - CameraSuiteOrientation90, - CameraSuiteOrientation180, - CameraSuiteOrientation270, -}; - -const char* const haptic_text[2] = { - "OFF", - "ON", -}; - -const uint32_t haptic_value[2] = { - CameraSuiteHapticOff, - CameraSuiteHapticOn, -}; - -const char* const speaker_text[2] = { - "OFF", - "ON", -}; - -const uint32_t speaker_value[2] = { - CameraSuiteSpeakerOff, - CameraSuiteSpeakerOn, -}; - -const char* const led_text[2] = { - "OFF", - "ON", -}; - -const uint32_t led_value[2] = { - CameraSuiteLedOff, - CameraSuiteLedOn, -}; - -static void camera_suite_scene_settings_set_camera_orientation(VariableItem* item) { - CameraSuite* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, orientation_text[index]); - app->orientation = orientation_value[index]; -} - -static void camera_suite_scene_settings_set_haptic(VariableItem* item) { - CameraSuite* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, haptic_text[index]); - app->haptic = haptic_value[index]; -} - -static void camera_suite_scene_settings_set_speaker(VariableItem* item) { - CameraSuite* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, speaker_text[index]); - app->speaker = speaker_value[index]; -} - -static void camera_suite_scene_settings_set_led(VariableItem* item) { - CameraSuite* app = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, led_text[index]); - app->led = led_value[index]; -} - -void camera_suite_scene_settings_submenu_callback(void* context, uint32_t index) { - CameraSuite* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, index); -} - -void camera_suite_scene_settings_on_enter(void* context) { - CameraSuite* app = context; - VariableItem* item; - uint8_t value_index; - - // Camera Orientation - item = variable_item_list_add( - app->variable_item_list, - "Orientation:", - 4, - camera_suite_scene_settings_set_camera_orientation, - app); - value_index = value_index_uint32(app->orientation, orientation_value, 4); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, orientation_text[value_index]); - - // Haptic FX ON/OFF - item = variable_item_list_add( - app->variable_item_list, "Haptic FX:", 2, camera_suite_scene_settings_set_haptic, app); - value_index = value_index_uint32(app->haptic, haptic_value, 2); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, haptic_text[value_index]); - - // Sound FX ON/OFF - item = variable_item_list_add( - app->variable_item_list, "Sound FX:", 2, camera_suite_scene_settings_set_speaker, app); - value_index = value_index_uint32(app->speaker, speaker_value, 2); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, speaker_text[value_index]); - - // LED FX ON/OFF - item = variable_item_list_add( - app->variable_item_list, "LED FX:", 2, camera_suite_scene_settings_set_led, app); - value_index = value_index_uint32(app->led, led_value, 2); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, led_text[value_index]); - - view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdSettings); -} - -bool camera_suite_scene_settings_on_event(void* context, SceneManagerEvent event) { - CameraSuite* app = context; - UNUSED(app); - bool consumed = false; - if(event.type == SceneManagerEventTypeCustom) { - } - return consumed; -} - -void camera_suite_scene_settings_on_exit(void* context) { - CameraSuite* app = context; - variable_item_list_set_selected_item(app->variable_item_list, 0); - variable_item_list_reset(app->variable_item_list); -} \ No newline at end of file diff --git a/applications/external/camera_suite/scenes/camera_suite_scene_start.c b/applications/external/camera_suite/scenes/camera_suite_scene_start.c deleted file mode 100644 index 0dda05ede..000000000 --- a/applications/external/camera_suite/scenes/camera_suite_scene_start.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "../camera_suite.h" -#include "../helpers/camera_suite_custom_event.h" -#include "../views/camera_suite_view_start.h" - -void camera_suite_scene_start_callback(CameraSuiteCustomEvent event, void* context) { - furi_assert(context); - CameraSuite* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, event); -} - -void camera_suite_scene_start_on_enter(void* context) { - furi_assert(context); - CameraSuite* app = context; - camera_suite_view_start_set_callback( - app->camera_suite_view_start, camera_suite_scene_start_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdStartscreen); -} - -bool camera_suite_scene_start_on_event(void* context, SceneManagerEvent event) { - CameraSuite* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - switch(event.event) { - case CameraSuiteCustomEventStartLeft: - case CameraSuiteCustomEventStartRight: - case CameraSuiteCustomEventStartUp: - case CameraSuiteCustomEventStartDown: - // Do nothing. - break; - case CameraSuiteCustomEventStartOk: - scene_manager_next_scene(app->scene_manager, CameraSuiteSceneMenu); - consumed = true; - break; - case CameraSuiteCustomEventStartBack: - notification_message(app->notification, &sequence_reset_red); - notification_message(app->notification, &sequence_reset_green); - notification_message(app->notification, &sequence_reset_blue); - if(!scene_manager_search_and_switch_to_previous_scene( - app->scene_manager, CameraSuiteSceneStart)) { - scene_manager_stop(app->scene_manager); - view_dispatcher_stop(app->view_dispatcher); - } - consumed = true; - break; - } - } - - return consumed; -} - -void camera_suite_scene_start_on_exit(void* context) { - CameraSuite* app = context; - UNUSED(app); -} \ No newline at end of file diff --git a/applications/external/camera_suite/screenshots/camera_preview.png b/applications/external/camera_suite/screenshots/camera_preview.png deleted file mode 100644 index 5f66ec42c833d6464c028af3aa5be312a7bc09a0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3342 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 diff --git a/applications/external/camera_suite/screenshots/guide.png b/applications/external/camera_suite/screenshots/guide.png deleted file mode 100644 index 87ed51218cb4f7b5cd39414798c3fa0335e10d28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2233 zcmaJ@dr*^C7QYc`kq)t|18tPZ&a%E&D>BG4fUf1yvC&!-kf?0al0r-nD9;dxEw+n7 z5Tzg$5?ERD077d>Qh5YBl#o_14v?e}61R#56CSJZB@pd2Dq&(A3RIk<6PF9jY-S`0Q>XGhxv9Vb|V08 z)YyajSc%}VXTxfJmYkIHuc}K!ftz9` z5r@N$n(r?oTnB3INl&Fwsx|hwo?J~1AX>G&T)G54nx$MY1dFbkkz zllvB)ApWg>_e=@K?d0pe%z(W_!jSaQ$L3J|;z=?42TE}Y>Jrplsj%bnIr-+8Nk<8J0tcnbbjLdqu|MKS3Qlm13@6RuoC!uV@z%cM z)if^8gPBI$Ua!o{P6xI@bDXb@j7n_?+ve zrdWm8j{P4ckk{<)waJ>0 ztuu+IcswpcQzGD8_cZ2CBox%i-#|_F#6ib;OC!_*ZFq(I6|R3!pE5YJNRT-={&z^+ z4vx#!M?W*Fhs)}GjWs*80~DzCN4%UOoQ;5Z6`XYN4Q8}y@vC9&KZ}R*Z)NElw;~(k z;7hGk%(NoU@6sX2&5c1!%a0Xu$~Cc^s@d0UzE&}x)J&s_KIzLXZ=W8O9Ukngvy7Ky+WNNMGbDwEEXaZD$*P-MnkDJNCmZ-nTL73^xzb6gtk z-zD2_r$gzMAl|p>5Bn51ixq`n3T`%oSaY#QC@OVq7qh!5p`Ca}MVf=e6#86lqwwMS z)3#(BvgXeHRYX|_mG5iwV5Hp$K+r`-OKZP@UFAiK-nryayQBkOEp|R^~OjhZb zC@6a8m}N?bCC{(v3DCf!E;S2?6<+bzBl{tZNynwXxq882$_XP z;Zm3>lwY*dJUCIEA@n?35cx|U`%klHCS(su%a1#{xljb%j^)ZWA-SGFi{?apE@?Fc z5}utT(3*9)QMpfLB%DnHmw^EV2R7TS@UA4A6%>M^lpj_wuH3apo5u8YA zJ$96)+t8ELedAYUf`R^!wM(oxI$PJ18l17KKCM_fc4{!>^v=)1PYhCctL|x|j)fYC`osW0^i-6eo Li3ddolE3^P)2XN* diff --git a/applications/external/camera_suite/screenshots/main_menu.png b/applications/external/camera_suite/screenshots/main_menu.png deleted file mode 100644 index 3ae802cc2d31175779a3914ea594a0390fe8809e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1499 zcmeAS@N?(olHy`uVBq!ia0y~yU;;8388|?c*QZDWAjMhW5n0T@z;^_M8K-LVNi#68 z_IkQFhE&XXdpEFeikCz~;O#R-G4s0%YS$hAacik2JIk)d_j9(-{-SW$_nCL#-@}iu z_vgnm?3lj(etq4)nc{QU8Pru67y_9X1ipU!Bk%oAlJP@a_1n8qj0`TE3=UHm7+h@c z)iN>s*?+UH!IFVt84H7g7X!m0e3YHt{}WZ-^Fm{*zc*wv&FK||m;*HKN?tX?`R_ZX z-#H!k`=@r1Z-en>CWa0nh6W8rh7PL~zJ%_*`}FsJvz?j#W8S}M_p{69?6s@kmR;BO zKfa(e4B|{xAhrB9bH%nN|K7j(_x0BNyPw|6<-UDyy7}q%s_Ulri{>5JABk}N+$#PA z?(a?U=S|zQHzF+C^;YhH=zQkluiqJCKF(Qqh70bktnIc9y5|?nkNjQpbML(D{nPiZ zJ9+Ezo=e*6f6iRb95WHgH;?bCe^|b{UR>_M)NOC}B3TLzIamPJyq9|r^{wjOEF`@j z>=@2Ru)o-Va1t~aD5R?X{Q8*ty}9o1zt5M?%lv)6&i3E5`K&vXv>|~EG<`+?UcN2g z4&G3F#`@Yotgwyj_4oJ>mW=K$grlU)?4L8fbt$)<@oF{>gp`j6&hCp$Ddmd|v z>%PbQF%yC5hz*$FKrz{`{4QJn=e|F;zO(6Do{M|W9PxYeH?{`tO^9%@O5tZ{_-w$= zaKHg#_bEAGf0WN@5uLGjp0Z!vWcCcpcoj+5*(16eUq7?14;VU+qnlA=1fL1gQhVu dhzze|{_==S09OD2 diff --git a/applications/external/camera_suite/screenshots/settings.png b/applications/external/camera_suite/screenshots/settings.png deleted file mode 100644 index 32de46d72a82b836a5cb4da71a8b5dee4f9166f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1900 zcmaJ?eN<9s7=MZKYcpIso*DVkvK>8oI$2F;8feYUkENChMfs7_tb;XI9GyW1xm>mU z*m9H=qNbH8*;y?6Km%fnEa6%b99y84#gqiU@Tv&zvT$8@I{V|E_rCA@+~?!>Jip&P zz9}x!*$M9i0C0|uibw!p9`cF-4%Q`Rgc5)(j^9R6XaFqy-TGkI{tK4?VDA|nu|6rE zGpJr)`Xb0>K}UXvv~6<#G0sY@<247DiU38;*}5bJGbhmP=FV-+)8g|d=w`FlAjAwM z3oR2mAydfI&fLxj0MFt9II;+Unp7nKtIuHGZ03XT`l|waAlfzsz-|Y?+B6R={e_6E zS%@ie>x%&{FBoZVj{(@=h}?ZO1vI+Y$Si_k^WMfnf2ndbnGLy`&gQnJO}2`K z?KU&SmEt!di*I~MG42i&ACi+|Lz`snw>5?-&CP_ORXUW*m*ew7Oxt_-LS|Tq{JtCA zl9$Pegc#$u{HhvL25#|}3&;6%Vg|c}cYx<630-5$6aWX(H_koksFFQOP4KRnyt-G! zgwtM^SCE6ZIC7I<1LXw%5x?|$tw4z#!+;K#H&xX?&IbI~sh{;N_M7%RSVUo%!}>Yy zkL*FXqqTm-tLv|JLkjwH{UYj}$hW@kQcV+XI&e@4rdP`T^31{9sfPNOQt{QE*(qfu3GeslJG94XPv7 z<{s@t<#f@Aaeq>gMX|i7o*F5a0S!e0IRQm#Dpfk zyE>{TTBv#Js`z?|I1q(iy{eo_I~4Cc6pd<$p9&#NGAf#)7THYH{ZCt8FPJ|RPJpRF zNV~0Fw6aKs5095Quo$v|?$xN{f9ME#XbfABkdd<#w2jBRx70SmVa9kvoB$S?*7hG5 z^fna;y_kImIyK(JlH`Z}M2WYww|udU&4{bB&34twmeNrPo1FZRPn3kFzQ-2oP*b8f zNo09?3yTbs>ftVF`#_4@q-?~AK)`rwohLL=X>uxRCagT*(dk9>vHMfv9}6~TU5PJN zEb~|Rcd6Lfd)Q%(Pn6iXz5xRx3)D>)^U_7f`dV2Ul0b|D|qN&ta(4&<%v4hIa5Zu7{(zk+S<@Rg? z(%5i=T~8;EAVo)@g|RN_cj81oC{h^{VEg>$TGP67Z=?8T&n1>ZH4`?URf*O~WGXhG zl-5c`+$GeM#OkoY4XaArwY-bXxg){5q%B`_2pJW*-1Jl2?B2il1Eb89s4PggaXZK* zIk|f`uQ>TZsPg&~J$xSJjh%{(>gfEiBQcDWp*{iY%4yf+z3{z1UVv%`o0#~u+67#b z-e+f(&@#)aowSDym-jJq`bHrbJ@be-3KSM{cj9`&*y5H;<}dYwVjq;`G>qZH3%T2d zc;>=-2}1?ZMQA~ywjrt*k^*_cjb(hsU@koD^XG7D==Rgy!-2%(L;&zMi8Usi!{!H} q{64_Xm9m$I{(~2S?HsYRU@Cmq(bqmZDnx#tAo`2Ah^7rY3jYS@P22$h diff --git a/applications/external/camera_suite/screenshots/start_screen.png b/applications/external/camera_suite/screenshots/start_screen.png deleted file mode 100644 index 6fe690c58414675c4fff5ef8552d1619c1ad7442..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1707 zcmd5-ZA@EL7(N9Y4h81o7aNo%lW4+1hD2aMDJXJ*Et{V})iTD%uCWM=YT9z4m(4^s zMrjtNp#&~E{j<>yVnx289j4$YTuH{Q4B9#n3M=-)@DA5P>#ZzuuRr~xKW?6LbIy6+ z=Y5~&&AIT}aVRigUjP6gFf$|V4FG)TB?mm}g{*sm!!+zJ&k%?K2>QYMaXya>+7G}k zVP;zDThiv=%&Dcbu^?sg_UM_4!O$DFFjW}gB^?B4}EZ3mo{9VDcZ?{g}dCN2(DugUzi)Oxk5Z){;q?f$x zP?W&umaGvaf;D6`;WPlR`+z+WG~5SK9Zvb(O9N-05^YJOj`@yO$crTy z+3!_@;P2+#jyClmf9o24cd7RQHc%^Ba||_{DA=_fj*eO!{EI>(cZ`S4KIkilCO+=s zU5FWQaSaWPY^p>8skXWJQw`Yu^>18GC}|mX)*Au1;s*|7(NGnxQy44LH?hL{*)sW* z_*cl%=hQY)!ohd5>rhJT-sqR(h+yRL?c?;Vn_ODTkbfcPUkX$~LuL1;CP^h#z1P!7 zsfJYyGAo><*;=YtM4$;yRozO6?hvbo@g=y`zhwHI$Z#2n&8w41F65EV8AV4bqcA;_ zpl-OO$1q@PLyW|ZnQCOkuqu}@ABACjweEdu+Zr2A=XoVf{<6;s$q zt!qK#ifhJMJ4fr1PP&(njjHGMD*G}-TG6l>m8qf&SFAI zI^v-*XjmX=85m8V74Z$L=5DoVcwg~GLcOIL_u;j4y>2y*5}iLMR_%(}cBV;xE{v2!x(A{VPy5Tel(`2{<rJ}h= zgZML1|M0W#zsL>%k%6f2lf1P%y=4KiuG7! z#XFu^5TqOvf7DW0By<_Y`ZpOSoA%8gC2l(>k@cMR$+9r9s2MTDR45L%ceU!-^CLZa zP-I8ywXhi_EIQ1Ty8%DKGD$xV_c`Zb&-yFrj<@;iE>Oc~=WG(-ww{@u^ohqLr*~*l or@|U?99PkQuxC4-{YS-R{fMTn1YbTuzdj)I)#GW`U(P@GC%ZCr_5c6? diff --git a/applications/external/camera_suite/views/camera_suite_view_camera.c b/applications/external/camera_suite/views/camera_suite_view_camera.c deleted file mode 100644 index 5d710044b..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_camera.c +++ /dev/null @@ -1,374 +0,0 @@ -#include "../camera_suite.h" -#include -#include -#include -#include -#include -#include "../helpers/camera_suite_haptic.h" -#include "../helpers/camera_suite_speaker.h" -#include "../helpers/camera_suite_led.h" - -static CameraSuiteViewCamera* current_instance = NULL; - -struct CameraSuiteViewCamera { - CameraSuiteViewCameraCallback callback; - FuriStreamBuffer* rx_stream; - FuriThread* worker_thread; - View* view; - void* context; -}; - -void camera_suite_view_camera_set_callback( - CameraSuiteViewCamera* instance, - CameraSuiteViewCameraCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - instance->callback = callback; - instance->context = context; -} - -// Function to draw pixels on the canvas based on camera orientation -static void draw_pixel_by_orientation(Canvas* canvas, uint8_t x, uint8_t y, uint8_t orientation) { - switch(orientation) { - case 0: // Camera rotated 0 degrees (right side up, default) - canvas_draw_dot(canvas, x, y); - break; - case 1: // Camera rotated 90 degrees - canvas_draw_dot(canvas, y, FRAME_WIDTH - 1 - x); - break; - case 2: // Camera rotated 180 degrees (upside down) - canvas_draw_dot(canvas, FRAME_WIDTH - 1 - x, FRAME_HEIGHT - 1 - y); - break; - case 3: // Camera rotated 270 degrees - canvas_draw_dot(canvas, FRAME_HEIGHT - 1 - y, x); - break; - default: - break; - } -} - -static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) { - UartDumpModel* model = _model; - - // Clear the screen. - canvas_set_color(canvas, ColorBlack); - - // Draw the frame. - canvas_draw_frame(canvas, 0, 0, FRAME_WIDTH, FRAME_HEIGHT); - - CameraSuite* app = current_instance->context; - - for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) { - uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15 - uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63 - - for(uint8_t i = 0; i < 8; ++i) { - if((model->pixels[p] & (1 << (7 - i))) != 0) { - draw_pixel_by_orientation(canvas, (x * 8) + i, y, app->orientation); - } - } - } - - // Draw the guide if the camera is not initialized. - if(!model->initialized) { - canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 8, 12, "Connect the ESP32-CAM"); - canvas_draw_str(canvas, 20, 24, "VCC - 3V3"); - canvas_draw_str(canvas, 20, 34, "GND - GND"); - canvas_draw_str(canvas, 20, 44, "U0R - TX"); - canvas_draw_str(canvas, 20, 54, "U0T - RX"); - } -} - -static void camera_suite_view_camera_model_init(UartDumpModel* const model) { - for(size_t i = 0; i < FRAME_BUFFER_LENGTH; i++) { - model->pixels[i] = 0; - } -} - -static bool camera_suite_view_camera_input(InputEvent* event, void* context) { - furi_assert(context); - CameraSuiteViewCamera* instance = context; - if(event->type == InputTypeRelease) { - switch(event->key) { - default: // Stop all sounds, reset the LED. - with_view_model( - instance->view, - UartDumpModel * model, - { - UNUSED(model); - camera_suite_play_bad_bump(instance->context); - camera_suite_stop_all_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 0, 0, 0); - }, - true); - break; - } - // Send `data` to the ESP32-CAM - } else if(event->type == InputTypePress) { - uint8_t data[1]; - switch(event->key) { - case InputKeyBack: - // Stop the camera stream. - data[0] = 's'; - // Go back to the main menu. - with_view_model( - instance->view, - UartDumpModel * model, - { - UNUSED(model); - instance->callback(CameraSuiteCustomEventSceneCameraBack, instance->context); - }, - true); - break; - case InputKeyLeft: - // Camera: Invert. - data[0] = '<'; - with_view_model( - instance->view, - UartDumpModel * model, - { - UNUSED(model); - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneCameraLeft, instance->context); - }, - true); - break; - case InputKeyRight: - // Camera: Enable/disable dithering. - data[0] = '>'; - with_view_model( - instance->view, - UartDumpModel * model, - { - UNUSED(model); - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneCameraRight, instance->context); - }, - true); - break; - case InputKeyUp: - // Camera: Increase contrast. - data[0] = 'C'; - with_view_model( - instance->view, - UartDumpModel * model, - { - UNUSED(model); - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneCameraUp, instance->context); - }, - true); - break; - case InputKeyDown: - // Camera: Reduce contrast. - data[0] = 'c'; - with_view_model( - instance->view, - UartDumpModel * model, - { - UNUSED(model); - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneCameraDown, instance->context); - }, - true); - break; - case InputKeyOk: - // Switch dithering types. - data[0] = 'D'; - with_view_model( - instance->view, - UartDumpModel * model, - { - UNUSED(model); - camera_suite_play_happy_bump(instance->context); - camera_suite_play_input_sound(instance->context); - camera_suite_led_set_rgb(instance->context, 0, 0, 255); - instance->callback(CameraSuiteCustomEventSceneCameraOk, instance->context); - }, - true); - break; - case InputKeyMAX: - break; - } - // Send `data` to the ESP32-CAM - furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); - } - return true; -} - -static void camera_suite_view_camera_exit(void* context) { - furi_assert(context); -} - -static void camera_suite_view_camera_enter(void* context) { - // Check `context` for null. If it is null, abort program, else continue. - furi_assert(context); - - // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`. - CameraSuiteViewCamera* instance = (CameraSuiteViewCamera*)context; - - // Assign the current instance to the global variable - current_instance = instance; - - uint8_t data[1]; - data[0] = 'S'; // Uppercase `S` to start the camera - // Send `data` to the ESP32-CAM - furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); - - with_view_model( - instance->view, - UartDumpModel * model, - { camera_suite_view_camera_model_init(model); }, - true); -} - -static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* context) { - // Check `context` for null. If it is null, abort program, else continue. - furi_assert(context); - - // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`. - CameraSuiteViewCamera* instance = context; - - // If `uartIrqEvent` is `UartIrqEventRXNE`, send the data to the - // `rx_stream` and set the `WorkerEventRx` flag. - if(uartIrqEvent == UartIrqEventRXNE) { - furi_stream_buffer_send(instance->rx_stream, &data, 1, 0); - furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), WorkerEventRx); - } -} - -static void process_ringbuffer(UartDumpModel* model, uint8_t byte) { - // First char has to be 'Y' in the buffer. - if(model->ringbuffer_index == 0 && byte != 'Y') { - return; - } - - // Second char has to be ':' in the buffer or reset. - if(model->ringbuffer_index == 1 && byte != ':') { - model->ringbuffer_index = 0; - process_ringbuffer(model, byte); - return; - } - - // Assign current byte to the ringbuffer. - model->row_ringbuffer[model->ringbuffer_index] = byte; - // Increment the ringbuffer index. - ++model->ringbuffer_index; - - // Let's wait 'till the buffer fills. - if(model->ringbuffer_index < RING_BUFFER_LENGTH) { - return; - } - - // Flush the ringbuffer to the framebuffer. - model->ringbuffer_index = 0; // Reset the ringbuffer - model->initialized = true; // Established the connection successfully. - size_t row_start_index = - model->row_ringbuffer[2] * ROW_BUFFER_LENGTH; // Third char will determine the row number - - if(row_start_index > LAST_ROW_INDEX) { // Failsafe - row_start_index = 0; - } - - for(size_t i = 0; i < ROW_BUFFER_LENGTH; ++i) { - model->pixels[row_start_index + i] = - model->row_ringbuffer[i + 3]; // Writing the remaining 16 bytes into the frame buffer - } -} - -static int32_t camera_worker(void* context) { - furi_assert(context); - CameraSuiteViewCamera* instance = context; - - while(1) { - uint32_t events = - furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); - - if(events & WorkerEventStop) { - break; - } else if(events & WorkerEventRx) { - size_t length = 0; - do { - size_t intended_data_size = 64; - uint8_t data[intended_data_size]; - length = - furi_stream_buffer_receive(instance->rx_stream, data, intended_data_size, 0); - - if(length > 0) { - with_view_model( - instance->view, - UartDumpModel * model, - { - for(size_t i = 0; i < length; i++) { - process_ringbuffer(model, data[i]); - } - }, - false); - } - } while(length > 0); - - with_view_model( - instance->view, UartDumpModel * model, { UNUSED(model); }, true); - } - } - - return 0; -} - -CameraSuiteViewCamera* camera_suite_view_camera_alloc() { - CameraSuiteViewCamera* instance = malloc(sizeof(CameraSuiteViewCamera)); - - instance->view = view_alloc(); - - instance->rx_stream = furi_stream_buffer_alloc(2048, 1); - - // Set up views - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel)); - view_set_context(instance->view, instance); // furi_assert crashes in events without this - view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_camera_draw); - view_set_input_callback(instance->view, camera_suite_view_camera_input); - view_set_enter_callback(instance->view, camera_suite_view_camera_enter); - view_set_exit_callback(instance->view, camera_suite_view_camera_exit); - - with_view_model( - instance->view, - UartDumpModel * model, - { camera_suite_view_camera_model_init(model); }, - true); - - instance->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance); - furi_thread_start(instance->worker_thread); - - // Enable uart listener - furi_hal_console_disable(); - furi_hal_uart_set_br(FuriHalUartIdUSART1, 230400); - furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, camera_on_irq_cb, instance); - - return instance; -} - -void camera_suite_view_camera_free(CameraSuiteViewCamera* instance) { - furi_assert(instance); - - with_view_model( - instance->view, UartDumpModel * model, { UNUSED(model); }, true); - view_free(instance->view); - free(instance); -} - -View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* instance) { - furi_assert(instance); - return instance->view; -} \ No newline at end of file diff --git a/applications/external/camera_suite/views/camera_suite_view_camera.h b/applications/external/camera_suite/views/camera_suite_view_camera.h deleted file mode 100644 index 5ccbac71a..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_camera.h +++ /dev/null @@ -1,61 +0,0 @@ -#include "../helpers/camera_suite_custom_event.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#pragma once - -#define FRAME_WIDTH 128 -#define FRAME_HEIGHT 64 -#define FRAME_BIT_DEPTH 1 -#define FRAME_BUFFER_LENGTH 1024 -#define ROW_BUFFER_LENGTH 16 -#define RING_BUFFER_LENGTH 19 -#define LAST_ROW_INDEX 1008 - -extern const Icon I_DolphinCommon_56x48; - -typedef struct UartDumpModel UartDumpModel; - -struct UartDumpModel { - bool initialized; - int rotation_angle; - uint8_t pixels[FRAME_BUFFER_LENGTH]; - uint8_t ringbuffer_index; - uint8_t row_ringbuffer[RING_BUFFER_LENGTH]; -}; - -typedef struct CameraSuiteViewCamera CameraSuiteViewCamera; - -typedef void (*CameraSuiteViewCameraCallback)(CameraSuiteCustomEvent event, void* context); - -void camera_suite_view_camera_set_callback( - CameraSuiteViewCamera* camera_suite_view_camera, - CameraSuiteViewCameraCallback callback, - void* context); - -CameraSuiteViewCamera* camera_suite_view_camera_alloc(); - -void camera_suite_view_camera_free(CameraSuiteViewCamera* camera_suite_static); - -View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* camera_suite_static); - -typedef enum { - // Reserved for StreamBuffer internal event - WorkerEventReserved = (1 << 0), - WorkerEventStop = (1 << 1), - WorkerEventRx = (1 << 2), -} WorkerEventFlags; - -#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) diff --git a/applications/external/camera_suite/views/camera_suite_view_guide.c b/applications/external/camera_suite/views/camera_suite_view_guide.c deleted file mode 100644 index 479f8d4d1..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_guide.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "../camera_suite.h" -#include -#include -#include -#include -#include - -struct CameraSuiteViewGuide { - View* view; - CameraSuiteViewGuideCallback callback; - void* context; -}; - -typedef struct { - int some_value; -} CameraSuiteViewGuideModel; - -void camera_suite_view_guide_set_callback( - CameraSuiteViewGuide* instance, - CameraSuiteViewGuideCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - instance->callback = callback; - instance->context = context; -} - -void camera_suite_view_guide_draw(Canvas* canvas, CameraSuiteViewGuideModel* model) { - UNUSED(model); - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Guide"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 0, 12, AlignLeft, AlignTop, "Left = Toggle Invert"); - canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Right = Toggle Dithering"); - canvas_draw_str_aligned(canvas, 0, 32, AlignLeft, AlignTop, "Up = Contrast Up"); - canvas_draw_str_aligned(canvas, 0, 42, AlignLeft, AlignTop, "Down = Contrast Down"); - // TODO: Possibly update to take picture instead. - canvas_draw_str_aligned(canvas, 0, 52, AlignLeft, AlignTop, "Center = Toggle Dither Type"); -} - -static void camera_suite_view_guide_model_init(CameraSuiteViewGuideModel* const model) { - model->some_value = 1; -} - -bool camera_suite_view_guide_input(InputEvent* event, void* context) { - furi_assert(context); - CameraSuiteViewGuide* instance = context; - if(event->type == InputTypeRelease) { - switch(event->key) { - case InputKeyBack: - with_view_model( - instance->view, - CameraSuiteViewGuideModel * model, - { - UNUSED(model); - instance->callback(CameraSuiteCustomEventSceneGuideBack, instance->context); - }, - true); - break; - case InputKeyLeft: - case InputKeyRight: - case InputKeyUp: - case InputKeyDown: - case InputKeyOk: - case InputKeyMAX: - // Do nothing. - break; - } - } - return true; -} - -void camera_suite_view_guide_exit(void* context) { - furi_assert(context); -} - -void camera_suite_view_guide_enter(void* context) { - furi_assert(context); - CameraSuiteViewGuide* instance = (CameraSuiteViewGuide*)context; - with_view_model( - instance->view, - CameraSuiteViewGuideModel * model, - { camera_suite_view_guide_model_init(model); }, - true); -} - -CameraSuiteViewGuide* camera_suite_view_guide_alloc() { - CameraSuiteViewGuide* instance = malloc(sizeof(CameraSuiteViewGuide)); - instance->view = view_alloc(); - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(CameraSuiteViewGuideModel)); - view_set_context(instance->view, instance); // furi_assert crashes in events without this - view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_guide_draw); - view_set_input_callback(instance->view, camera_suite_view_guide_input); - view_set_enter_callback(instance->view, camera_suite_view_guide_enter); - view_set_exit_callback(instance->view, camera_suite_view_guide_exit); - - with_view_model( - instance->view, - CameraSuiteViewGuideModel * model, - { camera_suite_view_guide_model_init(model); }, - true); - - return instance; -} - -void camera_suite_view_guide_free(CameraSuiteViewGuide* instance) { - furi_assert(instance); - - with_view_model( - instance->view, CameraSuiteViewGuideModel * model, { UNUSED(model); }, true); - view_free(instance->view); - free(instance); -} - -View* camera_suite_view_guide_get_view(CameraSuiteViewGuide* instance) { - furi_assert(instance); - return instance->view; -} diff --git a/applications/external/camera_suite/views/camera_suite_view_guide.h b/applications/external/camera_suite/views/camera_suite_view_guide.h deleted file mode 100644 index cd78d4b01..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_guide.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "../helpers/camera_suite_custom_event.h" - -typedef struct CameraSuiteViewGuide CameraSuiteViewGuide; - -typedef void (*CameraSuiteViewGuideCallback)(CameraSuiteCustomEvent event, void* context); - -void camera_suite_view_guide_set_callback( - CameraSuiteViewGuide* camera_suite_view_guide, - CameraSuiteViewGuideCallback callback, - void* context); - -View* camera_suite_view_guide_get_view(CameraSuiteViewGuide* camera_suite_static); - -CameraSuiteViewGuide* camera_suite_view_guide_alloc(); - -void camera_suite_view_guide_free(CameraSuiteViewGuide* camera_suite_static); \ No newline at end of file diff --git a/applications/external/camera_suite/views/camera_suite_view_start.c b/applications/external/camera_suite/views/camera_suite_view_start.c deleted file mode 100644 index a84ee50c2..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_start.c +++ /dev/null @@ -1,126 +0,0 @@ -#include "../camera_suite.h" -#include -#include -#include -#include - -struct CameraSuiteViewStart { - View* view; - CameraSuiteViewStartCallback callback; - void* context; -}; - -typedef struct { - int some_value; -} CameraSuiteViewStartModel; - -void camera_suite_view_start_set_callback( - CameraSuiteViewStart* instance, - CameraSuiteViewStartCallback callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - instance->callback = callback; - instance->context = context; -} - -void camera_suite_view_start_draw(Canvas* canvas, CameraSuiteViewStartModel* model) { - UNUSED(model); - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Camera Suite"); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 22, AlignCenter, AlignTop, "Flipper Zero"); - canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "ESP32 CAM"); - elements_button_center(canvas, "Start"); -} - -static void camera_suite_view_start_model_init(CameraSuiteViewStartModel* const model) { - model->some_value = 1; -} - -bool camera_suite_view_start_input(InputEvent* event, void* context) { - furi_assert(context); - CameraSuiteViewStart* instance = context; - if(event->type == InputTypeRelease) { - switch(event->key) { - case InputKeyBack: - // Exit application. - with_view_model( - instance->view, - CameraSuiteViewStartModel * model, - { - UNUSED(model); - instance->callback(CameraSuiteCustomEventStartBack, instance->context); - }, - true); - break; - case InputKeyOk: - // Start the application. - with_view_model( - instance->view, - CameraSuiteViewStartModel * model, - { - UNUSED(model); - instance->callback(CameraSuiteCustomEventStartOk, instance->context); - }, - true); - break; - case InputKeyMAX: - case InputKeyLeft: - case InputKeyRight: - case InputKeyUp: - case InputKeyDown: - // Do nothing. - break; - } - } - return true; -} - -void camera_suite_view_start_exit(void* context) { - furi_assert(context); -} - -void camera_suite_view_start_enter(void* context) { - furi_assert(context); - CameraSuiteViewStart* instance = (CameraSuiteViewStart*)context; - with_view_model( - instance->view, - CameraSuiteViewStartModel * model, - { camera_suite_view_start_model_init(model); }, - true); -} - -CameraSuiteViewStart* camera_suite_view_start_alloc() { - CameraSuiteViewStart* instance = malloc(sizeof(CameraSuiteViewStart)); - instance->view = view_alloc(); - view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(CameraSuiteViewStartModel)); - // furi_assert crashes in events without this - view_set_context(instance->view, instance); - view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_start_draw); - view_set_input_callback(instance->view, camera_suite_view_start_input); - - with_view_model( - instance->view, - CameraSuiteViewStartModel * model, - { camera_suite_view_start_model_init(model); }, - true); - - return instance; -} - -void camera_suite_view_start_free(CameraSuiteViewStart* instance) { - furi_assert(instance); - - with_view_model( - instance->view, CameraSuiteViewStartModel * model, { UNUSED(model); }, true); - view_free(instance->view); - free(instance); -} - -View* camera_suite_view_start_get_view(CameraSuiteViewStart* instance) { - furi_assert(instance); - return instance->view; -} diff --git a/applications/external/camera_suite/views/camera_suite_view_start.h b/applications/external/camera_suite/views/camera_suite_view_start.h deleted file mode 100644 index e991cce92..000000000 --- a/applications/external/camera_suite/views/camera_suite_view_start.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include "../helpers/camera_suite_custom_event.h" - -typedef struct CameraSuiteViewStart CameraSuiteViewStart; - -typedef void (*CameraSuiteViewStartCallback)(CameraSuiteCustomEvent event, void* context); - -void camera_suite_view_start_set_callback( - CameraSuiteViewStart* camera_suite_view_start, - CameraSuiteViewStartCallback callback, - void* context); - -View* camera_suite_view_start_get_view(CameraSuiteViewStart* camera_suite_static); - -CameraSuiteViewStart* camera_suite_view_start_alloc(); - -void camera_suite_view_start_free(CameraSuiteViewStart* camera_suite_static); \ No newline at end of file diff --git a/applications/external/dap_link/README.md b/applications/external/dap_link/README.md deleted file mode 100644 index aead0a60a..000000000 --- a/applications/external/dap_link/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# Flipper Zero as CMSIS DAP/DAP Link -Flipper Zero as a [Free-DAP](https://github.com/ataradov/free-dap) based SWD\JTAG debugger. Free-DAP is a free and open source firmware implementation of the [CMSIS-DAP](https://www.keil.com/pack/doc/CMSIS_Dev/DAP/html/index.html) debugger. - -## Protocols -SWD, JTAG , CMSIS-DAP v1 (18 KiB/s), CMSIS-DAP v2 (46 KiB/s), VCP (USB-UART). - -WinUSB for driverless installation for Windows 8 and above. - -## Usage - -### VSCode + Cortex-Debug - Set `"device": "cmsis-dap"` - -