diff --git a/.drone.yml b/.drone.yml index 7a9577fa59..9bcb355466 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,6 +36,7 @@ steps: - mkdir -p sd-card/nfc/assets - mkdir -p sd-card/infrared/assets - mkdir -p sd-card/unirf + - mkdir -p sd-card/rfidfuzzer - mkdir -p sd-card/badusb/layouts - cp assets/resources/badusb/layouts/* sd-card/badusb/layouts/ - cp assets/resources/subghz/assets/dangerous_settings sd-card/subghz/assets/dangerous_settings @@ -48,6 +49,7 @@ steps: - cp assets/resources/infrared/assets/projectors.ir sd-card/infrared/assets/projectors.ir - cp assets/resources/infrared/assets/audio.ir sd-card/infrared/assets/audio.ir - cp assets/resources/unirf/unirf_map_example.txt sd-card/unirf/unirf_map_example.txt + - cp assets/resources/rfidfuzzer/example_uids.txt sd-card/rfidfuzzer/example_uids.txt - cp assets/resources/Manifest sd-card/Manifest - zip -r artifacts-default/sd-card-${DRONE_TAG}.zip sd-card - rm -rf sd-card diff --git a/CHANGELOG.md b/CHANGELOG.md index f25923c152..8f9bfcdb02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,19 @@ ### New changes -* NRF24 sniffer - tweak sniff parameters for more speed and reliability (by @mothball187) (PR #51) -* Fixed text in LF RFID -> Extra Actions +* Fix picopass plugin (revert OFW PR 1701) * Updated universal remote assets (by @Amec0e) -* OFW PR: SubGHz decode raw gui (by @qistoph) (PR 1667) / xMasterX: Replaced custom image with default one & Fixed Led don't stop blink after pressing Send from decoder scene -* WAV Player plugin excluded from releases to save space, you can enable it in `applications\meta` for your builds -* OFW PR: SubGhz: add protocol Intertechno_V3 - OFW PR 1622 -* OFW PR: SubGhz: add protocol Prastel - OFW PR 1674 -* OFW PR: Fix displaying LFRFID protocol names - OFW PR 1682 / xMasterX: Fixed display for N/A manufacturer -* OFW: LF RFID - PAC/Stanley, Paradox, Jablotron, Viking, Pyramid protocols support -* OFW: Picopass write (PR 1658) -* OFW: SubGhz: fix CLI "subghz tx" -* OFW: IR: Fix crash after cancelling Learn New Remote -* OFW: SubGhz: output debug data to external pin -* OFW: Speedup SD card & enlarge your RAM -* OFW: Other small changes +### Changes from previous release +* Add SubGhz Bruteforce plugin (by @Ganapati & @xMasterX) (PR #57) - saving functionality and many fixes by @xMasterX +* Fix GUI and add new icon in LF-RFID App (icon by @Svaarich) +* GUI Changes to LFRFID Fuzzer +* New Battery info (from @theeogflip) (PR #60) +* NRFSniff: Adds unique count display (by @Graf3x) (PR #56) +* Updated universal remote assets (by @Amec0e) +* OFW: RFID app port to plain C +* OFW: SubGhz: fix decoder keeloq +* OFW PR: Picopass: detect and show SE / SIO - OFW PR 1701 (by pcunning) +* OFW PR: Fix MFClassic 4k reading - OFW PR 1712 (by Astrrra) +* OFW: SubGhz: handle missing key in cryptostore. Lib: lower default display contrast. +* OFW: Furi: wait for timer wind down in destructor **Note: To avoid issues prefer installing using web updater or by self update package, all needed assets will be installed** diff --git a/ReadMe.md b/ReadMe.md index f7236640d4..42e1ba680b 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,4 +39,4 @@ The great user [dummy-decoy](https://github.com/dummy-decoy) implemented loading * DOGE: `D6R6gYgBn5LwTNmPyvAQR6bZ9EtGgFCpvv` * LTC: `ltc1q3ex4ejkl0xpx3znwrmth4lyuadr5qgv8tmq8z9` -_logo generated with DALLE-2_ +_logo generated with DALLE-2_ \ No newline at end of file diff --git a/applications/debug_tools/application.fam b/applications/debug_tools/application.fam index 8cb495b0c4..701115241c 100644 --- a/applications/debug_tools/application.fam +++ b/applications/debug_tools/application.fam @@ -15,6 +15,16 @@ App( ], ) +App( + appid="debug_apps_lite", + name="Basic debug apps bundle", + apptype=FlipperAppType.METAPACKAGE, + provides=[ + "usb_mouse", + "uart_echo", + ], +) + App( appid="blink_test", name="Blink Test", diff --git a/applications/desktop/desktop_settings/desktop_settings.h b/applications/desktop/desktop_settings/desktop_settings.h index 800847d56a..c33aadb76c 100644 --- a/applications/desktop/desktop_settings/desktop_settings.h +++ b/applications/desktop/desktop_settings/desktop_settings.h @@ -46,4 +46,5 @@ typedef struct { PinCode pin_code; uint8_t is_locked; uint32_t auto_lock_delay_ms; + uint8_t displayBatteryPercentage; } DesktopSettings; diff --git a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c b/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c index aaaf2e779b..d3c3aa072c 100644 --- a/applications/desktop/desktop_settings/scenes/desktop_settings_scene_start.c +++ b/applications/desktop/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_BATTERY_DISPLAY 4 #define AUTO_LOCK_DELAY_COUNT 9 const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { @@ -25,11 +26,31 @@ const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = { const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] = {0, 10000, 15000, 30000, 60000, 90000, 120000, 300000, 600000}; +#define BATTERY_VIEW_COUNT 5 + +const char* const battery_view_count_text[BATTERY_VIEW_COUNT] = { + "Bar", + "%", + "Inv. %", + "Retro 3", + "Retro 5", +}; + +const uint32_t displayBatteryPercentage_value[BATTERY_VIEW_COUNT] = {0, 1, 2, 3, 4}; + 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_battery_view_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, battery_view_count_text[index]); + app->settings.displayBatteryPercentage = 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); @@ -65,6 +86,20 @@ 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, + "Battery View", + BATTERY_VIEW_COUNT, + desktop_settings_scene_start_battery_view_changed, + app); + + value_index = value_index_uint32( + app->settings.displayBatteryPercentage, + displayBatteryPercentage_value, + BATTERY_VIEW_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, battery_view_count_text[value_index]); + view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList); } @@ -91,6 +126,9 @@ bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent even case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY: consumed = true; break; + case SCENE_EVENT_SELECT_BATTERY_DISPLAY: + consumed = true; + break; } } return consumed; diff --git a/applications/desktop/views/desktop_view_main.c b/applications/desktop/views/desktop_view_main.c index a3aa637075..397011a43d 100644 --- a/applications/desktop/views/desktop_view_main.c +++ b/applications/desktop/views/desktop_view_main.c @@ -16,7 +16,7 @@ struct DesktopMainView { TimerHandle_t poweroff_timer; }; -#define DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT 3000 +#define DESKTOP_MAIN_VIEW_POWEROFF_TIMEOUT 2000 static void desktop_main_poweroff_timer_callback(TimerHandle_t timer) { DesktopMainView* main_view = pvTimerGetTimerID(timer); diff --git a/applications/flipfrid/README.md b/applications/flipfrid/README.md index d5f79bf5d2..51ed2fa670 100644 --- a/applications/flipfrid/README.md +++ b/applications/flipfrid/README.md @@ -1,4 +1,21 @@ -# FlipFrid +# Flipfrid -A simple implementation of ZigFrid on Flipper zero (+bonus) -(https://z4ziggy.wordpress.com/2017/07/21/zigfrid-a-passive-rfid-fuzzer/) \ No newline at end of file +Basic EM4100 Fuzzer + +## Why + +Flipfrid is a simple Rfid fuzzer using EM4100 protocol (125khz). +Objective is to provide a simple to use fuzzer to test readers by emulating various cards. + +EM4100 cards use a 1 byte customer id and 4 bytes card id. + +## How + +There is 4 modes : +- Default key loop over 16 factory/default keys and emulate each one after one ; +- BF customer id. just an iteration from 0X00 to 0XFF on the first byte ; +- Load Dump file : Load an existing EM4100 dump generated by Flipperzero, select an index and bruteforce from 0X00 to 0XFF; +- Uids list: loop over a text file (one uid per line) + +TODO : +- blank screen on back press diff --git a/applications/flipfrid/application.fam b/applications/flipfrid/application.fam index 0eee3cc1bd..4bcb381393 100644 --- a/applications/flipfrid/application.fam +++ b/applications/flipfrid/application.fam @@ -6,5 +6,5 @@ App( cdefines=["APP_FLIP_FRID"], requires=["gui"], stack_size=1 * 1024, - order=29, + order=13, ) diff --git a/applications/flipfrid/flipfrid.c b/applications/flipfrid/flipfrid.c index cc8898dff6..1dab3cae1f 100644 --- a/applications/flipfrid/flipfrid.c +++ b/applications/flipfrid/flipfrid.c @@ -1,123 +1,38 @@ -#include -#include -#include -#include - -#include -#include "raw_em4100.h" -#include - #include "flipfrid.h" -#define NUMBER_OF_ATTACKS 3 -#define TIME_BETWEEN_CARDS \ - 5 // Emulate 2 cards per second : (5 * (configTICK_RATE_HZ_RAW/10)) == (5*(1000/10)) == (5*100) == (500)ms -#define TAG "FLIPFRID" - -uint8_t id_list[16][5] = { - {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF - {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 - {0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 - {0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 - {0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 - {0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 - {0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 - {0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 - {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 - {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 - {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID - {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha - {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha - {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha - {0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha -}; - -typedef enum { - DefaultKeys, - BruteForceCustomerId, - BadCrc, -} AttackType; - -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; - -typedef struct { - EventType evt_type; - InputKey key; - InputType input_type; -} FlipFridEvent; - -// STRUCTS -typedef struct { - bool emitting; - AttackType current_attack_type; - uint8_t* current_uid; - uint8_t meta_data; - NotificationApp* notify; -} FlipFridState; +#include "scene/flipfrid_scene_entrypoint.h" +#include "scene/flipfrid_scene_load_file.h" +#include "scene/flipfrid_scene_select_field.h" +#include "scene/flipfrid_scene_run_attack.h" +#include "scene/flipfrid_scene_load_custom_uids.h" + +#define RFIDFUZZER_APP_FOLDER "/ext/rfidfuzzer" static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) { - const FlipFridState* flipfrid_state = (FlipFridState*)acquire_mutex((ValueMutex*)ctx, 100); + FlipFridState* flipfrid_state = (FlipFridState*)acquire_mutex((ValueMutex*)ctx, 100); if(flipfrid_state == NULL) { return; } - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - // Frame - canvas_draw_frame(canvas, 0, 0, 128, 64); - - // Title - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignTop, "Flip/Frid"); - - // Badge Type - char uid[16]; - char badge_type[21]; - switch(flipfrid_state->current_attack_type) { - case DefaultKeys: - strcpy(badge_type, " Default Values >"); + // Draw correct Canvas + switch(flipfrid_state->current_scene) { + case NoneScene: + case SceneEntryPoint: + flipfrid_scene_entrypoint_on_draw(canvas, flipfrid_state); break; - case BruteForceCustomerId: - strcpy(badge_type, "< BF Customer ID >"); + case SceneSelectFile: + flipfrid_scene_load_file_on_draw(canvas, flipfrid_state); break; - case BadCrc: - strcpy(badge_type, "< Bad CRC "); + case SceneSelectField: + flipfrid_scene_select_field_on_draw(canvas, flipfrid_state); break; - default: + case SceneAttack: + flipfrid_scene_run_attack_on_draw(canvas, flipfrid_state); + break; + case SceneLoadCustomUids: + flipfrid_scene_load_custom_uids_on_draw(canvas, flipfrid_state); break; - } - - if(flipfrid_state->current_attack_type == BruteForceCustomerId) { - snprintf(uid, sizeof(uid), " ID : %2X ", flipfrid_state->current_uid[0]); - } else if(flipfrid_state->current_attack_type == BadCrc) { - snprintf(uid, sizeof(uid), "Sending packets"); - } else { - snprintf( - uid, - sizeof(uid), - "%2X:%2X:%2X:%2X:%2X", - flipfrid_state->current_uid[0], - flipfrid_state->current_uid[1], - flipfrid_state->current_uid[2], - flipfrid_state->current_uid[3], - flipfrid_state->current_uid[4]); - } - - // Badge infos - canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, badge_type); - - if(flipfrid_state->emitting) { - canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignCenter, uid); - } else { - canvas_draw_str_aligned( - canvas, 64, 42, AlignCenter, AlignCenter, "Press OK to start/stop"); } release_mutex((ValueMutex*)ctx, flipfrid_state); @@ -133,173 +48,194 @@ void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_qu static void flipfrid_timer_callback(FuriMessageQueue* event_queue) { furi_assert(event_queue); - FlipFridEvent event = { .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; furi_message_queue_put(event_queue, &event, 25); } +FlipFridState* flipfrid_alloc() { + FlipFridState* flipfrid = malloc(sizeof(FlipFridState)); + string_init(flipfrid->notification_msg); + string_init(flipfrid->attack_name); + + flipfrid->previous_scene = NoneScene; + flipfrid->current_scene = SceneEntryPoint; + flipfrid->is_running = true; + flipfrid->is_attacking = false; + flipfrid->key_index = 0; + flipfrid->menu_index = 0; + + flipfrid->attack = FlipFridAttackDefaultValues; + flipfrid->notify = furi_record_open(RECORD_NOTIFICATION); + + flipfrid->data[0] = 0x00; + flipfrid->data[1] = 0x00; + flipfrid->data[2] = 0x00; + flipfrid->data[3] = 0x00; + flipfrid->data[4] = 0x00; + + flipfrid->payload[0] = 0x00; + flipfrid->payload[1] = 0x00; + flipfrid->payload[2] = 0x00; + flipfrid->payload[3] = 0x00; + flipfrid->payload[4] = 0x00; + + //Dialog + flipfrid->dialogs = furi_record_open(RECORD_DIALOGS); + + return flipfrid; +} + +void flipfrid_free(FlipFridState* flipfrid) { + //Dialog + furi_record_close(RECORD_DIALOGS); + notification_message(flipfrid->notify, &sequence_blink_stop); + + // Strings + string_clear(flipfrid->notification_msg); + string_clear(flipfrid->attack_name); + + free(flipfrid->data); + free(flipfrid->payload); + + // The rest + free(flipfrid); +} + // ENTRYPOINT int32_t flipfrid_start(void* p) { UNUSED(p); - // Input + FURI_LOG_I(TAG, "Initializing input"); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent)); - FlipFridState* flipfrid_state = (FlipFridState*)malloc(sizeof(FlipFridState)); + FlipFridState* flipfrid_state = flipfrid_alloc(); ValueMutex flipfrid_state_mutex; // Mutex + FURI_LOG_I(TAG, "Initializing flipfrid mutex"); if(!init_mutex(&flipfrid_state_mutex, flipfrid_state, sizeof(FlipFridState))) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); furi_message_queue_free(event_queue); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); free(flipfrid_state); + return 255; + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + if(!storage_simply_mkdir(storage, RFIDFUZZER_APP_FOLDER)) { + FURI_LOG_E(TAG, "Could not create folder %s", RFIDFUZZER_APP_FOLDER); } + furi_record_close(RECORD_STORAGE); // Configure view port + FURI_LOG_I(TAG, "Initializing viewport"); ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, flipfrid_draw_callback, &flipfrid_state_mutex); view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue); // Configure timer + FURI_LOG_I(TAG, "Initializing timer"); FuriTimer* timer = furi_timer_alloc(flipfrid_timer_callback, FuriTimerTypePeriodic, event_queue); - furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // configTICK_RATE_HZ_RAW 1000 + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second // Register view port in GUI + FURI_LOG_I(TAG, "Initializing gui"); Gui* gui = (Gui*)furi_record_open(RECORD_GUI); gui_add_view_port(gui, view_port, GuiLayerFullscreen); // Init values FlipFridEvent event; - flipfrid_state->emitting = false; - flipfrid_state->current_uid = id_list[0]; - flipfrid_state->current_attack_type = DefaultKeys; - flipfrid_state->meta_data = 0; - flipfrid_state->notify = furi_record_open(RECORD_NOTIFICATION); - - // RFID Configuration - size_t data_size = 5; // Default EM4100 data size is 5 (1 customer id + 4 uid) - LFRFIDWorker* worker; - const ProtocolBase* lfrfid_protocol[] = {&protocol_raw_em4100, &protocol_raw_wrong_crc_em4100}; - ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocol, 2); - worker = lfrfid_worker_alloc(dict); - uint8_t selectedProtocol = CLEAN; - - // Application state - int menu_selected_item_index = 0; // Menu current item index - uint8_t counter = 0; // Used to count the step and wqaiting time between each test - uint8_t attack_state = 0; // Used to store the current attack state - uint8_t candidate[5]; // uid candidate - bool running = true; // Used to stop the application - - // Main loop - while(running) { + while(flipfrid_state->is_running) { // Get next event FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); if(event_status == FuriStatusOk) { if(event.evt_type == EventTypeKey) { - if(event.input_type == InputTypeShort) { - counter = 0; - switch(event.key) { - case InputKeyUp: - case InputKeyDown: - // OSEF + //Handle event key + switch(flipfrid_state->current_scene) { + case NoneScene: + case SceneEntryPoint: + flipfrid_scene_entrypoint_on_event(event, flipfrid_state); + break; + case SceneSelectFile: + flipfrid_scene_load_file_on_event(event, flipfrid_state); + break; + case SceneSelectField: + flipfrid_scene_select_field_on_event(event, flipfrid_state); + break; + case SceneAttack: + flipfrid_scene_run_attack_on_event(event, flipfrid_state); + break; + case SceneLoadCustomUids: + flipfrid_scene_load_custom_uids_on_event(event, flipfrid_state); + break; + } + + } else if(event.evt_type == EventTypeTick) { + //Handle event tick + if(flipfrid_state->current_scene != flipfrid_state->previous_scene) { + // Trigger Exit Scene + switch(flipfrid_state->previous_scene) { + case SceneEntryPoint: + flipfrid_scene_entrypoint_on_exit(flipfrid_state); break; - case InputKeyRight: - // Next badge type - flipfrid_state->emitting = false; - attack_state = 0; - notification_message(flipfrid_state->notify, &sequence_blink_stop); - if(menu_selected_item_index < (NUMBER_OF_ATTACKS - 1)) { - menu_selected_item_index++; - flipfrid_state->current_attack_type = - (AttackType)menu_selected_item_index; - } + case SceneSelectFile: + flipfrid_scene_load_file_on_exit(flipfrid_state); break; - case InputKeyLeft: - // Previous badge type - flipfrid_state->emitting = false; - attack_state = 0; - notification_message(flipfrid_state->notify, &sequence_blink_stop); - if(menu_selected_item_index > 0) { - menu_selected_item_index--; - flipfrid_state->current_attack_type = - (AttackType)menu_selected_item_index; - } + case SceneSelectField: + flipfrid_scene_select_field_on_exit(flipfrid_state); break; - case InputKeyOk: - if(flipfrid_state->emitting) { - flipfrid_state->emitting = false; - attack_state = 0; - // TODO FIX BLINK - notification_message(flipfrid_state->notify, &sequence_blink_stop); - } else { - flipfrid_state->emitting = true; - attack_state = 0; - // TODO FIX BLINK - notification_message( - flipfrid_state->notify, &sequence_blink_start_blue); - } + case SceneAttack: + flipfrid_scene_run_attack_on_exit(flipfrid_state); break; - case InputKeyBack: - notification_message(flipfrid_state->notify, &sequence_blink_stop); - flipfrid_state->emitting = false; - running = false; + case SceneLoadCustomUids: + flipfrid_scene_load_custom_uids_on_exit(flipfrid_state); + break; + case NoneScene: break; } - } - } else if(event.evt_type == EventTypeTick) { - // Emulate card - if(flipfrid_state->emitting) { - if(1 == counter) { - protocol_dict_set_data( - dict, (LFRFIDProtocol)0, flipfrid_state->current_uid, data_size); - worker = lfrfid_worker_alloc(dict); - lfrfid_worker_start_thread(worker); - lfrfid_worker_emulate_start(worker, (LFRFIDProtocol)selectedProtocol); - } else if(0 == counter) { - lfrfid_worker_stop(worker); - lfrfid_worker_stop_thread(worker); - // set next value - switch(flipfrid_state->current_attack_type) { - case DefaultKeys: { - selectedProtocol = CLEAN; - data_size = 5; - flipfrid_state->current_uid = id_list[attack_state]; - attack_state = attack_state + 1; - if(attack_state >= sizeof(id_list) / sizeof(id_list[0])) { - attack_state = 0; - } - break; - } - case BruteForceCustomerId: { - data_size = 5; - selectedProtocol = CLEAN; - candidate[0] = attack_state; - flipfrid_state->current_uid = candidate; - attack_state = attack_state + 1; - if((attack_state + 1) == 256) { - attack_state = 0; - } - break; - } - case BadCrc: { - selectedProtocol = BAD_CRC; - data_size = 5; - candidate[0] = 0xFF; - candidate[1] = 0xDE; - candidate[2] = 0xAD; - candidate[3] = 0xBE; - candidate[4] = 0xEF; - flipfrid_state->current_uid = candidate; - break; - } - } - } - if(counter > TIME_BETWEEN_CARDS) { - counter = 0; - } else { - counter++; + + // Trigger Entry Scene + switch(flipfrid_state->current_scene) { + case NoneScene: + case SceneEntryPoint: + flipfrid_scene_entrypoint_on_enter(flipfrid_state); + break; + case SceneSelectFile: + flipfrid_scene_load_file_on_enter(flipfrid_state); + break; + case SceneSelectField: + flipfrid_scene_select_field_on_enter(flipfrid_state); + break; + case SceneAttack: + flipfrid_scene_run_attack_on_enter(flipfrid_state); + break; + case SceneLoadCustomUids: + flipfrid_scene_load_custom_uids_on_enter(flipfrid_state); + break; } + flipfrid_state->previous_scene = flipfrid_state->current_scene; + } + + // Trigger Tick Scene + switch(flipfrid_state->current_scene) { + case NoneScene: + case SceneEntryPoint: + flipfrid_scene_entrypoint_on_tick(flipfrid_state); + break; + case SceneSelectFile: + flipfrid_scene_load_file_on_tick(flipfrid_state); + break; + case SceneSelectField: + flipfrid_scene_select_field_on_tick(flipfrid_state); + break; + case SceneAttack: + flipfrid_scene_run_attack_on_tick(flipfrid_state); + break; + case SceneLoadCustomUids: + flipfrid_scene_load_custom_uids_on_tick(flipfrid_state); + break; } view_port_update(view_port); } @@ -307,17 +243,16 @@ int32_t flipfrid_start(void* p) { } // Cleanup - notification_message(flipfrid_state->notify, &sequence_blink_stop); - lfrfid_worker_stop(worker); - lfrfid_worker_stop_thread(worker); - lfrfid_worker_free(worker); - protocol_dict_free(dict); furi_timer_stop(timer); furi_timer_free(timer); - free(flipfrid_state); + + FURI_LOG_I(TAG, "Cleaning up"); gui_remove_view_port(gui, view_port); view_port_free(view_port); furi_message_queue_free(event_queue); furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + flipfrid_free(flipfrid_state); + return 0; } \ No newline at end of file diff --git a/applications/flipfrid/flipfrid.h b/applications/flipfrid/flipfrid.h index 1b2369a4ca..95bb2121dc 100644 --- a/applications/flipfrid/flipfrid.h +++ b/applications/flipfrid/flipfrid.h @@ -1,7 +1,76 @@ #pragma once #include +#include +#include +#include +#include +#include "m-string.h" +#include #include #include -#include "raw_em4100.h" +#include +#include -int32_t flipfrid_start(void* p); \ No newline at end of file +#include +#include +#include +#include + +#include +#include + +#define TAG "FlipFrid" + +typedef enum { + FlipFridAttackDefaultValues, + FlipFridAttackBfCustomerId, + FlipFridAttackLoadFile, + FlipFridAttackLoadFileCustomUids, +} FlipFridAttacks; + +typedef enum { + NoneScene, + SceneEntryPoint, + SceneSelectFile, + SceneSelectField, + SceneAttack, + SceneLoadCustomUids, +} FlipFridScene; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType evt_type; + InputKey key; + InputType input_type; +} FlipFridEvent; + +// STRUCTS +typedef struct { + bool is_running; + bool is_attacking; + FlipFridScene current_scene; + FlipFridScene previous_scene; + NotificationApp* notify; + u_int8_t menu_index; + + string_t data_str; + uint8_t data[5]; + uint8_t payload[5]; + uint8_t attack_step; + FlipFridAttacks attack; + string_t attack_name; + + DialogsApp* dialogs; + string_t notification_msg; + uint8_t key_index; + LFRFIDWorker* worker; + ProtocolDict* dict; + ProtocolId protocol; + + // Used for custom dictionnary + Stream* uids_stream; +} FlipFridState; \ No newline at end of file diff --git a/applications/flipfrid/raw_em4100.c b/applications/flipfrid/raw_em4100.c deleted file mode 100644 index 4cc477ee78..0000000000 --- a/applications/flipfrid/raw_em4100.c +++ /dev/null @@ -1,106 +0,0 @@ -#include "raw_em4100.h" -#include - -bool protocol_em4100_raw_encoder_start(ProtocolEM4100* proto) { - FURI_LOG_D("RAW_EM4100", "encoder_start : CLEAN"); - // header - proto->encoded_data = 0b111111111; - - // data - for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) { - em4100_write_nibble(false, proto->data[i], &proto->encoded_data); - em4100_write_nibble(true, proto->data[i], &proto->encoded_data); - } - - // column parity and stop bit - uint8_t parity_sum; - - for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) { - parity_sum = 0; - for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) { - uint8_t parity_bit = (proto->encoded_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1; - parity_sum += parity_bit; - } - proto->encoded_data = (proto->encoded_data << 1) | ((parity_sum % 2) & 1); - } - - // stop bit - proto->encoded_data = (proto->encoded_data << 1) | 0; - - proto->encoded_data_index = 0; - proto->encoded_polarity = true; - - return true; -}; - -bool protocol_em4100_wrong_crc_encoder_start(ProtocolEM4100* proto) { - FURI_LOG_D("RAW_EM4100", "encoder_start : WRONG CRC"); - // header - proto->encoded_data = 0b111111111; - - // data - for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) { - em4100_write_nibble(false, proto->data[i], &proto->encoded_data); - em4100_write_nibble(true, proto->data[i], &proto->encoded_data); - } - - for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) { - proto->encoded_data = (proto->encoded_data << 1) | ((0 % 2) & 1); - } - - // stop bit - proto->encoded_data = (proto->encoded_data << 1) | 1; - - proto->encoded_data_index = 0; - proto->encoded_polarity = true; - - return true; -}; - -const ProtocolBase protocol_raw_em4100 = { - .name = "RawEM4100", - .manufacturer = "EM-Micro", - .data_size = EM4100_DECODED_DATA_SIZE, - .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, - .validate_count = 3, - .alloc = (ProtocolAlloc)protocol_em4100_alloc, - .free = (ProtocolFree)protocol_em4100_free, - .get_data = (ProtocolGetData)protocol_em4100_get_data, - .decoder = - { - .start = (ProtocolDecoderStart)protocol_em4100_decoder_start, - .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed, - }, - .encoder = - { - .start = (ProtocolEncoderStart)protocol_em4100_raw_encoder_start, - .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield, - }, - .render_data = (ProtocolRenderData)protocol_em4100_render_data, - .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, - .write_data = (ProtocolWriteData)protocol_em4100_write_data, -}; - -const ProtocolBase protocol_raw_wrong_crc_em4100 = { - .name = "RawEM4100", - .manufacturer = "EM-Micro", - .data_size = EM4100_DECODED_DATA_SIZE, - .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, - .validate_count = 3, - .alloc = (ProtocolAlloc)protocol_em4100_alloc, - .free = (ProtocolFree)protocol_em4100_free, - .get_data = (ProtocolGetData)protocol_em4100_get_data, - .decoder = - { - .start = (ProtocolDecoderStart)protocol_em4100_decoder_start, - .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed, - }, - .encoder = - { - .start = (ProtocolEncoderStart)protocol_em4100_wrong_crc_encoder_start, - .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield, - }, - .render_data = (ProtocolRenderData)protocol_em4100_render_data, - .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, - .write_data = (ProtocolWriteData)protocol_em4100_write_data, -}; \ No newline at end of file diff --git a/applications/flipfrid/raw_em4100.h b/applications/flipfrid/raw_em4100.h deleted file mode 100644 index faa2ccd9e3..0000000000 --- a/applications/flipfrid/raw_em4100.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include - - -enum FlipFridProtocol { - CLEAN, - BAD_CRC, -}; - -extern const ProtocolBase protocol_raw_em4100; -extern const ProtocolBase protocol_raw_wrong_crc_em4100; \ No newline at end of file diff --git a/applications/flipfrid/scene/flipfrid_scene_entrypoint.c b/applications/flipfrid/scene/flipfrid_scene_entrypoint.c new file mode 100644 index 0000000000..f30bb8e1de --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_entrypoint.c @@ -0,0 +1,123 @@ +#include "flipfrid_scene_entrypoint.h" + +string_t menu_items[4]; + +void flipfrid_scene_entrypoint_menu_callback(FlipFridState* context, uint32_t index) { + switch(index) { + case FlipFridAttackDefaultValues: + context->attack = FlipFridAttackDefaultValues; + context->current_scene = SceneAttack; + string_set_str(context->attack_name, "Default Values"); + break; + case FlipFridAttackBfCustomerId: + context->attack = FlipFridAttackBfCustomerId; + context->current_scene = SceneAttack; + string_set_str(context->attack_name, "Bad Customer ID"); + break; + case FlipFridAttackLoadFile: + context->attack = FlipFridAttackLoadFile; + context->current_scene = SceneSelectFile; + string_set_str(context->attack_name, "Load File"); + break; + case FlipFridAttackLoadFileCustomUids: + context->attack = FlipFridAttackLoadFileCustomUids; + context->current_scene = SceneLoadCustomUids; + string_set_str(context->attack_name, "Load Custom UIDs"); + break; + default: + break; + } +} + +void flipfrid_scene_entrypoint_on_enter(FlipFridState* context) { + // Clear the previous payload + context->payload[0] = 0x00; + context->payload[1] = 0x00; + context->payload[2] = 0x00; + context->payload[3] = 0x00; + context->payload[4] = 0x00; + + context->menu_index = 0; + for(uint32_t i = 0; i < 4; i++) { + string_init(menu_items[i]); + } + + string_set(menu_items[0], "Default Values"); + string_set(menu_items[1], "BF Customer ID"); + string_set(menu_items[2], "Load File"); + string_set(menu_items[3], "Load uids from file"); +} + +void flipfrid_scene_entrypoint_on_exit(FlipFridState* context) { + UNUSED(context); + for(uint32_t i = 0; i < 4; i++) { + string_clear(menu_items[i]); + } +} + +void flipfrid_scene_entrypoint_on_tick(FlipFridState* context) { + UNUSED(context); +} + +void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + if(context->menu_index < FlipFridAttackLoadFileCustomUids) { + context->menu_index++; + } + break; + case InputKeyUp: + if(context->menu_index > FlipFridAttackDefaultValues) { + context->menu_index--; + } + break; + case InputKeyLeft: + case InputKeyRight: + break; + case InputKeyOk: + flipfrid_scene_entrypoint_menu_callback(context, context->menu_index); + break; + case InputKeyBack: + context->is_running = false; + break; + } + } + } +} + +void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignTop, "RFID Fuzzer"); + + if(context->menu_index > FlipFridAttackDefaultValues) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 24, + AlignCenter, + AlignTop, + string_get_cstr(menu_items[context->menu_index - 1])); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 64, 36, AlignCenter, AlignTop, string_get_cstr(menu_items[context->menu_index])); + + if(context->menu_index < FlipFridAttackLoadFileCustomUids) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 48, + AlignCenter, + AlignTop, + string_get_cstr(menu_items[context->menu_index + 1])); + } +} \ No newline at end of file diff --git a/applications/flipfrid/scene/flipfrid_scene_entrypoint.h b/applications/flipfrid/scene/flipfrid_scene_entrypoint.h new file mode 100644 index 0000000000..29ca5bdfa2 --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_entrypoint.h @@ -0,0 +1,8 @@ +#pragma once +#include "../flipfrid.h" + +void flipfrid_scene_entrypoint_on_enter(FlipFridState* context); +void flipfrid_scene_entrypoint_on_exit(FlipFridState* context); +void flipfrid_scene_entrypoint_on_tick(FlipFridState* context); +void flipfrid_scene_entrypoint_on_event(FlipFridEvent event, FlipFridState* context); +void flipfrid_scene_entrypoint_on_draw(Canvas* canvas, FlipFridState* context); \ No newline at end of file diff --git a/applications/flipfrid/scene/flipfrid_scene_load_custom_uids.c b/applications/flipfrid/scene/flipfrid_scene_load_custom_uids.c new file mode 100644 index 0000000000..bb3cf37d7a --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_load_custom_uids.c @@ -0,0 +1,79 @@ +#include "flipfrid_scene_load_custom_uids.h" +#include "flipfrid_scene_run_attack.h" +#include "flipfrid_scene_entrypoint.h" + +#define LFRFID_UIDS_EXTENSION ".txt" +#define RFIDFUZZER_APP_PATH_FOLDER "/ext/rfidfuzzer" + +bool flipfrid_load_uids(FlipFridState* context, const char* file_path) { + bool result = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + context->uids_stream = buffered_file_stream_alloc(storage); + result = + buffered_file_stream_open(context->uids_stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING); + // Close if loading fails + if(!result) { + buffered_file_stream_close(context->uids_stream); + return false; + } + return result; +} + +bool flipfrid_load_custom_uids_from_file(FlipFridState* context) { + // Input events and views are managed by file_select + string_t uid_path; + string_init(uid_path); + string_set_str(uid_path, RFIDFUZZER_APP_PATH_FOLDER); + + bool res = dialog_file_browser_show( + context->dialogs, uid_path, uid_path, LFRFID_UIDS_EXTENSION, true, &I_125_10px, false); + + if(res) { + res = flipfrid_load_uids(context, string_get_cstr(uid_path)); + } + + string_clear(uid_path); + + return res; +} + +void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context) { + if(flipfrid_load_custom_uids_from_file(context)) { + // Force context loading + flipfrid_scene_run_attack_on_enter(context); + context->current_scene = SceneAttack; + } else { + flipfrid_scene_entrypoint_on_enter(context); + context->current_scene = SceneEntryPoint; + } +} + +void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context) { + UNUSED(context); +} + +void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context) { + UNUSED(context); +} + +void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + case InputKeyUp: + case InputKeyLeft: + case InputKeyRight: + case InputKeyOk: + case InputKeyBack: + context->current_scene = SceneEntryPoint; + break; + } + } + } +} + +void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context) { + UNUSED(context); + UNUSED(canvas); +} diff --git a/applications/flipfrid/scene/flipfrid_scene_load_custom_uids.h b/applications/flipfrid/scene/flipfrid_scene_load_custom_uids.h new file mode 100644 index 0000000000..a8ed982b68 --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_load_custom_uids.h @@ -0,0 +1,9 @@ +#pragma once +#include "../flipfrid.h" + +void flipfrid_scene_load_custom_uids_on_enter(FlipFridState* context); +void flipfrid_scene_load_custom_uids_on_exit(FlipFridState* context); +void flipfrid_scene_load_custom_uids_on_tick(FlipFridState* context); +void flipfrid_scene_load_custom_uids_on_event(FlipFridEvent event, FlipFridState* context); +void flipfrid_scene_load_custom_uids_on_draw(Canvas* canvas, FlipFridState* context); +bool flipfrid_load_custom_uids_from_file(FlipFridState* context); \ No newline at end of file diff --git a/applications/flipfrid/scene/flipfrid_scene_load_file.c b/applications/flipfrid/scene/flipfrid_scene_load_file.c new file mode 100644 index 0000000000..c4b1321d60 --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_load_file.c @@ -0,0 +1,146 @@ +#include "flipfrid_scene_load_file.h" +#include "flipfrid_scene_entrypoint.h" + +#define LFRFID_APP_EXTENSION ".rfid" +#define LFRFID_APP_PATH_FOLDER "/ext/lfrfid" + +bool flipfrid_load(FlipFridState* context, const char* file_path) { + bool result = false; + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + string_t temp_str; + string_init(temp_str); + do { + if(!flipper_format_file_open_existing(fff_data_file, file_path)) { + FURI_LOG_E(TAG, "Error open file %s", file_path); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Error open file"); + break; + } + + // FileType + if(!flipper_format_read_string(fff_data_file, "Filetype", temp_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Filetype"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Missing or incorrect Filetypes"); + break; + } else { + FURI_LOG_I(TAG, "Filetype: %s", string_get_cstr(temp_str)); + } + + // Key type + if(!flipper_format_read_string(fff_data_file, "Key type", temp_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Key type"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Missing or incorrect Key type"); + break; + } else { + FURI_LOG_I(TAG, "Key type: %s", string_get_cstr(temp_str)); + if(strcmp(string_get_cstr(temp_str), "EM4100") != 0) { + FURI_LOG_E(TAG, "Unsupported Key type"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Unsupported Key type"); + break; + } + } + + // Data + if(!flipper_format_read_string(fff_data_file, "Data", context->data_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Data"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Missing or incorrect Key"); + break; + } else { + FURI_LOG_I(TAG, "Key: %s", string_get_cstr(context->data_str)); + + // Check data size + if(string_size(context->data_str) != 14) { + FURI_LOG_E(TAG, "Incorrect Key length"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Incorrect Key length"); + break; + } + // String to uint8_t + for(uint8_t i = 0; i < 5; i++) { + char temp_str2[3]; + temp_str2[0] = string_get_cstr(context->data_str)[i * 3]; + temp_str2[1] = string_get_cstr(context->data_str)[i * 3 + 1]; + temp_str2[2] = '\0'; + context->data[i] = (uint8_t)strtol(temp_str2, NULL, 16); + } + } + + result = true; + } while(0); + string_clear(temp_str); + flipper_format_free(fff_data_file); + if(result) { + FURI_LOG_I(TAG, "Loaded successfully"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Source loaded."); + } + return result; +} + +void flipfrid_scene_load_file_on_enter(FlipFridState* context) { + if(flipfrid_load_protocol_from_file(context)) { + context->current_scene = SceneSelectField; + } else { + flipfrid_scene_entrypoint_on_enter(context); + context->current_scene = SceneEntryPoint; + } +} + +void flipfrid_scene_load_file_on_exit(FlipFridState* context) { + UNUSED(context); +} + +void flipfrid_scene_load_file_on_tick(FlipFridState* context) { + UNUSED(context); +} + +void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + case InputKeyUp: + case InputKeyLeft: + case InputKeyRight: + case InputKeyOk: + case InputKeyBack: + context->current_scene = SceneEntryPoint; + break; + } + } + } +} + +void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context) { + UNUSED(context); + UNUSED(canvas); +} + +bool flipfrid_load_protocol_from_file(FlipFridState* context) { + string_t user_file_path; + string_init(user_file_path); + string_set_str(user_file_path, LFRFID_APP_PATH_FOLDER); + + // Input events and views are managed by file_select + bool res = dialog_file_browser_show( + context->dialogs, + user_file_path, + user_file_path, + LFRFID_APP_EXTENSION, + true, + &I_125_10px, + true); + + if(res) { + res = flipfrid_load(context, string_get_cstr(user_file_path)); + } + + string_clear(user_file_path); + + return res; +} \ No newline at end of file diff --git a/applications/flipfrid/scene/flipfrid_scene_load_file.h b/applications/flipfrid/scene/flipfrid_scene_load_file.h new file mode 100644 index 0000000000..ca82daab4b --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_load_file.h @@ -0,0 +1,9 @@ +#pragma once +#include "../flipfrid.h" + +void flipfrid_scene_load_file_on_enter(FlipFridState* context); +void flipfrid_scene_load_file_on_exit(FlipFridState* context); +void flipfrid_scene_load_file_on_tick(FlipFridState* context); +void flipfrid_scene_load_file_on_event(FlipFridEvent event, FlipFridState* context); +void flipfrid_scene_load_file_on_draw(Canvas* canvas, FlipFridState* context); +bool flipfrid_load_protocol_from_file(FlipFridState* context); \ No newline at end of file diff --git a/applications/flipfrid/scene/flipfrid_scene_run_attack.c b/applications/flipfrid/scene/flipfrid_scene_run_attack.c new file mode 100644 index 0000000000..e34cb89863 --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_run_attack.c @@ -0,0 +1,214 @@ +#include "flipfrid_scene_run_attack.h" +#include + +uint8_t counter = 0; +#define TIME_BETWEEN_CARDS 5 +uint8_t id_list[16][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID + {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha + {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha + {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha + {0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +void flipfrid_scene_run_attack_on_enter(FlipFridState* context) { + context->attack_step = 0; + context->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + context->worker = lfrfid_worker_alloc(context->dict); + context->protocol = protocol_dict_get_protocol_by_name(context->dict, "EM4100"); +} + +void flipfrid_scene_run_attack_on_exit(FlipFridState* context) { + lfrfid_worker_stop(context->worker); + lfrfid_worker_stop_thread(context->worker); + lfrfid_worker_free(context->worker); + protocol_dict_free(context->dict); + notification_message(context->notify, &sequence_blink_stop); +} + +void flipfrid_scene_run_attack_on_tick(FlipFridState* context) { + if(context->is_attacking) { + if(1 == counter) { + protocol_dict_set_data(context->dict, context->protocol, context->payload, 5); + lfrfid_worker_free(context->worker); + context->worker = lfrfid_worker_alloc(context->dict); + lfrfid_worker_start_thread(context->worker); + lfrfid_worker_emulate_start(context->worker, context->protocol); + } else if(0 == counter) { + lfrfid_worker_stop(context->worker); + lfrfid_worker_stop_thread(context->worker); + switch(context->attack) { + case FlipFridAttackDefaultValues: + context->payload[0] = id_list[context->attack_step][0]; + context->payload[1] = id_list[context->attack_step][1]; + context->payload[2] = id_list[context->attack_step][2]; + context->payload[3] = id_list[context->attack_step][3]; + context->payload[4] = id_list[context->attack_step][4]; + + if(context->attack_step == 15) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + + } else { + context->attack_step++; + } + break; + + case FlipFridAttackBfCustomerId: + context->payload[0] = context->attack_step; + context->payload[1] = 0x00; + context->payload[2] = 0x00; + context->payload[3] = 0x00; + context->payload[4] = 0x00; + + if(context->attack_step == 255) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + } else { + context->attack_step++; + } + break; + case FlipFridAttackLoadFile: + context->payload[0] = context->data[0]; + context->payload[1] = context->data[1]; + context->payload[2] = context->data[2]; + context->payload[3] = context->data[3]; + context->payload[4] = context->data[4]; + + context->payload[context->key_index] = context->attack_step; + + if(context->attack_step == 255) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + break; + } else { + context->attack_step++; + } + break; + case FlipFridAttackLoadFileCustomUids: + while(true) { + string_reset(context->data_str); + if(!stream_read_line(context->uids_stream, context->data_str)) { + context->attack_step = 0; + counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + break; + }; + if(string_get_char(context->data_str, 0) == '#') continue; + if(string_size(context->data_str) != 11) continue; + break; + } + FURI_LOG_D(TAG, string_get_cstr(context->data_str)); + + // string is valid, parse it in context->payload + for(uint8_t i = 0; i < 5; i++) { + char temp_str[3]; + temp_str[0] = string_get_cstr(context->data_str)[i * 2]; + temp_str[1] = string_get_cstr(context->data_str)[i * 2 + 1]; + temp_str[2] = '\0'; + context->payload[i] = (uint8_t)strtol(temp_str, NULL, 16); + } + break; + } + } + + if(counter > TIME_BETWEEN_CARDS) { + counter = 0; + } else { + counter++; + } + } +} + +void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + case InputKeyUp: + case InputKeyLeft: + case InputKeyRight: + break; + case InputKeyOk: + counter = 0; + if(!context->is_attacking) { + notification_message(context->notify, &sequence_blink_start_blue); + context->is_attacking = true; + } else { + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + } + break; + case InputKeyBack: + if(context->attack == FlipFridAttackLoadFileCustomUids) { + buffered_file_stream_close(context->uids_stream); + } + + context->attack_step = 0; + context->is_attacking = false; + string_reset(context->notification_msg); + context->current_scene = SceneEntryPoint; + notification_message(context->notify, &sequence_blink_stop); + break; + } + } + } +} + +void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Frame + //canvas_draw_frame(canvas, 0, 0, 128, 64); + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, 64, 8, AlignCenter, AlignTop, string_get_cstr(context->attack_name)); + + char uid[16]; + snprintf( + uid, + sizeof(uid), + "%02X:%02X:%02X:%02X:%02X", + context->payload[0], + context->payload[1], + context->payload[2], + context->payload[3], + context->payload[4]); + canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, uid); + + canvas_set_font(canvas, FontSecondary); + //char start_stop_msg[20]; + if(context->is_attacking) { + elements_button_center(canvas, "Stop"); + //snprintf(start_stop_msg, sizeof(start_stop_msg), " Press OK to stop "); + } else { + elements_button_center(canvas, "Start"); + } + //canvas_draw_str_aligned(canvas, 64, 44, AlignCenter, AlignTop, start_stop_msg); +} diff --git a/applications/flipfrid/scene/flipfrid_scene_run_attack.h b/applications/flipfrid/scene/flipfrid_scene_run_attack.h new file mode 100644 index 0000000000..ae56d35e7e --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_run_attack.h @@ -0,0 +1,8 @@ +#pragma once +#include "../flipfrid.h" + +void flipfrid_scene_run_attack_on_enter(FlipFridState* context); +void flipfrid_scene_run_attack_on_exit(FlipFridState* context); +void flipfrid_scene_run_attack_on_tick(FlipFridState* context); +void flipfrid_scene_run_attack_on_event(FlipFridEvent event, FlipFridState* context); +void flipfrid_scene_run_attack_on_draw(Canvas* canvas, FlipFridState* context); diff --git a/applications/flipfrid/scene/flipfrid_scene_select_field.c b/applications/flipfrid/scene/flipfrid_scene_select_field.c new file mode 100644 index 0000000000..03411b2f4f --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_select_field.c @@ -0,0 +1,121 @@ +#include "flipfrid_scene_select_field.h" + +void flipfrid_center_displayed_key(FlipFridState* context, uint8_t index) { + const char* key_cstr = string_get_cstr(context->data_str); + uint8_t str_index = (index * 3); + + char display_menu[17] = { + 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; + + if(index > 1) { + display_menu[0] = key_cstr[str_index - 6]; + display_menu[1] = key_cstr[str_index - 5]; + } else { + display_menu[0] = ' '; + display_menu[1] = ' '; + } + + if(index > 0) { + display_menu[3] = key_cstr[str_index - 3]; + display_menu[4] = key_cstr[str_index - 2]; + } else { + display_menu[3] = ' '; + display_menu[4] = ' '; + } + + display_menu[7] = key_cstr[str_index]; + display_menu[8] = key_cstr[str_index + 1]; + + if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { + display_menu[11] = key_cstr[str_index + 3]; + display_menu[12] = key_cstr[str_index + 4]; + } else { + display_menu[11] = ' '; + display_menu[12] = ' '; + } + + if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { + display_menu[14] = key_cstr[str_index + 6]; + display_menu[15] = key_cstr[str_index + 7]; + } else { + display_menu[14] = ' '; + display_menu[15] = ' '; + } + + string_reset(context->notification_msg); + string_set_str(context->notification_msg, display_menu); +} + +void flipfrid_scene_select_field_on_enter(FlipFridState* context) { + string_clear(context->notification_msg); +} + +void flipfrid_scene_select_field_on_exit(FlipFridState* context) { + UNUSED(context); +} + +void flipfrid_scene_select_field_on_tick(FlipFridState* context) { + UNUSED(context); +} + +void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + const char* key_cstr = string_get_cstr(context->data_str); + + // don't look, it's ugly but I'm a python dev so... + uint8_t nb_bytes = 0; + for(uint8_t i = 0; i < strlen(key_cstr); i++) { + if(' ' == key_cstr[i]) { + nb_bytes++; + } + } + + switch(event.key) { + case InputKeyDown: + case InputKeyUp: + break; + case InputKeyLeft: + if(context->key_index > 0) { + context->key_index = context->key_index - 1; + } + break; + case InputKeyRight: + if(context->key_index < nb_bytes) { + context->key_index = context->key_index + 1; + } + break; + case InputKeyOk: + string_reset(context->notification_msg); + context->current_scene = SceneAttack; + break; + case InputKeyBack: + string_reset(context->notification_msg); + context->current_scene = SceneSelectFile; + break; + } + FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); + } + } +} + +void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Frame + //canvas_draw_frame(canvas, 0, 0, 128, 64); + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Use < > to select byte."); + + char msg_index[18]; + snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); + canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index); + + flipfrid_center_displayed_key(context, context->key_index); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 40, AlignCenter, AlignTop, string_get_cstr(context->notification_msg)); +} diff --git a/applications/flipfrid/scene/flipfrid_scene_select_field.h b/applications/flipfrid/scene/flipfrid_scene_select_field.h new file mode 100644 index 0000000000..5533e321cd --- /dev/null +++ b/applications/flipfrid/scene/flipfrid_scene_select_field.h @@ -0,0 +1,9 @@ +#pragma once +#include "../flipfrid.h" + +void flipfrid_scene_select_field_on_enter(FlipFridState* context); +void flipfrid_scene_select_field_on_exit(FlipFridState* context); +void flipfrid_scene_select_field_on_tick(FlipFridState* context); +void flipfrid_scene_select_field_on_event(FlipFridEvent event, FlipFridState* context); +void flipfrid_scene_select_field_on_draw(Canvas* canvas, FlipFridState* context); +void center_displayed_key(FlipFridState* context, uint8_t index); \ No newline at end of file diff --git a/applications/gui/canvas.c b/applications/gui/canvas.c index 1e275aafb4..562f2e1833 100644 --- a/applications/gui/canvas.c +++ b/applications/gui/canvas.c @@ -12,6 +12,7 @@ const CanvasFontParameters canvas_font_params[FontTotalNumber] = { [FontSecondary] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2}, [FontKeyboard] = {.leading_default = 11, .leading_min = 9, .height = 7, .descender = 2}, [FontBigNumbers] = {.leading_default = 18, .leading_min = 16, .height = 15, .descender = 0}, + [FontBatteryPercent] = {.leading_default = 11, .leading_min = 9, .height = 6, .descender = 0}, }; Canvas* canvas_init() { @@ -132,6 +133,8 @@ void canvas_set_font(Canvas* canvas, Font font) { u8g2_SetFont(&canvas->fb, u8g2_font_profont11_mr); } else if(font == FontBigNumbers) { u8g2_SetFont(&canvas->fb, u8g2_font_profont22_tn); + } else if(font == FontBatteryPercent) { + u8g2_SetFont(&canvas->fb, u8g2_font_5x7_tf); //u8g2_font_micro_tr); } else { furi_crash(NULL); } diff --git a/applications/gui/canvas.h b/applications/gui/canvas.h index 4923e2e661..edde10a01a 100644 --- a/applications/gui/canvas.h +++ b/applications/gui/canvas.h @@ -25,6 +25,7 @@ typedef enum { FontSecondary, FontKeyboard, FontBigNumbers, + FontBatteryPercent, // Keep last for fonts number calculation FontTotalNumber, diff --git a/applications/gui/modules/text_input.c b/applications/gui/modules/text_input.c index 26f74e7aca..b8098a3b9f 100644 --- a/applications/gui/modules/text_input.c +++ b/applications/gui/modules/text_input.c @@ -463,8 +463,6 @@ void text_input_free(TextInput* text_input) { // Send stop command furi_timer_stop(text_input->timer); - // Wait till timer stop - while(furi_timer_is_running(text_input->timer)) furi_delay_tick(1); // Release allocated memory furi_timer_free(text_input->timer); diff --git a/applications/gui/modules/validators.c b/applications/gui/modules/validators.c index ac293f8cb1..52f9946ca2 100644 --- a/applications/gui/modules/validators.c +++ b/applications/gui/modules/validators.c @@ -42,7 +42,9 @@ ValidatorIsFile* validator_is_file_alloc_init( instance->app_path_folder = strdup(app_path_folder); instance->app_extension = app_extension; - instance->current_name = strdup(current_name); + if(current_name != NULL) { + instance->current_name = strdup(current_name); + } return instance; } diff --git a/applications/infrared/infrared_cli.c b/applications/infrared/infrared_cli.c index aae02e8fd1..693e191ee6 100644 --- a/applications/infrared/infrared_cli.c +++ b/applications/infrared/infrared_cli.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "infrared_signal.h" @@ -10,6 +12,7 @@ static void infrared_cli_start_ir_rx(Cli* cli, string_t args); static void infrared_cli_start_ir_tx(Cli* cli, string_t args); +static void infrared_cli_process_decode(Cli* cli, string_t args); static const struct { const char* cmd; @@ -17,6 +20,7 @@ static const struct { } infrared_cli_commands[] = { {.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, {.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, + {.cmd = "decode", .process_function = infrared_cli_process_decode}, }; static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { @@ -86,6 +90,7 @@ static void infrared_cli_print_usage(void) { "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", INFRARED_MIN_FREQUENCY, INFRARED_MAX_FREQUENCY); + printf("\tir decode []\r\n"); } static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { @@ -162,6 +167,160 @@ static void infrared_cli_start_ir_tx(Cli* cli, string_t args) { infrared_signal_free(signal); } +static bool + infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) { + bool ret = infrared_signal_save(signal, file, name); + if(!ret) { + printf("Failed to save signal: \"%s\"\r\n", name); + } + return ret; +} + +static bool infrared_cli_decode_raw_signal( + InfraredRawSignal* raw_signal, + InfraredDecoderHandler* decoder, + FlipperFormat* output_file, + const char* signal_name) { + InfraredSignal* signal = infrared_signal_alloc(); + bool ret = false, level = true, is_decoded = false; + + size_t i; + for(i = 0; i < raw_signal->timings_size; ++i) { + // TODO: Any infrared_check_decoder_ready() magic? + const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]); + + if(message) { + is_decoded = true; + printf( + "Protocol: %s address: 0x%lX command: 0x%lX %s\r\n", + infrared_get_protocol_name(message->protocol), + message->address, + message->command, + (message->repeat ? "R" : "")); + if(output_file && !message->repeat) { + infrared_signal_set_message(signal, message); + if(!infrared_cli_save_signal(signal, output_file, signal_name)) break; + } + } + + level = !level; + } + + if(i == raw_signal->timings_size) { + if(!is_decoded && output_file) { + infrared_signal_set_raw_signal( + signal, + raw_signal->timings, + raw_signal->timings_size, + raw_signal->frequency, + raw_signal->duty_cycle); + ret = infrared_cli_save_signal(signal, output_file, signal_name); + } else { + ret = true; + } + } + + infrared_reset_decoder(decoder); + infrared_signal_free(signal); + return ret; +} + +static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) { + bool ret = false; + + InfraredSignal* signal = infrared_signal_alloc(); + InfraredDecoderHandler* decoder = infrared_alloc_decoder(); + + string_t tmp; + string_init(tmp); + + while(infrared_signal_read(signal, input_file, tmp)) { + ret = false; + if(!infrared_signal_is_valid(signal)) { + printf("Invalid signal\r\n"); + break; + } + if(!infrared_signal_is_raw(signal)) { + if(output_file && + !infrared_cli_save_signal(signal, output_file, string_get_cstr(tmp))) { + break; + } else { + printf("Skipping decoded signal\r\n"); + continue; + } + } + InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal); + printf("Raw signal: %s, %u samples\r\n", string_get_cstr(tmp), raw_signal->timings_size); + if(!infrared_cli_decode_raw_signal(raw_signal, decoder, output_file, string_get_cstr(tmp))) + break; + ret = true; + } + + infrared_free_decoder(decoder); + infrared_signal_free(signal); + string_clear(tmp); + + return ret; +} + +static void infrared_cli_process_decode(Cli* cli, string_t args) { + UNUSED(cli); + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage); + FlipperFormat* output_file = NULL; + + uint32_t version; + string_t tmp, header, input_path, output_path; + string_init(tmp); + string_init(header); + string_init(input_path); + string_init(output_path); + + do { + if(!args_read_probably_quoted_string_and_trim(args, input_path)) { + printf("Wrong arguments.\r\n"); + infrared_cli_print_usage(); + break; + } + args_read_probably_quoted_string_and_trim(args, output_path); + if(!flipper_format_buffered_file_open_existing(input_file, string_get_cstr(input_path))) { + printf("Failed to open file for reading: \"%s\"\r\n", string_get_cstr(input_path)); + break; + } + if(!flipper_format_read_header(input_file, header, &version) || + (!string_start_with_str_p(header, "IR")) || version != 1) { + printf("Invalid or corrupted input file: \"%s\"\r\n", string_get_cstr(input_path)); + break; + } + if(!string_empty_p(output_path)) { + printf("Writing output to file: \"%s\"\r\n", string_get_cstr(output_path)); + output_file = flipper_format_file_alloc(storage); + } + if(output_file && + !flipper_format_file_open_always(output_file, string_get_cstr(output_path))) { + printf("Failed to open file for writing: \"%s\"\r\n", string_get_cstr(output_path)); + break; + } + if(output_file && !flipper_format_write_header(output_file, header, version)) { + printf("Failed to write to the output file: \"%s\"\r\n", string_get_cstr(output_path)); + break; + } + if(!infrared_cli_decode_file(input_file, output_file)) { + break; + } + printf("File successfully decoded.\r\n"); + } while(false); + + string_clear(tmp); + string_clear(header); + string_clear(input_path); + string_clear(output_path); + + flipper_format_free(input_file); + if(output_file) flipper_format_free(output_file); + furi_record_close(RECORD_STORAGE); +} + static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { UNUSED(context); if(furi_hal_infrared_is_busy()) { @@ -169,18 +328,15 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { return; } + string_t command; + string_init(command); + args_read_string_and_trim(args, command); + size_t i = 0; for(; i < COUNT_OF(infrared_cli_commands); ++i) { - size_t size = strlen(infrared_cli_commands[i].cmd); - bool cmd_found = !strncmp(string_get_cstr(args), infrared_cli_commands[i].cmd, size); - if(cmd_found) { - if(string_size(args) == size) { - break; - } - if(string_get_cstr(args)[size] == ' ') { - string_right(args, size + 1); - break; - } + size_t cmd_len = strlen(infrared_cli_commands[i].cmd); + if(!strncmp(string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) { + break; } } @@ -189,6 +345,8 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { } else { infrared_cli_print_usage(); } + + string_clear(command); } void infrared_on_system_start() { #ifdef SRV_CLI diff --git a/applications/lfrfid/lfrfid.c b/applications/lfrfid/lfrfid.c new file mode 100644 index 0000000000..87332b1ac4 --- /dev/null +++ b/applications/lfrfid/lfrfid.c @@ -0,0 +1,316 @@ +#include "lfrfid_i.h" + +static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + LfRfid* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool lfrfid_debug_back_event_callback(void* context) { + furi_assert(context); + LfRfid* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { + furi_assert(context); + LfRfid* app = (LfRfid*)context; + + if(rpc_event == RpcAppEventSessionClose) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcSessionClose); + // Detach RPC + rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); + app->rpc_ctx = NULL; + } else if(rpc_event == RpcAppEventAppExit) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventExit); + } else if(rpc_event == RpcAppEventLoadFile) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventRpcLoadFile); + } else { + rpc_system_app_confirm(app->rpc_ctx, rpc_event, false); + } +} + +static LfRfid* lfrfid_alloc() { + LfRfid* lfrfid = malloc(sizeof(LfRfid)); + + lfrfid->storage = furi_record_open(RECORD_STORAGE); + lfrfid->dialogs = furi_record_open(RECORD_DIALOGS); + + string_init(lfrfid->file_name); + string_init(lfrfid->raw_file_name); + string_init_set_str(lfrfid->file_path, LFRFID_APP_FOLDER); + + lfrfid->dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + + size_t size = protocol_dict_get_max_data_size(lfrfid->dict); + lfrfid->new_key_data = (uint8_t*)malloc(size); + lfrfid->old_key_data = (uint8_t*)malloc(size); + + lfrfid->lfworker = lfrfid_worker_alloc(lfrfid->dict); + + lfrfid->view_dispatcher = view_dispatcher_alloc(); + lfrfid->scene_manager = scene_manager_alloc(&lfrfid_scene_handlers, lfrfid); + view_dispatcher_enable_queue(lfrfid->view_dispatcher); + view_dispatcher_set_event_callback_context(lfrfid->view_dispatcher, lfrfid); + view_dispatcher_set_custom_event_callback( + lfrfid->view_dispatcher, lfrfid_debug_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + lfrfid->view_dispatcher, lfrfid_debug_back_event_callback); + + // Open GUI record + lfrfid->gui = furi_record_open(RECORD_GUI); + + // Open Notification record + lfrfid->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Submenu + lfrfid->submenu = submenu_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewSubmenu, submenu_get_view(lfrfid->submenu)); + + // Dialog + lfrfid->dialog_ex = dialog_ex_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewDialogEx, dialog_ex_get_view(lfrfid->dialog_ex)); + + // Popup + lfrfid->popup = popup_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewPopup, popup_get_view(lfrfid->popup)); + + // Widget + lfrfid->widget = widget_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewWidget, widget_get_view(lfrfid->widget)); + + // Text Input + lfrfid->text_input = text_input_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewTextInput, text_input_get_view(lfrfid->text_input)); + + // Byte Input + lfrfid->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewByteInput, byte_input_get_view(lfrfid->byte_input)); + + // Read custom view + lfrfid->read_view = lfrfid_view_read_alloc(); + view_dispatcher_add_view( + lfrfid->view_dispatcher, LfRfidViewRead, lfrfid_view_read_get_view(lfrfid->read_view)); + + return lfrfid; +} + +static void lfrfid_free(LfRfid* lfrfid) { + furi_assert(lfrfid); + + string_clear(lfrfid->raw_file_name); + string_clear(lfrfid->file_name); + string_clear(lfrfid->file_path); + protocol_dict_free(lfrfid->dict); + + lfrfid_worker_free(lfrfid->lfworker); + + if(lfrfid->rpc_ctx) { + rpc_system_app_set_callback(lfrfid->rpc_ctx, NULL, NULL); + rpc_system_app_send_exited(lfrfid->rpc_ctx); + } + + free(lfrfid->new_key_data); + free(lfrfid->old_key_data); + + // Submenu + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewSubmenu); + submenu_free(lfrfid->submenu); + + // DialogEx + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewDialogEx); + dialog_ex_free(lfrfid->dialog_ex); + + // Popup + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewPopup); + popup_free(lfrfid->popup); + + // Widget + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewWidget); + widget_free(lfrfid->widget); + + // TextInput + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewTextInput); + text_input_free(lfrfid->text_input); + + // ByteInput + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewByteInput); + byte_input_free(lfrfid->byte_input); + + // Read custom view + view_dispatcher_remove_view(lfrfid->view_dispatcher, LfRfidViewRead); + lfrfid_view_read_free(lfrfid->read_view); + + // View Dispatcher + view_dispatcher_free(lfrfid->view_dispatcher); + + // Scene Manager + scene_manager_free(lfrfid->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + lfrfid->gui = NULL; + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + lfrfid->notifications = NULL; + + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); + + free(lfrfid); +} + +int32_t lfrfid_app(void* p) { + LfRfid* app = lfrfid_alloc(); + char* args = p; + + lfrfid_make_app_folder(app); + + if(args && strlen(args)) { + uint32_t rpc_ctx_ptr = 0; + if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { + app->rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr; + rpc_system_app_set_callback(app->rpc_ctx, rpc_command_callback, app); + rpc_system_app_send_started(app->rpc_ctx); + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeDesktop); + scene_manager_next_scene(app->scene_manager, LfRfidSceneRpc); + } else { + string_set_str(app->file_path, args); + lfrfid_load_key_data(app, app->file_path, true); + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + } + + } else { + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + scene_manager_next_scene(app->scene_manager, LfRfidSceneStart); + } + + view_dispatcher_run(app->view_dispatcher); + + lfrfid_free(app); + + return 0; +} + +bool lfrfid_save_key(LfRfid* app) { + furi_assert(app); + + bool result = false; + + lfrfid_make_app_folder(app); + + if(string_end_with_str_p(app->file_path, LFRFID_APP_EXTENSION)) { + size_t filename_start = string_search_rchar(app->file_path, '/'); + string_left(app->file_path, filename_start); + } + + string_cat_printf( + app->file_path, "/%s%s", string_get_cstr(app->file_name), LFRFID_APP_EXTENSION); + + result = lfrfid_save_key_data(app, app->file_path); + return result; +} + +bool lfrfid_load_key_from_file_select(LfRfid* app) { + furi_assert(app); + + bool result = dialog_file_browser_show( + app->dialogs, + app->file_path, + app->file_path, + LFRFID_APP_EXTENSION, + true, + &I_125_10px, + true); + + if(result) { + result = lfrfid_load_key_data(app, app->file_path, true); + } + + return result; +} + +bool lfrfid_delete_key(LfRfid* app) { + furi_assert(app); + + return storage_simply_remove(app->storage, string_get_cstr(app->file_path)); +} + +bool lfrfid_load_key_data(LfRfid* app, string_t path, bool show_dialog) { + bool result = false; + + do { + app->protocol_id = lfrfid_dict_file_load(app->dict, string_get_cstr(path)); + if(app->protocol_id == PROTOCOL_NO) break; + + path_extract_filename(path, app->file_name, true); + result = true; + } while(0); + + if((!result) && (show_dialog)) { + dialog_message_show_storage_error(app->dialogs, "Cannot load\nkey file"); + } + + return result; +} + +bool lfrfid_save_key_data(LfRfid* app, string_t path) { + bool result = lfrfid_dict_file_save(app->dict, app->protocol_id, string_get_cstr(path)); + + if(!result) { + dialog_message_show_storage_error(app->dialogs, "Cannot save\nkey file"); + } + + return result; +} + +void lfrfid_make_app_folder(LfRfid* app) { + furi_assert(app); + + if(!storage_simply_mkdir(app->storage, LFRFID_APP_FOLDER)) { + dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder"); + } +} + +void lfrfid_text_store_set(LfRfid* app, const char* text, ...) { + furi_assert(app); + va_list args; + va_start(args, text); + + vsnprintf(app->text_store, LFRFID_TEXT_STORE_SIZE, text, args); + + va_end(args); +} + +void lfrfid_text_store_clear(LfRfid* app) { + furi_assert(app); + memset(app->text_store, 0, sizeof(app->text_store)); +} + +void lfrfid_popup_timeout_callback(void* context) { + LfRfid* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventPopupClosed); +} + +void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context) { + LfRfid* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void lfrfid_text_input_callback(void* context) { + LfRfid* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventNext); +} \ No newline at end of file diff --git a/applications/lfrfid/lfrfid_app.cpp b/applications/lfrfid/lfrfid_app.cpp deleted file mode 100644 index 9373ca8c71..0000000000 --- a/applications/lfrfid/lfrfid_app.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#include "lfrfid_app.h" -#include "assets_icons.h" -#include -#include "m-string.h" -#include "scene/lfrfid_app_scene_start.h" -#include "scene/lfrfid_app_scene_read.h" -#include "scene/lfrfid_app_scene_read_success.h" -#include "scene/lfrfid_app_scene_retry_confirm.h" -#include "scene/lfrfid_app_scene_exit_confirm.h" -#include "scene/lfrfid_app_scene_read_menu.h" -#include "scene/lfrfid_app_scene_write.h" -#include "scene/lfrfid_app_scene_write_success.h" -#include "scene/lfrfid_app_scene_emulate.h" -#include "scene/lfrfid_app_scene_save_name.h" -#include "scene/lfrfid_app_scene_save_success.h" -#include "scene/lfrfid_app_scene_select_key.h" -#include "scene/lfrfid_app_scene_saved_key_menu.h" -#include "scene/lfrfid_app_scene_save_data.h" -#include "scene/lfrfid_app_scene_save_type.h" -#include "scene/lfrfid_app_scene_saved_info.h" -#include "scene/lfrfid_app_scene_delete_confirm.h" -#include "scene/lfrfid_app_scene_delete_success.h" -#include "scene/lfrfid_app_scene_rpc.h" -#include "scene/lfrfid_app_scene_extra_actions.h" -#include "scene/lfrfid_app_scene_raw_info.h" -#include "scene/lfrfid_app_scene_raw_name.h" -#include "scene/lfrfid_app_scene_raw_read.h" -#include "scene/lfrfid_app_scene_raw_success.h" - -#include -#include - -#include - -const char* LfRfidApp::app_folder = ANY_PATH("lfrfid"); -const char* LfRfidApp::app_sd_folder = EXT_PATH("lfrfid"); -const char* LfRfidApp::app_extension = ".rfid"; -const char* LfRfidApp::app_filetype = "Flipper RFID key"; - -LfRfidApp::LfRfidApp() - : scene_controller{this} - , notification{RECORD_NOTIFICATION} - , storage{RECORD_STORAGE} - , dialogs{RECORD_DIALOGS} - , text_store(40) { - string_init(file_name); - string_init(raw_file_name); - string_init_set_str(file_path, app_folder); - - dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); - - size_t size = protocol_dict_get_max_data_size(dict); - new_key_data = (uint8_t*)malloc(size); - old_key_data = (uint8_t*)malloc(size); - - lfworker = lfrfid_worker_alloc(dict); -} - -LfRfidApp::~LfRfidApp() { - string_clear(raw_file_name); - string_clear(file_name); - string_clear(file_path); - protocol_dict_free(dict); - - lfrfid_worker_free(lfworker); - - if(rpc_ctx) { - rpc_system_app_set_callback(rpc_ctx, NULL, NULL); - rpc_system_app_send_exited(rpc_ctx); - } - - free(new_key_data); - free(old_key_data); -} - -static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { - furi_assert(context); - LfRfidApp* app = static_cast(context); - - if(rpc_event == RpcAppEventSessionClose) { - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::RpcSessionClose; - app->view_controller.send_event(&event); - // Detach RPC - rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); - app->rpc_ctx = NULL; - } else if(rpc_event == RpcAppEventAppExit) { - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Exit; - app->view_controller.send_event(&event); - } else if(rpc_event == RpcAppEventLoadFile) { - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::RpcLoadFile; - app->view_controller.send_event(&event); - } else { - rpc_system_app_confirm(app->rpc_ctx, rpc_event, false); - } -} - -void LfRfidApp::run(void* _args) { - const char* args = reinterpret_cast(_args); - - make_app_folder(); - - if(args && strlen(args)) { - uint32_t rpc_ctx_ptr = 0; - if(sscanf(args, "RPC %lX", &rpc_ctx_ptr) == 1) { - rpc_ctx = (RpcAppSystem*)rpc_ctx_ptr; - rpc_system_app_set_callback(rpc_ctx, rpc_command_callback, this); - rpc_system_app_send_started(rpc_ctx); - view_controller.attach_to_gui(ViewDispatcherTypeDesktop); - scene_controller.add_scene(SceneType::Rpc, new LfRfidAppSceneRpc()); - scene_controller.process(100, SceneType::Rpc); - } else { - string_set_str(file_path, args); - load_key_data(file_path, true); - view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); - scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); - scene_controller.process(100, SceneType::Emulate); - } - - } else { - view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); - scene_controller.add_scene(SceneType::Start, new LfRfidAppSceneStart()); - scene_controller.add_scene(SceneType::Read, new LfRfidAppSceneRead()); - scene_controller.add_scene(SceneType::RetryConfirm, new LfRfidAppSceneRetryConfirm()); - scene_controller.add_scene(SceneType::ExitConfirm, new LfRfidAppSceneExitConfirm()); - scene_controller.add_scene(SceneType::ReadSuccess, new LfRfidAppSceneReadSuccess()); - scene_controller.add_scene(SceneType::ReadKeyMenu, new LfRfidAppSceneReadKeyMenu()); - scene_controller.add_scene(SceneType::Write, new LfRfidAppSceneWrite()); - scene_controller.add_scene(SceneType::WriteSuccess, new LfRfidAppSceneWriteSuccess()); - scene_controller.add_scene(SceneType::Emulate, new LfRfidAppSceneEmulate()); - scene_controller.add_scene(SceneType::SaveName, new LfRfidAppSceneSaveName()); - scene_controller.add_scene(SceneType::SaveSuccess, new LfRfidAppSceneSaveSuccess()); - scene_controller.add_scene(SceneType::SelectKey, new LfRfidAppSceneSelectKey()); - scene_controller.add_scene(SceneType::SavedKeyMenu, new LfRfidAppSceneSavedKeyMenu()); - scene_controller.add_scene(SceneType::SaveData, new LfRfidAppSceneSaveData()); - scene_controller.add_scene(SceneType::SaveType, new LfRfidAppSceneSaveType()); - scene_controller.add_scene(SceneType::SavedInfo, new LfRfidAppSceneSavedInfo()); - scene_controller.add_scene(SceneType::DeleteConfirm, new LfRfidAppSceneDeleteConfirm()); - scene_controller.add_scene(SceneType::DeleteSuccess, new LfRfidAppSceneDeleteSuccess()); - scene_controller.add_scene(SceneType::ExtraActions, new LfRfidAppSceneExtraActions()); - scene_controller.add_scene(SceneType::RawInfo, new LfRfidAppSceneRawInfo()); - scene_controller.add_scene(SceneType::RawName, new LfRfidAppSceneRawName()); - scene_controller.add_scene(SceneType::RawRead, new LfRfidAppSceneRawRead()); - scene_controller.add_scene(SceneType::RawSuccess, new LfRfidAppSceneRawSuccess()); - scene_controller.process(100); - } -} - -bool LfRfidApp::save_key() { - bool result = false; - - make_app_folder(); - - if(string_end_with_str_p(file_path, app_extension)) { - size_t filename_start = string_search_rchar(file_path, '/'); - string_left(file_path, filename_start); - } - - string_cat_printf(file_path, "/%s%s", string_get_cstr(file_name), app_extension); - - result = save_key_data(file_path); - return result; -} - -bool LfRfidApp::load_key_from_file_select(bool need_restore) { - if(!need_restore) { - string_set_str(file_path, app_folder); - } - - bool result = dialog_file_browser_show( - dialogs, file_path, file_path, app_extension, true, &I_125_10px, true); - - if(result) { - result = load_key_data(file_path, true); - } - - return result; -} - -bool LfRfidApp::delete_key() { - return storage_simply_remove(storage, string_get_cstr(file_path)); -} - -bool LfRfidApp::load_key_data(string_t path, bool show_dialog) { - bool result = false; - - do { - protocol_id = lfrfid_dict_file_load(dict, string_get_cstr(path)); - if(protocol_id == PROTOCOL_NO) break; - - path_extract_filename(path, file_name, true); - result = true; - } while(0); - - if((!result) && (show_dialog)) { - dialog_message_show_storage_error(dialogs, "Cannot load\nkey file"); - } - - return result; -} - -bool LfRfidApp::save_key_data(string_t path) { - bool result = lfrfid_dict_file_save(dict, protocol_id, string_get_cstr(path)); - - if(!result) { - dialog_message_show_storage_error(dialogs, "Cannot save\nkey file"); - } - - return result; -} - -void LfRfidApp::make_app_folder() { - if(!storage_simply_mkdir(storage, app_folder)) { - dialog_message_show_storage_error(dialogs, "Cannot create\napp folder"); - } -} diff --git a/applications/lfrfid/lfrfid_app.h b/applications/lfrfid/lfrfid_app.h deleted file mode 100644 index 153218dbdf..0000000000 --- a/applications/lfrfid/lfrfid_app.h +++ /dev/null @@ -1,137 +0,0 @@ -#pragma once -#include "m-string.h" -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include "view/container_vm.h" - -#include -#include -#include - -#include "rpc/rpc_app.h" - -#include -#include -#include -#include - -#define LFRFID_KEY_NAME_SIZE 22 - -class LfRfidApp { -public: - enum class EventType : uint8_t { - GENERIC_EVENT_ENUM_VALUES, - Next, - MenuSelected, - Stay, - Retry, - Exit, - ReadEventSenseStart, - ReadEventSenseEnd, - ReadEventSenseCardStart, - ReadEventSenseCardEnd, - ReadEventStartASK, - ReadEventStartPSK, - ReadEventDone, - ReadEventOverrun, - ReadEventError, - WriteEventOK, - WriteEventProtocolCannotBeWritten, - WriteEventFobCannotBeWritten, - WriteEventTooLongToWrite, - RpcLoadFile, - RpcSessionClose, - }; - - enum class SceneType : uint8_t { - GENERIC_SCENE_ENUM_VALUES, - Read, - ReadSuccess, - RetryConfirm, - ExitConfirm, - ReadKeyMenu, - Write, - WriteSuccess, - Emulate, - SaveName, - SaveSuccess, - SelectKey, - SavedKeyMenu, - SaveData, - SaveType, - SavedInfo, - DeleteConfirm, - DeleteSuccess, - Rpc, - ExtraActions, - RawInfo, - RawName, - RawRead, - RawSuccess, - }; - - class Event { - public: - union { - int32_t signed_int; - } payload; - - EventType type; - }; - - SceneController, LfRfidApp> scene_controller; - ViewController - view_controller; - - ~LfRfidApp(); - LfRfidApp(); - - RecordController notification; - RecordController storage; - RecordController dialogs; - - TextStore text_store; - - string_t file_path; - - RpcAppSystem* rpc_ctx; - - void run(void* args); - - static const char* app_folder; - static const char* app_sd_folder; - static const char* app_extension; - static const char* app_filetype; - - bool save_key(); - bool load_key_from_file_select(bool need_restore); - bool delete_key(); - - bool load_key_data(string_t path, bool show_dialog); - bool save_key_data(string_t path); - - void make_app_folder(); - - ProtocolDict* dict; - LFRFIDWorker* lfworker; - string_t file_name; - ProtocolId protocol_id; - LFRFIDWorkerReadType read_type; - - uint8_t* old_key_data; - uint8_t* new_key_data; - - string_t raw_file_name; -}; diff --git a/applications/lfrfid/lfrfid_app_launcher.cpp b/applications/lfrfid/lfrfid_app_launcher.cpp deleted file mode 100644 index 2855850f56..0000000000 --- a/applications/lfrfid/lfrfid_app_launcher.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "lfrfid_app.h" - -// app enter function -extern "C" int32_t lfrfid_app(void* args) { - LfRfidApp* app = new LfRfidApp(); - app->run(args); - delete app; - - return 0; -} diff --git a/applications/lfrfid/lfrfid_cli.c b/applications/lfrfid/lfrfid_cli.c index 9a6930a671..281a0e9638 100644 --- a/applications/lfrfid/lfrfid_cli.c +++ b/applications/lfrfid/lfrfid_cli.c @@ -114,7 +114,7 @@ static void lfrfid_cli_read(Cli* cli, string_t args) { string_t info; string_init(info); protocol_dict_render_data(dict, info, context.protocol); - if(string_size(info) > 0) { + if(!string_empty_p(info)) { printf("%s\r\n", string_get_cstr(info)); } string_clear(info); diff --git a/applications/lfrfid/lfrfid_i.h b/applications/lfrfid/lfrfid_i.h new file mode 100644 index 0000000000..77e872527e --- /dev/null +++ b/applications/lfrfid/lfrfid_i.h @@ -0,0 +1,145 @@ +#pragma once + +#include "m-string.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#define LFRFID_KEY_NAME_SIZE 22 +#define LFRFID_TEXT_STORE_SIZE 40 + +#define LFRFID_APP_FOLDER ANY_PATH("lfrfid") +#define LFRFID_SD_FOLDER EXT_PATH("lfrfid") +#define LFRFID_APP_EXTENSION ".rfid" +#define LFRFID_APP_SHADOW_EXTENSION ".shd" + +#define LFRFID_APP_RAW_ASK_EXTENSION ".ask.raw" +#define LFRFID_APP_RAW_PSK_EXTENSION ".psk.raw" + +enum LfRfidCustomEvent { + LfRfidEventNext = 100, + LfRfidEventExit, + LfRfidEventPopupClosed, + LfRfidEventReadSenseStart, + LfRfidEventReadSenseEnd, + LfRfidEventReadSenseCardStart, + LfRfidEventReadSenseCardEnd, + LfRfidEventReadStartASK, + LfRfidEventReadStartPSK, + LfRfidEventReadDone, + LfRfidEventReadOverrun, + LfRfidEventReadError, + LfRfidEventWriteOK, + LfRfidEventWriteProtocolCannotBeWritten, + LfRfidEventWriteFobCannotBeWritten, + LfRfidEventWriteTooLongToWrite, + LfRfidEventRpcLoadFile, + LfRfidEventRpcSessionClose, +}; + +typedef enum { + LfRfidRpcStateIdle, + LfRfidRpcStateEmulating, +} LfRfidRpcState; + +typedef struct LfRfid LfRfid; + +struct LfRfid { + LFRFIDWorker* lfworker; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notifications; + SceneManager* scene_manager; + Storage* storage; + DialogsApp* dialogs; + Widget* widget; + + char text_store[LFRFID_TEXT_STORE_SIZE + 1]; + string_t file_path; + string_t file_name; + string_t raw_file_name; + + ProtocolDict* dict; + ProtocolId protocol_id; + ProtocolId protocol_id_next; + LFRFIDWorkerReadType read_type; + + uint8_t* old_key_data; + uint8_t* new_key_data; + + RpcAppSystem* rpc_ctx; + LfRfidRpcState rpc_state; + + // Common Views + Submenu* submenu; + DialogEx* dialog_ex; + Popup* popup; + TextInput* text_input; + ByteInput* byte_input; + + // Custom views + LfRfidReadView* read_view; +}; + +typedef enum { + LfRfidViewSubmenu, + LfRfidViewDialogEx, + LfRfidViewPopup, + LfRfidViewWidget, + LfRfidViewTextInput, + LfRfidViewByteInput, + LfRfidViewRead, +} LfRfidView; + +bool lfrfid_save_key(LfRfid* app); + +bool lfrfid_load_key_from_file_select(LfRfid* app); + +bool lfrfid_delete_key(LfRfid* app); + +bool lfrfid_load_key_data(LfRfid* app, string_t path, bool show_dialog); + +bool lfrfid_save_key_data(LfRfid* app, string_t path); + +void lfrfid_make_app_folder(LfRfid* app); + +void lfrfid_text_store_set(LfRfid* app, const char* text, ...); + +void lfrfid_text_store_clear(LfRfid* app); + +void lfrfid_popup_timeout_callback(void* context); + +void lfrfid_widget_callback(GuiButtonType result, InputType type, void* context); + +void lfrfid_text_input_callback(void* context); diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp deleted file mode 100644 index 58ff4dcdfc..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "lfrfid_app_scene_delete_confirm.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_data); - string_init(string_header); - - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Back"); - button->set_callback(app, LfRfidAppSceneDeleteConfirm::back_callback); - - button = container->add(); - button->set_type(ButtonElement::Type::Right, "Delete"); - button->set_callback(app, LfRfidAppSceneDeleteConfirm::delete_callback); - - auto line_1 = container->add(); - auto line_2 = container->add(); - auto line_3 = container->add(); - - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - uint8_t* data = (uint8_t*)malloc(size); - protocol_dict_get_data(app->dict, app->protocol_id, data, size); - for(uint8_t i = 0; i < MIN(size, (size_t)8); i++) { - if(i != 0) { - string_cat_printf(string_data, " "); - } - - string_cat_printf(string_data, "%02X", data[i]); - } - free(data); - - string_printf(string_header, "Delete %s?", string_get_cstr(app->file_name)); - line_1->set_text( - string_get_cstr(string_header), 64, 0, 128 - 2, AlignCenter, AlignTop, FontPrimary); - line_2->set_text( - string_get_cstr(string_data), 64, 19, 0, AlignCenter, AlignTop, FontSecondary); - line_3->set_text( - protocol_dict_get_name(app->dict, app->protocol_id), - 64, - 49, - 0, - AlignCenter, - AlignBottom, - FontSecondary); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneDeleteConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - app->delete_key(); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteSuccess); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Stay) { - app->scene_controller.switch_to_previous_scene(); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneDeleteConfirm::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - string_clear(string_data); - string_clear(string_header); -} - -void LfRfidAppSceneDeleteConfirm::back_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Stay; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneDeleteConfirm::delete_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h b/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h deleted file mode 100644 index f9daed5432..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneDeleteConfirm : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void back_callback(void* context); - static void delete_callback(void* context); - - string_t string_header; - string_t string_data; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_delete_success.cpp deleted file mode 100644 index 9820338d12..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_success.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "lfrfid_app_scene_delete_success.h" - -void LfRfidAppSceneDeleteSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - popup->set_icon(0, 2, &I_DolphinMafia_115x62); - popup->set_header("Deleted", 83, 19, AlignLeft, AlignBottom); - popup->set_context(app); - popup->set_callback(LfRfidAppSceneDeleteSuccess::timeout_callback); - popup->set_timeout(1500); - popup->enable_timeout(); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneDeleteSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Back) { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::SelectKey}); - - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneDeleteSuccess::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneDeleteSuccess::timeout_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_delete_success.h b/applications/lfrfid/scene/lfrfid_app_scene_delete_success.h deleted file mode 100644 index 009b9f25db..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_delete_success.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneDeleteSuccess : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void timeout_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp b/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp deleted file mode 100644 index 02cb011d13..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "lfrfid_app_scene_emulate.h" -#include -#include - -void LfRfidAppSceneEmulate::on_enter(LfRfidApp* app, bool /* need_restore */) { - DOLPHIN_DEED(DolphinDeedRfidEmulate); - auto popup = app->view_controller.get(); - - popup->set_header("Emulating", 89, 30, AlignCenter, AlignTop); - if(string_size(app->file_name)) { - popup->set_text(string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); - } else { - popup->set_text( - protocol_dict_get_name(app->dict, app->protocol_id), 89, 43, AlignCenter, AlignTop); - } - popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); - - app->view_controller.switch_to(); - lfrfid_worker_start_thread(app->lfworker); - lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); - notification_message(app->notification, &sequence_blink_start_magenta); -} - -bool LfRfidAppSceneEmulate::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - UNUSED(app); - UNUSED(event); - bool consumed = false; - return consumed; -} - -void LfRfidAppSceneEmulate::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - lfrfid_worker_stop(app->lfworker); - lfrfid_worker_stop_thread(app->lfworker); - notification_message(app->notification, &sequence_blink_stop); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_emulate.h b/applications/lfrfid/scene/lfrfid_app_scene_emulate.h deleted file mode 100644 index 13d2b857dc..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_emulate.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneEmulate : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp deleted file mode 100644 index be070b40e0..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "lfrfid_app_scene_exit_confirm.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneExitConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Exit"); - button->set_callback(app, LfRfidAppSceneExitConfirm::exit_callback); - - button = container->add(); - button->set_type(ButtonElement::Type::Right, "Stay"); - button->set_callback(app, LfRfidAppSceneExitConfirm::stay_callback); - - auto line_1 = container->add(); - auto line_2 = container->add(); - - line_1->set_text("Exit to RFID Menu?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); - line_2->set_text( - "All unsaved data will be lost!", 64, 31, 0, AlignCenter, AlignBottom, FontSecondary); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneExitConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - app->scene_controller.search_and_switch_to_previous_scene({LfRfidApp::SceneType::Start}); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Stay) { - app->scene_controller.switch_to_previous_scene(); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneExitConfirm::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneExitConfirm::exit_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneExitConfirm::stay_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Stay; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.h b/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.h deleted file mode 100644 index a0a1023258..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_exit_confirm.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneExitConfirm : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void exit_callback(void* context); - static void stay_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp deleted file mode 100644 index 91e26d23a8..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "lfrfid_app_scene_extra_actions.h" - -typedef enum { - SubmenuASK, - SubmenuPSK, - SubmenuRAW, -} SubmenuIndex; - -void LfRfidAppSceneExtraActions::on_enter(LfRfidApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - - submenu->add_item("Read ASK (FDX,Regular)", SubmenuASK, submenu_callback, app); - submenu->add_item("Read PSK (Indala)", SubmenuPSK, submenu_callback, app); - - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - submenu->add_item("Read RAW RFID data", SubmenuRAW, submenu_callback, app); - } - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneExtraActions::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.signed_int; - switch(event->payload.signed_int) { - case SubmenuASK: - app->read_type = LFRFIDWorkerReadTypeASKOnly; - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); - break; - case SubmenuPSK: - app->read_type = LFRFIDWorkerReadTypePSKOnly; - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); - break; - case SubmenuRAW: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawName); - break; - } - - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneExtraActions::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneExtraActions::submenu_callback(void* context, uint32_t index) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - - event.type = LfRfidApp::EventType::MenuSelected; - event.payload.signed_int = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h b/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h deleted file mode 100644 index dcd746146c..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_extra_actions.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneExtraActions : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp deleted file mode 100644 index ce3634b2ac..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_raw_info.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "lfrfid_app_scene_raw_info.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -static void ok_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} - -static void back_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneRawInfo::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_info); - - auto container = app->view_controller.get(); - - bool sd_exist = storage_sd_status(app->storage) == FSE_OK; - if(!sd_exist) { - auto icon = container->add(); - icon->set_icon(0, 0, &I_SDQuestion_35x43); - auto line = container->add(); - line->set_text( - "No SD card found.\nThis function will not\nwork without\nSD card.", - 81, - 4, - 0, - AlignCenter, - AlignTop, - FontSecondary); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Back"); - button->set_callback(app, back_callback); - } else { - string_printf( - string_info, - "RAW RFID data reader\r\n" - "1) Put the Flipper on your card\r\n" - "2) Press OK\r\n" - "3) Wait until data is read"); - - auto line = container->add(); - line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Center, "OK"); - button->set_callback(app, ok_callback); - } - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneRawInfo::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - if(event->type == LfRfidApp::EventType::Next) { - app->scene_controller.switch_to_scene({LfRfidApp::SceneType::RawRead}); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::ExtraActions}); - consumed = true; - } - return consumed; -} - -void LfRfidAppSceneRawInfo::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - string_clear(string_info); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_info.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_info.h deleted file mode 100644 index eecca14368..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_raw_info.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRawInfo : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - string_t string_info; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp deleted file mode 100644 index 0ad346198e..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_raw_name.cpp +++ /dev/null @@ -1,46 +0,0 @@ - -#include "lfrfid_app_scene_raw_name.h" -#include "m-string.h" -#include -#include - -void LfRfidAppSceneRawName::on_enter(LfRfidApp* app, bool /* need_restore */) { - const char* key_name = string_get_cstr(app->raw_file_name); - - bool key_name_empty = (string_size(app->raw_file_name) == 0); - if(key_name_empty) { - app->text_store.set("RfidRecord"); - } else { - app->text_store.set("%s", key_name); - } - - auto text_input = app->view_controller.get(); - text_input->set_header_text("Name the raw file"); - - text_input->set_result_callback( - save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE, key_name_empty); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneRawName::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - string_set_str(app->raw_file_name, app->text_store.text); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawInfo); - } - - return consumed; -} - -void LfRfidAppSceneRawName::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneRawName::save_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_name.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_name.h deleted file mode 100644 index 225d135e5e..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_raw_name.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRawName : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void save_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp deleted file mode 100644 index 0d04e6bc75..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_raw_read.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include "lfrfid_app_scene_raw_read.h" -#include - -#define RAW_READ_TIME 5000 - -static void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* ctx) { - LfRfidApp* app = static_cast(ctx); - LfRfidApp::Event event; - - switch(result) { - case LFRFIDWorkerReadRawFileError: - event.type = LfRfidApp::EventType::ReadEventError; - break; - case LFRFIDWorkerReadRawOverrun: - event.type = LfRfidApp::EventType::ReadEventOverrun; - break; - } - - app->view_controller.send_event(&event); -} - -static void timer_callback(void* ctx) { - LfRfidApp* app = static_cast(ctx); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::ReadEventDone; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneRawRead::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_file_name); - auto popup = app->view_controller.get(); - popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); - app->view_controller.switch_to(); - lfrfid_worker_start_thread(app->lfworker); - app->make_app_folder(); - - timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app); - furi_timer_start(timer, RAW_READ_TIME); - string_printf( - string_file_name, "%s/%s.ask.raw", app->app_sd_folder, string_get_cstr(app->raw_file_name)); - popup->set_header("Reading\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop); - lfrfid_worker_read_raw_start( - app->lfworker, - string_get_cstr(string_file_name), - LFRFIDWorkerReadTypeASKOnly, - lfrfid_read_callback, - app); - - notification_message(app->notification, &sequence_blink_start_cyan); - - is_psk = false; - error = false; -} - -bool LfRfidAppSceneRawRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - UNUSED(app); - bool consumed = true; - auto popup = app->view_controller.get(); - - switch(event->type) { - case LfRfidApp::EventType::ReadEventError: - error = true; - popup->set_header("Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop); - notification_message(app->notification, &sequence_blink_start_red); - furi_timer_stop(timer); - break; - case LfRfidApp::EventType::ReadEventDone: - if(!error) { - if(is_psk) { - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::RawSuccess); - } else { - popup->set_header("Reading\nRAW RFID\nPSK", 89, 30, AlignCenter, AlignTop); - notification_message(app->notification, &sequence_blink_start_yellow); - lfrfid_worker_stop(app->lfworker); - string_printf( - string_file_name, - "%s/%s.psk.raw", - app->app_sd_folder, - string_get_cstr(app->raw_file_name)); - lfrfid_worker_read_raw_start( - app->lfworker, - string_get_cstr(string_file_name), - LFRFIDWorkerReadTypePSKOnly, - lfrfid_read_callback, - app); - furi_timer_start(timer, RAW_READ_TIME); - is_psk = true; - } - } - break; - default: - consumed = false; - break; - } - - return consumed; -} - -void LfRfidAppSceneRawRead::on_exit(LfRfidApp* app) { - notification_message(app->notification, &sequence_blink_stop); - app->view_controller.get()->clean(); - lfrfid_worker_stop(app->lfworker); - lfrfid_worker_stop_thread(app->lfworker); - furi_timer_free(timer); - string_clear(string_file_name); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_read.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_read.h deleted file mode 100644 index 09ef746390..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_raw_read.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRawRead : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - string_t string_file_name; - FuriTimer* timer; - bool is_psk; - bool error; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp deleted file mode 100644 index 227ab580a8..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_raw_success.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "lfrfid_app_scene_raw_success.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneRawSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_info); - - string_printf(string_info, "RAW RFID read success!\r\n"); - string_cat_printf(string_info, "Now you can analyze files\r\n"); - string_cat_printf(string_info, "Or send them to developers"); - - auto container = app->view_controller.get(); - - auto line = container->add(); - line->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Center, "OK"); - button->set_callback(app, LfRfidAppSceneRawSuccess::ok_callback); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneRawSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - if(event->type == LfRfidApp::EventType::Next) { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::ExtraActions}); - consumed = true; - } - return consumed; -} - -void LfRfidAppSceneRawSuccess::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - string_clear(string_info); -} - -void LfRfidAppSceneRawSuccess::ok_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_raw_success.h b/applications/lfrfid/scene/lfrfid_app_scene_raw_success.h deleted file mode 100644 index 0a0b0116b5..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_raw_success.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRawSuccess : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - string_t string_info; - static void ok_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read.cpp deleted file mode 100644 index 120eb1a072..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "lfrfid_app_scene_read.h" -#include - -static void lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* ctx) { - LfRfidApp* app = static_cast(ctx); - LfRfidApp::Event event; - - switch(result) { - case LFRFIDWorkerReadSenseStart: - event.type = LfRfidApp::EventType::ReadEventSenseStart; - break; - case LFRFIDWorkerReadSenseEnd: - event.type = LfRfidApp::EventType::ReadEventSenseEnd; - break; - case LFRFIDWorkerReadSenseCardStart: - event.type = LfRfidApp::EventType::ReadEventSenseCardStart; - break; - case LFRFIDWorkerReadSenseCardEnd: - event.type = LfRfidApp::EventType::ReadEventSenseCardEnd; - break; - case LFRFIDWorkerReadDone: - event.type = LfRfidApp::EventType::ReadEventDone; - break; - case LFRFIDWorkerReadStartASK: - event.type = LfRfidApp::EventType::ReadEventStartASK; - break; - case LFRFIDWorkerReadStartPSK: - event.type = LfRfidApp::EventType::ReadEventStartPSK; - break; - } - - event.payload.signed_int = protocol; - - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneRead::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - DOLPHIN_DEED(DolphinDeedRfidRead); - if(app->read_type == LFRFIDWorkerReadTypePSKOnly) { - popup->set_header("Reading\nLF RFID\nPSK", 89, 30, AlignCenter, AlignTop); - } else { - popup->set_header("Reading\nLF RFID\nASK", 89, 30, AlignCenter, AlignTop); - } - - popup->set_icon(0, 3, &I_RFIDDolphinReceive_97x61); - - app->view_controller.switch_to(); - lfrfid_worker_start_thread(app->lfworker); - lfrfid_worker_read_start(app->lfworker, app->read_type, lfrfid_read_callback, app); - - notification_message(app->notification, &sequence_blink_start_cyan); -} - -bool LfRfidAppSceneRead::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = true; - auto popup = app->view_controller.get(); - - switch(event->type) { - case LfRfidApp::EventType::ReadEventSenseStart: - notification_message(app->notification, &sequence_blink_stop); - notification_message(app->notification, &sequence_blink_start_yellow); - break; - case LfRfidApp::EventType::ReadEventSenseCardStart: - notification_message(app->notification, &sequence_blink_stop); - notification_message(app->notification, &sequence_blink_start_green); - break; - case LfRfidApp::EventType::ReadEventSenseEnd: - case LfRfidApp::EventType::ReadEventSenseCardEnd: - notification_message(app->notification, &sequence_blink_stop); - notification_message(app->notification, &sequence_blink_start_cyan); - break; - case LfRfidApp::EventType::ReadEventDone: - app->protocol_id = event->payload.signed_int; - DOLPHIN_DEED(DolphinDeedRfidReadSuccess); - notification_message(app->notification, &sequence_success); - string_reset(app->file_name); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadSuccess); - break; - case LfRfidApp::EventType::ReadEventStartPSK: - popup->set_header("Reading\nLF RFID\nPSK", 89, 30, AlignCenter, AlignTop); - break; - case LfRfidApp::EventType::ReadEventStartASK: - popup->set_header("Reading\nLF RFID\nASK", 89, 30, AlignCenter, AlignTop); - break; - default: - consumed = false; - break; - } - - return consumed; -} - -void LfRfidAppSceneRead::on_exit(LfRfidApp* app) { - notification_message(app->notification, &sequence_blink_stop); - app->view_controller.get()->clean(); - lfrfid_worker_stop(app->lfworker); - lfrfid_worker_stop_thread(app->lfworker); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read.h b/applications/lfrfid/scene/lfrfid_app_scene_read.h deleted file mode 100644 index b5035b72a0..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRead : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp deleted file mode 100644 index aa3b3f1fba..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "lfrfid_app_scene_read_menu.h" - -typedef enum { - SubmenuSave, - SubmenuEmulate, - SubmenuWrite, -} SubmenuIndex; - -void LfRfidAppSceneReadKeyMenu::on_enter(LfRfidApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - - submenu->add_item("Save", SubmenuSave, submenu_callback, app); - submenu->add_item("Emulate", SubmenuEmulate, submenu_callback, app); - submenu->add_item("Write", SubmenuWrite, submenu_callback, app); - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneReadKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.signed_int; - switch(event->payload.signed_int) { - case SubmenuWrite: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write); - break; - case SubmenuSave: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName); - break; - case SubmenuEmulate: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Emulate); - break; - } - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - app->scene_controller.switch_to_previous_scene(); - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneReadKeyMenu::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneReadKeyMenu::submenu_callback(void* context, uint32_t index) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - - event.type = LfRfidApp::EventType::MenuSelected; - event.payload.signed_int = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.h b/applications/lfrfid/scene/lfrfid_app_scene_read_menu.h deleted file mode 100644 index 2b50b96f9a..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_menu.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneReadKeyMenu : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp deleted file mode 100644 index 277b43a3e5..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "lfrfid_app_scene_read_success.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_info); - string_init(string_header); - - string_init_printf( - string_header, - "%s[%s]", - protocol_dict_get_name(app->dict, app->protocol_id), - protocol_dict_get_manufacturer(app->dict, app->protocol_id)); - - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - uint8_t* data = (uint8_t*)malloc(size); - protocol_dict_get_data(app->dict, app->protocol_id, data, size); - for(uint8_t i = 0; i < size; i++) { - if(i != 0) { - string_cat_printf(string_info, " "); - } - - if(i >= 9) { - string_cat_printf(string_info, "..."); - break; - } else { - string_cat_printf(string_info, "%02X", data[i]); - } - } - free(data); - - string_t render_data; - string_init(render_data); - protocol_dict_render_brief_data(app->dict, render_data, app->protocol_id); - string_cat_printf(string_info, "\r\n%s", string_get_cstr(render_data)); - string_clear(render_data); - - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Retry"); - button->set_callback(app, LfRfidAppSceneReadSuccess::back_callback); - - button = container->add(); - button->set_type(ButtonElement::Type::Right, "More"); - button->set_callback(app, LfRfidAppSceneReadSuccess::more_callback); - - auto header = container->add(); - header->set_text(string_get_cstr(string_header), 0, 2, 0, AlignLeft, AlignTop, FontPrimary); - - auto text = container->add(); - text->set_text(string_get_cstr(string_info), 0, 16, 0, AlignLeft, AlignTop, FontSecondary); - - app->view_controller.switch_to(); - - notification_message_block(app->notification, &sequence_set_green_255); -} - -bool LfRfidAppSceneReadSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ReadKeyMenu); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Retry) { - app->scene_controller.switch_to_next_scene({LfRfidApp::SceneType::RetryConfirm}); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - app->scene_controller.switch_to_next_scene({LfRfidApp::SceneType::ExitConfirm}); - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneReadSuccess::on_exit(LfRfidApp* app) { - notification_message_block(app->notification, &sequence_reset_green); - app->view_controller.get()->clean(); - string_clear(string_info); - string_clear(string_header); -} - -void LfRfidAppSceneReadSuccess::back_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Retry; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneReadSuccess::more_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_read_success.h b/applications/lfrfid/scene/lfrfid_app_scene_read_success.h deleted file mode 100644 index 6d90f63109..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_read_success.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneReadSuccess : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void back_callback(void* context); - static void more_callback(void* context); - - string_t string_header; - string_t string_info; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp b/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp deleted file mode 100644 index c181223238..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "lfrfid_app_scene_retry_confirm.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneRetryConfirm::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto container = app->view_controller.get(); - - auto button = container->add(); - button->set_type(ButtonElement::Type::Left, "Exit"); - button->set_callback(app, LfRfidAppSceneRetryConfirm::exit_callback); - - button = container->add(); - button->set_type(ButtonElement::Type::Right, "Stay"); - button->set_callback(app, LfRfidAppSceneRetryConfirm::stay_callback); - - auto line_1 = container->add(); - auto line_2 = container->add(); - - line_1->set_text("Return to Reading?", 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary); - line_2->set_text( - "All unsaved data will be lost!", 64, 29, 0, AlignCenter, AlignBottom, FontSecondary); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneRetryConfirm::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - app->scene_controller.search_and_switch_to_previous_scene({LfRfidApp::SceneType::Read}); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Stay) { - app->scene_controller.switch_to_previous_scene(); - consumed = true; - } else if(event->type == LfRfidApp::EventType::Back) { - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneRetryConfirm::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneRetryConfirm::exit_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneRetryConfirm::stay_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Stay; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.h b/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.h deleted file mode 100644 index 01b7329c90..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_retry_confirm.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRetryConfirm : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void exit_callback(void* context); - static void stay_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp b/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp deleted file mode 100644 index c2e5ec2a61..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_rpc.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "lfrfid_app_scene_rpc.h" -#include -#include -#include - -void LfRfidAppSceneRpc::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - popup->set_header("LF RFID", 89, 42, AlignCenter, AlignBottom); - popup->set_text("RPC mode", 89, 44, AlignCenter, AlignTop); - popup->set_icon(0, 12, &I_RFIDDolphinSend_97x61); - - app->view_controller.switch_to(); - - notification_message(app->notification, &sequence_display_backlight_on); -} - -bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - UNUSED(app); - UNUSED(event); - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Exit) { - consumed = true; - LfRfidApp::Event view_event; - view_event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&view_event); - rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true); - } else if(event->type == LfRfidApp::EventType::RpcSessionClose) { - consumed = true; - LfRfidApp::Event view_event; - view_event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&view_event); - } else if(event->type == LfRfidApp::EventType::RpcLoadFile) { - const char* arg = rpc_system_app_get_data(app->rpc_ctx); - consumed = true; - bool result = false; - if(arg && !emulating) { - string_set_str(app->file_path, arg); - if(app->load_key_data(app->file_path, false)) { - lfrfid_worker_start_thread(app->lfworker); - lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); - emulating = true; - - auto popup = app->view_controller.get(); - app->text_store.set("emulating\n%s", string_get_cstr(app->file_name)); - popup->set_text(app->text_store.text, 89, 44, AlignCenter, AlignTop); - - notification_message(app->notification, &sequence_blink_start_magenta); - result = true; - } - } - rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result); - } - - return consumed; -} - -void LfRfidAppSceneRpc::on_exit(LfRfidApp* app) { - if(emulating) { - lfrfid_worker_stop(app->lfworker); - lfrfid_worker_stop_thread(app->lfworker); - notification_message(app->notification, &sequence_blink_stop); - } - app->view_controller.get()->clean(); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_rpc.h b/applications/lfrfid/scene/lfrfid_app_scene_rpc.h deleted file mode 100644 index f630dfd35e..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_rpc.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneRpc : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - bool emulating = false; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp deleted file mode 100644 index c506cd7299..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_data.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "lfrfid_app_scene_save_data.h" -#include - -void LfRfidAppSceneSaveData::on_enter(LfRfidApp* app, bool need_restore) { - auto byte_input = app->view_controller.get(); - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - - if(need_restore) { - protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size); - } else { - protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); - } - - protocol_dict_get_data(app->dict, app->protocol_id, app->new_key_data, size); - - byte_input->set_header_text("Enter the data in hex"); - - byte_input->set_result_callback(save_callback, NULL, app, app->new_key_data, size); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSaveData::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size); - DOLPHIN_DEED(DolphinDeedRfidAdd); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveName); - } - - return consumed; -} - -void LfRfidAppSceneSaveData::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneSaveData::save_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_data.h b/applications/lfrfid/scene/lfrfid_app_scene_save_data.h deleted file mode 100644 index d03cae1251..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_data.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSaveData : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void save_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp deleted file mode 100644 index ed58b6453e..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "lfrfid_app_scene_save_name.h" -#include "m-string.h" -#include -#include - -void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool /* need_restore */) { - const char* key_name = string_get_cstr(app->file_name); - - bool key_name_empty = (string_size(app->file_name) == 0); - if(key_name_empty) { - string_set_str(app->file_path, app->app_folder); - set_random_name(app->text_store.text, app->text_store.text_size); - } else { - app->text_store.set("%s", key_name); - } - - auto text_input = app->view_controller.get(); - text_input->set_header_text("Name the card"); - - text_input->set_result_callback( - save_callback, app, app->text_store.text, LFRFID_KEY_NAME_SIZE, key_name_empty); - - string_t folder_path; - string_init(folder_path); - - path_extract_dirname(string_get_cstr(app->file_path), folder_path); - - ValidatorIsFile* validator_is_file = - validator_is_file_alloc_init(string_get_cstr(folder_path), app->app_extension, key_name); - text_input->set_validator(validator_is_file_callback, validator_is_file); - - string_clear(folder_path); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSaveName::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Next) { - if(string_size(app->file_name) > 0) { - app->delete_key(); - } - - string_set_str(app->file_name, app->text_store.text); - - if(app->save_key()) { - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveSuccess); - } else { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::ReadKeyMenu}); - } - } - - return consumed; -} - -void LfRfidAppSceneSaveName::on_exit(LfRfidApp* app) { - void* validator_context = - app->view_controller.get()->get_validator_callback_context(); - app->view_controller.get()->set_validator(NULL, NULL); - validator_is_file_free((ValidatorIsFile*)validator_context); - - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneSaveName::save_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Next; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_name.h b/applications/lfrfid/scene/lfrfid_app_scene_save_name.h deleted file mode 100644 index ced42cc0e4..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_name.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSaveName : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void save_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_success.cpp deleted file mode 100644 index 64efafa731..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_success.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "lfrfid_app_scene_save_success.h" -#include -#include -#include - -void LfRfidAppSceneSaveSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - DOLPHIN_DEED(DolphinDeedRfidSave); - popup->set_icon(32, 5, &I_DolphinNice_96x59); - popup->set_header("Saved!", 5, 7, AlignLeft, AlignTop); - popup->set_context(app); - popup->set_callback(LfRfidAppSceneSaveSuccess::timeout_callback); - popup->set_timeout(1500); - popup->enable_timeout(); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSaveSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Back) { - bool result = app->scene_controller.has_previous_scene( - {LfRfidApp::SceneType::ReadKeyMenu, LfRfidApp::SceneType::SelectKey}); - - if(result) { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::ReadKeyMenu, LfRfidApp::SceneType::SelectKey}); - } else { - app->scene_controller.search_and_switch_to_another_scene( - {LfRfidApp::SceneType::SaveType}, LfRfidApp::SceneType::SelectKey); - } - - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneSaveSuccess::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneSaveSuccess::timeout_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_success.h b/applications/lfrfid/scene/lfrfid_app_scene_save_success.h deleted file mode 100644 index 62273a76be..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_success.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSaveSuccess : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void timeout_callback(void* context); -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp b/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp deleted file mode 100644 index 8e7af48fb6..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_type.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "lfrfid_app_scene_save_type.h" - -void LfRfidAppSceneSaveType::on_enter(LfRfidApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - - for(uint8_t i = 0; i < keys_count; i++) { - if(strcmp( - protocol_dict_get_manufacturer(app->dict, i), - protocol_dict_get_name(app->dict, i)) && - strcmp(protocol_dict_get_manufacturer(app->dict, i), "N/A")) { - string_init_printf( - submenu_name[i], - "%s %s", - protocol_dict_get_manufacturer(app->dict, i), - protocol_dict_get_name(app->dict, i)); - } else { - string_init_printf(submenu_name[i], "%s", protocol_dict_get_name(app->dict, i)); - } - submenu->add_item(string_get_cstr(submenu_name[i]), i, submenu_callback, app); - } - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - - app->view_controller.switch_to(); - - // clear key name - string_reset(app->file_name); -} - -bool LfRfidAppSceneSaveType::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.signed_int; - app->protocol_id = event->payload.signed_int; - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData); - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneSaveType::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - for(uint8_t i = 0; i < keys_count; i++) { - string_clear(submenu_name[i]); - } -} - -void LfRfidAppSceneSaveType::submenu_callback(void* context, uint32_t index) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - - event.type = LfRfidApp::EventType::MenuSelected; - event.payload.signed_int = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_save_type.h b/applications/lfrfid/scene/lfrfid_app_scene_save_type.h deleted file mode 100644 index e4c1be3e64..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_save_type.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSaveType : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; - static const uint8_t keys_count = static_cast(LFRFIDProtocol::LFRFIDProtocolMax); - string_t submenu_name[keys_count]; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp deleted file mode 100644 index 614dd505c2..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "lfrfid_app_scene_saved_info.h" -#include "../view/elements/button_element.h" -#include "../view/elements/icon_element.h" -#include "../view/elements/string_element.h" - -void LfRfidAppSceneSavedInfo::on_enter(LfRfidApp* app, bool /* need_restore */) { - string_init(string_info); - - string_printf( - string_info, - "%s [%s]\r\n", - string_get_cstr(app->file_name), - protocol_dict_get_name(app->dict, app->protocol_id)); - - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - uint8_t* data = (uint8_t*)malloc(size); - protocol_dict_get_data(app->dict, app->protocol_id, data, size); - for(uint8_t i = 0; i < size; i++) { - if(i != 0) { - string_cat_printf(string_info, " "); - } - - string_cat_printf(string_info, "%02X", data[i]); - } - free(data); - - string_t render_data; - string_init(render_data); - protocol_dict_render_data(app->dict, render_data, app->protocol_id); - string_cat_printf(string_info, "\r\n%s", string_get_cstr(render_data)); - string_clear(render_data); - - auto container = app->view_controller.get(); - - auto line_1 = container->add(); - line_1->set_text(string_get_cstr(string_info), 0, 1, 0, AlignLeft, AlignTop, FontSecondary); - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSavedInfo::on_event(LfRfidApp* /* app */, LfRfidApp::Event* /* event */) { - return false; -} - -void LfRfidAppSceneSavedInfo::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); - string_clear(string_info); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h b/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h deleted file mode 100644 index b0b588bcb1..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_info.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSavedInfo : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - string_t string_info; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp b/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp deleted file mode 100644 index e7a38d8ad1..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "lfrfid_app_scene_saved_key_menu.h" - -typedef enum { - SubmenuEmulate, - SubmenuWrite, - SubmenuEdit, - SubmenuDelete, - SubmenuInfo, -} SubmenuIndex; - -void LfRfidAppSceneSavedKeyMenu::on_enter(LfRfidApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - - submenu->add_item("Emulate", SubmenuEmulate, submenu_callback, app); - submenu->add_item("Write", SubmenuWrite, submenu_callback, app); - submenu->add_item("Edit", SubmenuEdit, submenu_callback, app); - submenu->add_item("Delete", SubmenuDelete, submenu_callback, app); - submenu->add_item("Info", SubmenuInfo, submenu_callback, app); - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - - app->view_controller.switch_to(); -} - -bool LfRfidAppSceneSavedKeyMenu::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.signed_int; - switch(event->payload.signed_int) { - case SubmenuEmulate: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Emulate); - break; - case SubmenuWrite: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Write); - break; - case SubmenuEdit: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveData); - break; - case SubmenuDelete: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::DeleteConfirm); - break; - case SubmenuInfo: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SavedInfo); - break; - } - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneSavedKeyMenu::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneSavedKeyMenu::submenu_callback(void* context, uint32_t index) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - - event.type = LfRfidApp::EventType::MenuSelected; - event.payload.signed_int = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.h b/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.h deleted file mode 100644 index 69a6e5e58b..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_saved_key_menu.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSavedKeyMenu : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_select_key.cpp b/applications/lfrfid/scene/lfrfid_app_scene_select_key.cpp deleted file mode 100644 index 6d5df73cb8..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_select_key.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "lfrfid_app_scene_select_key.h" - -void LfRfidAppSceneSelectKey::on_enter(LfRfidApp* app, bool need_restore) { - if(app->load_key_from_file_select(need_restore)) { - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SavedKeyMenu); - } else { - app->scene_controller.switch_to_previous_scene(); - } -} - -bool LfRfidAppSceneSelectKey::on_event(LfRfidApp* /* app */, LfRfidApp::Event* /* event */) { - return false; -} - -void LfRfidAppSceneSelectKey::on_exit(LfRfidApp* /* app */) { -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_select_key.h b/applications/lfrfid/scene/lfrfid_app_scene_select_key.h deleted file mode 100644 index be565a91c5..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_select_key.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneSelectKey : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_start.cpp b/applications/lfrfid/scene/lfrfid_app_scene_start.cpp deleted file mode 100644 index 5005c9afb5..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_start.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "lfrfid_app_scene_start.h" - -typedef enum { - SubmenuRead, - SubmenuSaved, - SubmenuAddManually, - SubmenuExtraActions, -} SubmenuIndex; - -void LfRfidAppSceneStart::on_enter(LfRfidApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - - submenu->add_item("Read", SubmenuRead, submenu_callback, app); - submenu->add_item("Saved", SubmenuSaved, submenu_callback, app); - submenu->add_item("Add Manually", SubmenuAddManually, submenu_callback, app); - submenu->add_item("Extra Actions", SubmenuExtraActions, submenu_callback, app); - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - - app->view_controller.switch_to(); - - // clear key - string_reset(app->file_name); - app->protocol_id = PROTOCOL_NO; - app->read_type = LFRFIDWorkerReadTypeAuto; -} - -bool LfRfidAppSceneStart::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.signed_int; - switch(event->payload.signed_int) { - case SubmenuRead: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::Read); - break; - case SubmenuSaved: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SelectKey); - break; - case SubmenuAddManually: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::SaveType); - break; - case SubmenuExtraActions: - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::ExtraActions); - break; - } - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneStart::on_exit(LfRfidApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneStart::submenu_callback(void* context, uint32_t index) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - - event.type = LfRfidApp::EventType::MenuSelected; - event.payload.signed_int = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_start.h b/applications/lfrfid/scene/lfrfid_app_scene_start.h deleted file mode 100644 index 255590d6ac..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_start.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneStart : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp b/applications/lfrfid/scene/lfrfid_app_scene_write.cpp deleted file mode 100644 index 39e0630e7d..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "lfrfid_app_scene_write.h" - -static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* ctx) { - LfRfidApp* app = static_cast(ctx); - LfRfidApp::Event event; - - switch(result) { - case LFRFIDWorkerWriteOK: - event.type = LfRfidApp::EventType::WriteEventOK; - break; - case LFRFIDWorkerWriteProtocolCannotBeWritten: - event.type = LfRfidApp::EventType::WriteEventProtocolCannotBeWritten; - break; - case LFRFIDWorkerWriteFobCannotBeWritten: - event.type = LfRfidApp::EventType::WriteEventFobCannotBeWritten; - break; - case LFRFIDWorkerWriteTooLongToWrite: - event.type = LfRfidApp::EventType::WriteEventTooLongToWrite; - break; - } - - app->view_controller.send_event(&event); -} - -void LfRfidAppSceneWrite::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - - popup->set_header("Writing", 89, 30, AlignCenter, AlignTop); - if(string_size(app->file_name)) { - popup->set_text(string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); - } else { - popup->set_text( - protocol_dict_get_name(app->dict, app->protocol_id), 89, 43, AlignCenter, AlignTop); - } - popup->set_icon(0, 3, &I_RFIDDolphinSend_97x61); - - app->view_controller.switch_to(); - - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - app->old_key_data = (uint8_t*)malloc(size); - protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); - - lfrfid_worker_start_thread(app->lfworker); - lfrfid_worker_write_start( - app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app); - notification_message(app->notification, &sequence_blink_start_magenta); -} - -bool LfRfidAppSceneWrite::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = true; - auto popup = app->view_controller.get(); - - switch(event->type) { - case LfRfidApp::EventType::WriteEventOK: - notification_message(app->notification, &sequence_success); - app->scene_controller.switch_to_next_scene(LfRfidApp::SceneType::WriteSuccess); - break; - case LfRfidApp::EventType::WriteEventProtocolCannotBeWritten: - popup->set_icon(72, 17, &I_DolphinCommon_56x48); - popup->set_header("Error", 64, 3, AlignCenter, AlignTop); - popup->set_text("This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop); - notification_message(app->notification, &sequence_blink_start_red); - break; - case LfRfidApp::EventType::WriteEventFobCannotBeWritten: - case LfRfidApp::EventType::WriteEventTooLongToWrite: - popup->set_icon(72, 17, &I_DolphinCommon_56x48); - popup->set_header("Still trying to write...", 64, 3, AlignCenter, AlignTop); - popup->set_text( - "Make sure this\ncard is writable\nand not\nprotected.", 3, 17, AlignLeft, AlignTop); - notification_message(app->notification, &sequence_blink_start_yellow); - break; - default: - consumed = false; - } - - return consumed; -} - -void LfRfidAppSceneWrite::on_exit(LfRfidApp* app) { - notification_message(app->notification, &sequence_blink_stop); - app->view_controller.get()->clean(); - lfrfid_worker_stop(app->lfworker); - lfrfid_worker_stop_thread(app->lfworker); - - size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); - protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size); - free(app->old_key_data); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write.h b/applications/lfrfid/scene/lfrfid_app_scene_write.h deleted file mode 100644 index 7564eff9dd..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_write.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneWrite : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; -}; diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write_success.cpp b/applications/lfrfid/scene/lfrfid_app_scene_write_success.cpp deleted file mode 100644 index 3cf00183d7..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_write_success.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "lfrfid_app_scene_write_success.h" - -void LfRfidAppSceneWriteSuccess::on_enter(LfRfidApp* app, bool /* need_restore */) { - auto popup = app->view_controller.get(); - popup->set_header("Successfully\nwritten!", 94, 3, AlignCenter, AlignTop); - popup->set_icon(0, 6, &I_RFIDDolphinSuccess_108x57); - popup->set_context(app); - popup->set_callback(LfRfidAppSceneWriteSuccess::timeout_callback); - popup->set_timeout(1500); - popup->enable_timeout(); - - app->view_controller.switch_to(); - notification_message_block(app->notification, &sequence_set_green_255); -} - -bool LfRfidAppSceneWriteSuccess::on_event(LfRfidApp* app, LfRfidApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidApp::EventType::Back) { - app->scene_controller.search_and_switch_to_previous_scene( - {LfRfidApp::SceneType::ReadKeyMenu, LfRfidApp::SceneType::SelectKey}); - consumed = true; - } - - return consumed; -} - -void LfRfidAppSceneWriteSuccess::on_exit(LfRfidApp* app) { - notification_message_block(app->notification, &sequence_reset_green); - app->view_controller.get()->clean(); -} - -void LfRfidAppSceneWriteSuccess::timeout_callback(void* context) { - LfRfidApp* app = static_cast(context); - LfRfidApp::Event event; - event.type = LfRfidApp::EventType::Back; - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid/scene/lfrfid_app_scene_write_success.h b/applications/lfrfid/scene/lfrfid_app_scene_write_success.h deleted file mode 100644 index 4ac9f0892f..0000000000 --- a/applications/lfrfid/scene/lfrfid_app_scene_write_success.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../lfrfid_app.h" - -class LfRfidAppSceneWriteSuccess : public GenericScene { -public: - void on_enter(LfRfidApp* app, bool need_restore) final; - bool on_event(LfRfidApp* app, LfRfidApp::Event* event) final; - void on_exit(LfRfidApp* app) final; - -private: - static void timeout_callback(void* context); -}; diff --git a/applications/lfrfid/scenes/lfrfid_scene.c b/applications/lfrfid/scenes/lfrfid_scene.c new file mode 100644 index 0000000000..0de5ec36b0 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene.c @@ -0,0 +1,30 @@ +#include "lfrfid_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const lfrfid_on_enter_handlers[])(void*) = { +#include "lfrfid_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 lfrfid_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "lfrfid_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 lfrfid_on_exit_handlers[])(void* context) = { +#include "lfrfid_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers lfrfid_scene_handlers = { + .on_enter_handlers = lfrfid_on_enter_handlers, + .on_event_handlers = lfrfid_on_event_handlers, + .on_exit_handlers = lfrfid_on_exit_handlers, + .scene_num = LfRfidSceneNum, +}; diff --git a/applications/lfrfid/scenes/lfrfid_scene.h b/applications/lfrfid/scenes/lfrfid_scene.h new file mode 100644 index 0000000000..8ce7da09c3 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) LfRfidScene##id, +typedef enum { +#include "lfrfid_scene_config.h" + LfRfidSceneNum, +} LfRfidScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers lfrfid_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "lfrfid_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 "lfrfid_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 "lfrfid_scene_config.h" +#undef ADD_SCENE diff --git a/applications/lfrfid/scenes/lfrfid_scene_config.h b/applications/lfrfid/scenes/lfrfid_scene_config.h new file mode 100644 index 0000000000..b77ade82f6 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_config.h @@ -0,0 +1,24 @@ +ADD_SCENE(lfrfid, start, Start) +ADD_SCENE(lfrfid, read, Read) +ADD_SCENE(lfrfid, read_success, ReadSuccess) +ADD_SCENE(lfrfid, retry_confirm, RetryConfirm) +ADD_SCENE(lfrfid, exit_confirm, ExitConfirm) +ADD_SCENE(lfrfid, delete_confirm, DeleteConfirm) +ADD_SCENE(lfrfid, read_key_menu, ReadKeyMenu) +ADD_SCENE(lfrfid, write, Write) +ADD_SCENE(lfrfid, write_success, WriteSuccess) +ADD_SCENE(lfrfid, emulate, Emulate) +ADD_SCENE(lfrfid, save_name, SaveName) +ADD_SCENE(lfrfid, save_success, SaveSuccess) +ADD_SCENE(lfrfid, select_key, SelectKey) +ADD_SCENE(lfrfid, saved_key_menu, SavedKeyMenu) +ADD_SCENE(lfrfid, save_data, SaveData) +ADD_SCENE(lfrfid, save_type, SaveType) +ADD_SCENE(lfrfid, saved_info, SavedInfo) +ADD_SCENE(lfrfid, delete_success, DeleteSuccess) +ADD_SCENE(lfrfid, extra_actions, ExtraActions) +ADD_SCENE(lfrfid, raw_info, RawInfo) +ADD_SCENE(lfrfid, raw_name, RawName) +ADD_SCENE(lfrfid, raw_read, RawRead) +ADD_SCENE(lfrfid, raw_success, RawSuccess) +ADD_SCENE(lfrfid, rpc, Rpc) diff --git a/applications/lfrfid/scenes/lfrfid_scene_delete_confirm.c b/applications/lfrfid/scenes/lfrfid_scene_delete_confirm.c new file mode 100644 index 0000000000..dc1c3df268 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_delete_confirm.c @@ -0,0 +1,68 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_delete_confirm_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + string_t tmp_string; + string_init(tmp_string); + + widget_add_button_element(widget, GuiButtonTypeLeft, "Back", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "Delete", lfrfid_widget_callback, app); + + string_printf(tmp_string, "Delete %s?", string_get_cstr(app->file_name)); + widget_add_string_element( + widget, 64, 0, AlignCenter, AlignTop, FontPrimary, string_get_cstr(tmp_string)); + + string_reset(tmp_string); + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < MIN(size, (size_t)8); i++) { + if(i != 0) { + string_cat_printf(tmp_string, " "); + } + + string_cat_printf(tmp_string, "%02X", data[i]); + } + free(data); + + widget_add_string_element( + widget, 64, 19, AlignCenter, AlignTop, FontSecondary, string_get_cstr(tmp_string)); + widget_add_string_element( + widget, + 64, + 49, + AlignCenter, + AlignBottom, + FontSecondary, + protocol_dict_get_name(app->dict, app->protocol_id)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); + string_clear(tmp_string); +} + +bool lfrfid_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(scene_manager); + } else if(event.event == GuiButtonTypeRight) { + lfrfid_delete_key(app); + scene_manager_next_scene(scene_manager, LfRfidSceneDeleteSuccess); + } + } + + return consumed; +} + +void lfrfid_scene_delete_confirm_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/lfrfid/scenes/lfrfid_scene_delete_success.c new file mode 100644 index 0000000000..f940b9bd43 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_delete_success.c @@ -0,0 +1,35 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_delete_success_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62); + popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom); + popup_set_context(popup, app); + popup_set_callback(popup, lfrfid_popup_timeout_callback); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); +} + +bool lfrfid_scene_delete_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if((event.type == SceneManagerEventTypeBack) || + ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, LfRfidSceneSelectKey); + consumed = true; + } + + return consumed; +} + +void lfrfid_scene_delete_success_on_exit(void* context) { + LfRfid* app = context; + + popup_reset(app->popup); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/lfrfid/scenes/lfrfid_scene_emulate.c new file mode 100644 index 0000000000..70cc2418c5 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_emulate.c @@ -0,0 +1,44 @@ +#include "../lfrfid_i.h" +#include + +void lfrfid_scene_emulate_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + DOLPHIN_DEED(DolphinDeedRfidEmulate); + + popup_set_header(popup, "Emulating", 89, 30, AlignCenter, AlignTop); + if(!string_empty_p(app->file_name)) { + popup_set_text(popup, string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); + } else { + popup_set_text( + popup, + protocol_dict_get_name(app->dict, app->protocol_id), + 89, + 43, + AlignCenter, + AlignTop); + } + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); + notification_message(app->notifications, &sequence_blink_start_magenta); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); +} + +bool lfrfid_scene_emulate_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void lfrfid_scene_emulate_on_exit(void* context) { + LfRfid* app = context; + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_exit_confirm.c b/applications/lfrfid/scenes/lfrfid_scene_exit_confirm.c new file mode 100644 index 0000000000..e8ab481c6f --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_exit_confirm.c @@ -0,0 +1,39 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_exit_confirm_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "Stay", lfrfid_widget_callback, app); + widget_add_string_element( + widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to RFID Menu?"); + widget_add_string_element( + widget, 64, 31, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); +} + +bool lfrfid_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneStart); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_previous_scene(scene_manager); + } + } + + return consumed; +} + +void lfrfid_scene_exit_confirm_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_extra_actions.c b/applications/lfrfid/scenes/lfrfid_scene_extra_actions.c new file mode 100644 index 0000000000..58082cf100 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_extra_actions.c @@ -0,0 +1,79 @@ +#include "../lfrfid_i.h" + +typedef enum { + SubmenuIndexASK, + SubmenuIndexPSK, + SubmenuIndexRAW, +} SubmenuIndex; + +static void lfrfid_scene_extra_actions_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_extra_actions_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, + "Read ASK (FDX,Regular)", + SubmenuIndexASK, + lfrfid_scene_extra_actions_submenu_callback, + app); + submenu_add_item( + submenu, + "Read PSK (Indala)", + SubmenuIndexPSK, + lfrfid_scene_extra_actions_submenu_callback, + app); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + submenu_add_item( + submenu, + "Read RAW RFID data", + SubmenuIndexRAW, + lfrfid_scene_extra_actions_submenu_callback, + app); + } + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneExtraActions)); + + // clear key + string_reset(app->file_name); + app->protocol_id = PROTOCOL_NO; + app->read_type = LFRFIDWorkerReadTypeAuto; + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_extra_actions_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexASK) { + app->read_type = LFRFIDWorkerReadTypeASKOnly; + scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + consumed = true; + } else if(event.event == SubmenuIndexPSK) { + app->read_type = LFRFIDWorkerReadTypePSKOnly; + scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + consumed = true; + } else if(event.event == SubmenuIndexRAW) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneRawName); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneExtraActions, event.event); + } + + return consumed; +} + +void lfrfid_scene_extra_actions_on_exit(void* context) { + LfRfid* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_raw_info.c b/applications/lfrfid/scenes/lfrfid_scene_raw_info.c new file mode 100644 index 0000000000..f60dd6243d --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_raw_info.c @@ -0,0 +1,64 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_raw_info_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + // string_t tmp_string; + // string_init(tmp_string); + + bool sd_exist = storage_sd_status(app->storage) == FSE_OK; + if(!sd_exist) { + widget_add_icon_element(widget, 0, 0, &I_SDQuestion_35x43); + widget_add_string_multiline_element( + widget, + 81, + 4, + AlignCenter, + AlignTop, + FontSecondary, + "No SD card found.\nThis function will not\nwork without\nSD card."); + + widget_add_button_element(widget, GuiButtonTypeLeft, "Back", lfrfid_widget_callback, app); + } else { + widget_add_string_multiline_element( + widget, + 0, + 1, + AlignLeft, + AlignTop, + FontSecondary, + "RAW RFID data reader\n1) Put the Flipper on your card\n2) Press OK\n3) Wait until data is read"); + + widget_add_button_element(widget, GuiButtonTypeCenter, "OK", lfrfid_widget_callback, app); + } + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); + //string_clear(tmp_string); +} + +bool lfrfid_scene_raw_info_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; + scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneExtraActions); + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeCenter) { + scene_manager_next_scene(scene_manager, LfRfidSceneRawRead); + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneExtraActions); + } + } + + return consumed; +} + +void lfrfid_scene_raw_info_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_raw_name.c b/applications/lfrfid/scenes/lfrfid_scene_raw_name.c new file mode 100644 index 0000000000..512f9ee4c9 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_raw_name.c @@ -0,0 +1,58 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_raw_name_on_enter(void* context) { + LfRfid* app = context; + TextInput* text_input = app->text_input; + + const char* key_name = string_get_cstr(app->raw_file_name); + + bool key_name_is_empty = string_empty_p(app->file_name); + if(key_name_is_empty) { + lfrfid_text_store_set(app, "RfidRecord"); + } else { + lfrfid_text_store_set(app, "%s", key_name); + } + + text_input_set_header_text(text_input, "Name the raw file"); + + text_input_set_result_callback( + text_input, + lfrfid_text_input_callback, + app, + app->text_store, + LFRFID_KEY_NAME_SIZE, + key_name_is_empty); + + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(LFRFID_SD_FOLDER, LFRFID_APP_RAW_ASK_EXTENSION, NULL); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewTextInput); +} + +bool lfrfid_scene_raw_name_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventNext) { + consumed = true; + string_set_str(app->raw_file_name, app->text_store); + scene_manager_next_scene(scene_manager, LfRfidSceneRawInfo); + } + } + + return consumed; +} + +void lfrfid_scene_raw_name_on_exit(void* context) { + LfRfid* app = context; + TextInput* text_input = app->text_input; + + void* validator_context = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + validator_is_file_free((ValidatorIsFile*)validator_context); + + text_input_reset(text_input); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_raw_read.c b/applications/lfrfid/scenes/lfrfid_scene_raw_read.c new file mode 100644 index 0000000000..d0c03ffa94 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_raw_read.c @@ -0,0 +1,126 @@ +#include "../lfrfid_i.h" + +#define RAW_READ_TIME 5000 + +typedef struct { + string_t string_file_name; + FuriTimer* timer; + bool is_psk; + bool error; +} LfRfidReadRawState; + +static void lfrfid_read_callback(LFRFIDWorkerReadRawResult result, void* context) { + LfRfid* app = context; + + if(result == LFRFIDWorkerReadRawFileError) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadError); + } else if(result == LFRFIDWorkerReadRawOverrun) { + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadOverrun); + } +} + +static void timer_callback(void* context) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, LfRfidEventReadDone); +} + +void lfrfid_scene_raw_read_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + LfRfidReadRawState* state = malloc(sizeof(LfRfidReadRawState)); + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneRawRead, (uint32_t)state); + string_init(state->string_file_name); + + popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61); + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + lfrfid_worker_start_thread(app->lfworker); + lfrfid_make_app_folder(app); + + state->timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, app); + furi_timer_start(state->timer, RAW_READ_TIME); + string_printf( + state->string_file_name, + "%s/%s%s", + LFRFID_SD_FOLDER, + string_get_cstr(app->raw_file_name), + LFRFID_APP_RAW_ASK_EXTENSION); + popup_set_header(popup, "Reading\nRAW RFID\nASK", 89, 30, AlignCenter, AlignTop); + lfrfid_worker_read_raw_start( + app->lfworker, + string_get_cstr(state->string_file_name), + LFRFIDWorkerReadTypeASKOnly, + lfrfid_read_callback, + app); + + notification_message(app->notifications, &sequence_blink_start_cyan); + + state->is_psk = false; + state->error = false; +} + +bool lfrfid_scene_raw_read_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + Popup* popup = app->popup; + LfRfidReadRawState* state = + (LfRfidReadRawState*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneRawRead); + bool consumed = false; + + furi_assert(state); + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventReadError) { + consumed = true; + state->error = true; + popup_set_header( + popup, "Reading\nRAW RFID\nFile error", 89, 30, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_blink_start_red); + furi_timer_stop(state->timer); + } else if(event.event == LfRfidEventReadDone) { + consumed = true; + if(!state->error) { + if(state->is_psk) { + notification_message(app->notifications, &sequence_success); + scene_manager_next_scene(app->scene_manager, LfRfidSceneRawSuccess); + } else { + popup_set_header( + popup, "Reading\nRAW RFID\nPSK", 89, 30, AlignCenter, AlignTop); + notification_message(app->notifications, &sequence_blink_start_yellow); + lfrfid_worker_stop(app->lfworker); + string_printf( + state->string_file_name, + "%s/%s%s", + LFRFID_SD_FOLDER, + string_get_cstr(app->raw_file_name), + LFRFID_APP_RAW_PSK_EXTENSION); + lfrfid_worker_read_raw_start( + app->lfworker, + string_get_cstr(state->string_file_name), + LFRFIDWorkerReadTypePSKOnly, + lfrfid_read_callback, + app); + furi_timer_start(state->timer, RAW_READ_TIME); + state->is_psk = true; + } + } + } + } + + return consumed; +} + +void lfrfid_scene_raw_read_on_exit(void* context) { + LfRfid* app = context; + LfRfidReadRawState* state = + (LfRfidReadRawState*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneRawRead); + + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + furi_timer_free(state->timer); + + string_clear(state->string_file_name); + free(state); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_raw_success.c b/applications/lfrfid/scenes/lfrfid_scene_raw_success.c new file mode 100644 index 0000000000..09a005298f --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_raw_success.c @@ -0,0 +1,39 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_raw_success_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + widget_add_button_element(widget, GuiButtonTypeCenter, "OK", lfrfid_widget_callback, app); + + widget_add_string_multiline_element( + widget, + 0, + 1, + AlignLeft, + AlignTop, + FontSecondary, + "RAW RFID read success!\nNow you can analyze files\nOr send them to developers"); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); +} + +bool lfrfid_scene_raw_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeCenter) { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneExtraActions); + } + } + return consumed; +} + +void lfrfid_scene_raw_success_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_read.c b/applications/lfrfid/scenes/lfrfid_scene_read.c new file mode 100644 index 0000000000..661680381e --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_read.c @@ -0,0 +1,109 @@ +#include "../lfrfid_i.h" +#include + +static const NotificationSequence sequence_blink_set_yellow = { + &message_blink_set_color_yellow, + NULL, +}; + +static const NotificationSequence sequence_blink_set_green = { + &message_blink_set_color_green, + NULL, +}; + +static const NotificationSequence sequence_blink_set_cyan = { + &message_blink_set_color_cyan, + NULL, +}; + +static void + lfrfid_read_callback(LFRFIDWorkerReadResult result, ProtocolId protocol, void* context) { + LfRfid* app = context; + uint32_t event = 0; + + if(result == LFRFIDWorkerReadSenseStart) { + event = LfRfidEventReadSenseStart; + } else if(result == LFRFIDWorkerReadSenseEnd) { + event = LfRfidEventReadSenseEnd; + } else if(result == LFRFIDWorkerReadSenseCardStart) { + event = LfRfidEventReadSenseCardStart; + } else if(result == LFRFIDWorkerReadSenseCardEnd) { + event = LfRfidEventReadSenseCardEnd; + } else if(result == LFRFIDWorkerReadDone) { + event = LfRfidEventReadDone; + app->protocol_id_next = protocol; + } else if(result == LFRFIDWorkerReadStartASK) { + event = LfRfidEventReadStartASK; + } else if(result == LFRFIDWorkerReadStartPSK) { + event = LfRfidEventReadStartPSK; + } else { + return; + } + + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void lfrfid_scene_read_on_enter(void* context) { + LfRfid* app = context; + + DOLPHIN_DEED(DolphinDeedRfidRead); + if(app->read_type == LFRFIDWorkerReadTypePSKOnly) { + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPskOnly); + } else if(app->read_type == LFRFIDWorkerReadTypeASKOnly) { + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAskOnly); + } + + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_read_start(app->lfworker, app->read_type, lfrfid_read_callback, app); + + notification_message(app->notifications, &sequence_blink_start_cyan); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewRead); +} + +bool lfrfid_scene_read_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventReadSenseStart) { + notification_message(app->notifications, &sequence_blink_set_yellow); + consumed = true; + } else if(event.event == LfRfidEventReadSenseCardStart) { + notification_message(app->notifications, &sequence_blink_set_green); + consumed = true; + } else if( + (event.event == LfRfidEventReadSenseEnd) || + (event.event == LfRfidEventReadSenseCardEnd)) { + notification_message(app->notifications, &sequence_blink_set_cyan); + consumed = true; + } else if(event.event == LfRfidEventReadDone) { + app->protocol_id = app->protocol_id_next; + DOLPHIN_DEED(DolphinDeedRfidReadSuccess); + notification_message(app->notifications, &sequence_success); + string_reset(app->file_name); + scene_manager_next_scene(app->scene_manager, LfRfidSceneReadSuccess); + consumed = true; + } else if(event.event == LfRfidEventReadStartPSK) { + if(app->read_type == LFRFIDWorkerReadTypeAuto) { + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadPsk); + } + consumed = true; + } else if(event.event == LfRfidEventReadStartASK) { + if(app->read_type == LFRFIDWorkerReadTypeAuto) { + lfrfid_view_read_set_read_mode(app->read_view, LfRfidReadAsk); + } + consumed = true; + } + } + + return consumed; +} + +void lfrfid_scene_read_on_exit(void* context) { + LfRfid* app = context; + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_read_key_menu.c b/applications/lfrfid/scenes/lfrfid_scene_read_key_menu.c new file mode 100644 index 0000000000..221cc00844 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_read_key_menu.c @@ -0,0 +1,58 @@ +#include "../lfrfid_i.h" + +typedef enum { + SubmenuIndexSave, + SubmenuIndexEmulate, + SubmenuIndexWrite, +} SubmenuIndex; + +void lfrfid_scene_read_key_menu_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_read_key_menu_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, "Save", SubmenuIndexSave, lfrfid_scene_read_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_read_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Write", SubmenuIndexWrite, lfrfid_scene_read_key_menu_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); + consumed = true; + } else if(event.event == SubmenuIndexSave) { + string_reset(app->file_name); + scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveName); + consumed = true; + } else if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneReadKeyMenu, event.event); + } + + return consumed; +} + +void lfrfid_scene_read_key_menu_on_exit(void* context) { + LfRfid* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_read_success.c b/applications/lfrfid/scenes/lfrfid_scene_read_success.c new file mode 100644 index 0000000000..6593715208 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_read_success.c @@ -0,0 +1,81 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_read_success_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + string_t tmp_string; + string_init(tmp_string); + + widget_add_button_element(widget, GuiButtonTypeLeft, "Retry", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "More", lfrfid_widget_callback, app); + + string_printf( + tmp_string, + "%s[%s]", + protocol_dict_get_name(app->dict, app->protocol_id), + protocol_dict_get_manufacturer(app->dict, app->protocol_id)); + + widget_add_string_element( + widget, 16, 3, AlignLeft, AlignTop, FontPrimary, string_get_cstr(tmp_string)); + + string_reset(tmp_string); + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < size; i++) { + if(i != 0) { + string_cat_printf(tmp_string, ":"); + } + + if(i >= 8) { + string_cat_printf(tmp_string, "..."); + break; + } else { + string_cat_printf(tmp_string, "%02X", data[i]); + } + } + free(data); + + string_t render_data; + string_init(render_data); + protocol_dict_render_brief_data(app->dict, render_data, app->protocol_id); + string_cat_printf(tmp_string, "\r\n%s", string_get_cstr(render_data)); + string_clear(render_data); + + widget_add_string_multiline_element( + widget, 0, 16, AlignLeft, AlignTop, FontSecondary, string_get_cstr(tmp_string)); + + widget_add_icon_element(app->widget, 0, 0, &I_RFIDSmallChip_14x14); + + notification_message_block(app->notifications, &sequence_set_green_255); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); + string_clear(tmp_string); +} + +bool lfrfid_scene_read_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + scene_manager_next_scene(scene_manager, LfRfidSceneExitConfirm); + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_next_scene(scene_manager, LfRfidSceneRetryConfirm); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(scene_manager, LfRfidSceneReadKeyMenu); + } + } + + return consumed; +} + +void lfrfid_scene_read_success_on_exit(void* context) { + LfRfid* app = context; + notification_message_block(app->notifications, &sequence_reset_green); + widget_reset(app->widget); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_retry_confirm.c b/applications/lfrfid/scenes/lfrfid_scene_retry_confirm.c new file mode 100644 index 0000000000..f639f0ae1a --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_retry_confirm.c @@ -0,0 +1,39 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_retry_confirm_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", lfrfid_widget_callback, app); + widget_add_button_element(widget, GuiButtonTypeRight, "Stay", lfrfid_widget_callback, app); + widget_add_string_element( + widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?"); + widget_add_string_element( + widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost!"); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); +} + +bool lfrfid_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; // Ignore Back button presses + } else if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene(scene_manager, LfRfidSceneRead); + } else if(event.event == GuiButtonTypeRight) { + scene_manager_previous_scene(scene_manager); + } + } + + return consumed; +} + +void lfrfid_scene_retry_confirm_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_rpc.c b/applications/lfrfid/scenes/lfrfid_scene_rpc.c new file mode 100644 index 0000000000..a69d6453a3 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_rpc.c @@ -0,0 +1,67 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_rpc_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_header(popup, "LF RFID", 89, 42, AlignCenter, AlignBottom); + popup_set_text(popup, "RPC mode", 89, 44, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 12, &I_RFIDDolphinSend_97x61); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + + notification_message(app->notifications, &sequence_display_backlight_on); + + app->rpc_state = LfRfidRpcStateIdle; +} + +bool lfrfid_scene_rpc_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + Popup* popup = app->popup; + UNUSED(event); + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == LfRfidEventExit) { + rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true); + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } else if(event.event == LfRfidEventRpcSessionClose) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } else if(event.event == LfRfidEventRpcLoadFile) { + const char* arg = rpc_system_app_get_data(app->rpc_ctx); + bool result = false; + if(arg && (app->rpc_state == LfRfidRpcStateIdle)) { + string_set_str(app->file_path, arg); + if(lfrfid_load_key_data(app, app->file_path, false)) { + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_emulate_start(app->lfworker, (LFRFIDProtocol)app->protocol_id); + app->rpc_state = LfRfidRpcStateEmulating; + + lfrfid_text_store_set(app, "emulating\n%s", string_get_cstr(app->file_name)); + popup_set_text(popup, app->text_store, 89, 44, AlignCenter, AlignTop); + + notification_message(app->notifications, &sequence_blink_start_magenta); + result = true; + } + } + rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result); + } + } + return consumed; +} + +void lfrfid_scene_rpc_on_exit(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + if(app->rpc_state == LfRfidRpcStateEmulating) { + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + notification_message(app->notifications, &sequence_blink_stop); + } + + popup_reset(popup); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_save_data.c b/applications/lfrfid/scenes/lfrfid_scene_save_data.c new file mode 100644 index 0000000000..2ca1bb433c --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_save_data.c @@ -0,0 +1,51 @@ +#include "../lfrfid_i.h" +#include + +void lfrfid_scene_save_data_on_enter(void* context) { + LfRfid* app = context; + ByteInput* byte_input = app->byte_input; + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + + bool need_restore = scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveData); + + if(need_restore) { + protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size); + } else { + protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); + } + + protocol_dict_get_data(app->dict, app->protocol_id, app->new_key_data, size); + + byte_input_set_header_text(byte_input, "Enter the data in hex"); + + byte_input_set_result_callback( + byte_input, lfrfid_text_input_callback, NULL, app, app->new_key_data, size); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewByteInput); +} + +bool lfrfid_scene_save_data_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventNext) { + consumed = true; + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_set_data(app->dict, app->protocol_id, app->new_key_data, size); + DOLPHIN_DEED(DolphinDeedRfidAdd); + scene_manager_next_scene(scene_manager, LfRfidSceneSaveName); + scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 1); + } + } else if(event.type == SceneManagerEventTypeBack) { + scene_manager_set_scene_state(scene_manager, LfRfidSceneSaveData, 0); + } + + return consumed; +} + +void lfrfid_scene_save_data_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_save_name.c b/applications/lfrfid/scenes/lfrfid_scene_save_name.c new file mode 100644 index 0000000000..febf30a418 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_save_name.c @@ -0,0 +1,76 @@ +#include "m-string.h" +#include +#include "../lfrfid_i.h" + +void lfrfid_scene_save_name_on_enter(void* context) { + LfRfid* app = context; + TextInput* text_input = app->text_input; + string_t folder_path; + string_init(folder_path); + + bool key_name_is_empty = string_empty_p(app->file_name); + if(key_name_is_empty) { + string_set_str(app->file_path, LFRFID_APP_FOLDER); + set_random_name(app->text_store, LFRFID_TEXT_STORE_SIZE); + string_set_str(folder_path, LFRFID_APP_FOLDER); + } else { + lfrfid_text_store_set(app, "%s", string_get_cstr(app->file_name)); + path_extract_dirname(string_get_cstr(app->file_path), folder_path); + } + + text_input_set_header_text(text_input, "Name the card"); + text_input_set_result_callback( + text_input, + lfrfid_text_input_callback, + app, + app->text_store, + LFRFID_KEY_NAME_SIZE, + key_name_is_empty); + + FURI_LOG_I("", "%s %s", string_get_cstr(folder_path), app->text_store); + + ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( + string_get_cstr(folder_path), LFRFID_APP_EXTENSION, string_get_cstr(app->file_name)); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + string_clear(folder_path); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewTextInput); +} + +bool lfrfid_scene_save_name_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + SceneManager* scene_manager = app->scene_manager; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventNext) { + consumed = true; + if(!string_empty_p(app->file_name)) { + lfrfid_delete_key(app); + } + + string_set_str(app->file_name, app->text_store); + + if(lfrfid_save_key(app)) { + scene_manager_next_scene(scene_manager, LfRfidSceneSaveSuccess); + } else { + scene_manager_search_and_switch_to_previous_scene( + scene_manager, LfRfidSceneReadKeyMenu); + } + } + } + + return consumed; +} + +void lfrfid_scene_save_name_on_exit(void* context) { + LfRfid* app = context; + TextInput* text_input = app->text_input; + + void* validator_context = text_input_get_validator_callback_context(text_input); + text_input_set_validator(text_input, NULL, NULL); + validator_is_file_free((ValidatorIsFile*)validator_context); + + text_input_reset(text_input); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/lfrfid/scenes/lfrfid_scene_save_success.c new file mode 100644 index 0000000000..830ef3368c --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_save_success.c @@ -0,0 +1,43 @@ +#include "../lfrfid_i.h" +#include + +void lfrfid_scene_save_success_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + DOLPHIN_DEED(DolphinDeedRfidSave); + popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop); + popup_set_context(popup, app); + popup_set_callback(popup, lfrfid_popup_timeout_callback); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); +} + +bool lfrfid_scene_save_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + const uint32_t prev_scenes[] = {LfRfidSceneReadKeyMenu, LfRfidSceneSelectKey}; + + if((event.type == SceneManagerEventTypeBack) || + ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) { + bool result = scene_manager_search_and_switch_to_previous_scene_one_of( + app->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); + if(!result) { + scene_manager_search_and_switch_to_another_scene( + app->scene_manager, LfRfidSceneSelectKey); + } + consumed = true; + } + + return consumed; +} + +void lfrfid_scene_save_success_on_exit(void* context) { + LfRfid* app = context; + + popup_reset(app->popup); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_save_type.c b/applications/lfrfid/scenes/lfrfid_scene_save_type.c new file mode 100644 index 0000000000..827e6d151e --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_save_type.c @@ -0,0 +1,87 @@ +#include "../lfrfid_i.h" + +typedef struct { + string_t menu_item_name[LFRFIDProtocolMax]; + uint32_t line_sel; +} SaveTypeCtx; + +static void lfrfid_scene_save_type_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_save_type_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + SaveTypeCtx* state = malloc(sizeof(SaveTypeCtx)); + for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) { + if((strcmp( + protocol_dict_get_manufacturer(app->dict, i), + protocol_dict_get_name(app->dict, i)) != 0) && + (strcmp(protocol_dict_get_manufacturer(app->dict, i), "N/A") != 0)) { + string_init_printf( + state->menu_item_name[i], + "%s %s", + protocol_dict_get_manufacturer(app->dict, i), + protocol_dict_get_name(app->dict, i)); + } else { + string_init_printf( + state->menu_item_name[i], "%s", protocol_dict_get_name(app->dict, i)); + } + submenu_add_item( + submenu, + string_get_cstr(state->menu_item_name[i]), + i, + lfrfid_scene_save_type_submenu_callback, + app); + } + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType)); + + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveType, (uint32_t)state); + + // clear key name + string_reset(app->file_name); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_save_type_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + SaveTypeCtx* state = + (SaveTypeCtx*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType); + furi_check(state); + + if(event.type == SceneManagerEventTypeCustom) { + app->protocol_id = event.event; + state->line_sel = event.event; + scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData); + consumed = true; + } + + return consumed; +} + +void lfrfid_scene_save_type_on_exit(void* context) { + LfRfid* app = context; + SaveTypeCtx* state = + (SaveTypeCtx*)scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSaveType); + furi_check(state); + + submenu_reset(app->submenu); + + for(uint8_t i = 0; i < LFRFIDProtocolMax; i++) { + string_clear(state->menu_item_name[i]); + } + + uint32_t line_sel = state->line_sel; + + free(state); + + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSaveType, line_sel); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_saved_info.c b/applications/lfrfid/scenes/lfrfid_scene_saved_info.c new file mode 100644 index 0000000000..7e376ca012 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_saved_info.c @@ -0,0 +1,51 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_saved_info_on_enter(void* context) { + LfRfid* app = context; + Widget* widget = app->widget; + + string_t tmp_string; + string_init(tmp_string); + + string_printf( + tmp_string, + "%s [%s]\r\n", + string_get_cstr(app->file_name), + protocol_dict_get_name(app->dict, app->protocol_id)); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + uint8_t* data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, data, size); + for(uint8_t i = 0; i < size; i++) { + if(i != 0) { + string_cat_printf(tmp_string, ":"); + } + + string_cat_printf(tmp_string, "%02X", data[i]); + } + free(data); + + string_t render_data; + string_init(render_data); + protocol_dict_render_data(app->dict, render_data, app->protocol_id); + string_cat_printf(tmp_string, "\r\n%s", string_get_cstr(render_data)); + string_clear(render_data); + + widget_add_string_multiline_element( + widget, 0, 1, AlignLeft, AlignTop, FontSecondary, string_get_cstr(tmp_string)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewWidget); + string_clear(tmp_string); +} + +bool lfrfid_scene_saved_info_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void lfrfid_scene_saved_info_on_exit(void* context) { + LfRfid* app = context; + widget_reset(app->widget); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_saved_key_menu.c b/applications/lfrfid/scenes/lfrfid_scene_saved_key_menu.c new file mode 100644 index 0000000000..040b31f10b --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_saved_key_menu.c @@ -0,0 +1,69 @@ +#include "../lfrfid_i.h" + +typedef enum { + SubmenuIndexEmulate, + SubmenuIndexWrite, + SubmenuIndexEdit, + SubmenuIndexDelete, + SubmenuIndexInfo, +} SubmenuIndex; + +static void lfrfid_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_saved_key_menu_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, "Emulate", SubmenuIndexEmulate, lfrfid_scene_saved_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Write", SubmenuIndexWrite, lfrfid_scene_saved_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Edit", SubmenuIndexEdit, lfrfid_scene_saved_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Delete", SubmenuIndexDelete, lfrfid_scene_saved_key_menu_submenu_callback, app); + submenu_add_item( + submenu, "Info", SubmenuIndexInfo, lfrfid_scene_saved_key_menu_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexEmulate) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneEmulate); + consumed = true; + } else if(event.event == SubmenuIndexWrite) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneWrite); + consumed = true; + } else if(event.event == SubmenuIndexEdit) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveData); + consumed = true; + } else if(event.event == SubmenuIndexDelete) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneDeleteConfirm); + consumed = true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneSavedInfo); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneSavedKeyMenu, event.event); + } + + return consumed; +} + +void lfrfid_scene_saved_key_menu_on_exit(void* context) { + LfRfid* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_select_key.c b/applications/lfrfid/scenes/lfrfid_scene_select_key.c new file mode 100644 index 0000000000..2a9cc1c634 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_select_key.c @@ -0,0 +1,22 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_select_key_on_enter(void* context) { + LfRfid* app = context; + + if(lfrfid_load_key_from_file_select(app)) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneSavedKeyMenu); + } else { + scene_manager_previous_scene(app->scene_manager); + } +} + +bool lfrfid_scene_select_key_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = false; + return consumed; +} + +void lfrfid_scene_select_key_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_start.c b/applications/lfrfid/scenes/lfrfid_scene_start.c new file mode 100644 index 0000000000..9074e859b4 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_start.c @@ -0,0 +1,72 @@ +#include "../lfrfid_i.h" + +typedef enum { + SubmenuIndexRead, + SubmenuIndexSaved, + SubmenuIndexAddManually, + SubmenuIndexExtraActions, +} SubmenuIndex; + +static void lfrfid_scene_start_submenu_callback(void* context, uint32_t index) { + LfRfid* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_scene_start_on_enter(void* context) { + LfRfid* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item(submenu, "Read", SubmenuIndexRead, lfrfid_scene_start_submenu_callback, app); + submenu_add_item( + submenu, "Saved", SubmenuIndexSaved, lfrfid_scene_start_submenu_callback, app); + submenu_add_item( + submenu, "Add Manually", SubmenuIndexAddManually, lfrfid_scene_start_submenu_callback, app); + submenu_add_item( + submenu, + "Extra Actions", + SubmenuIndexExtraActions, + lfrfid_scene_start_submenu_callback, + app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidSceneStart)); + + // clear key + string_reset(app->file_name); + app->protocol_id = PROTOCOL_NO; + app->read_type = LFRFIDWorkerReadTypeAuto; + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewSubmenu); +} + +bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexRead) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); + consumed = true; + } else if(event.event == SubmenuIndexSaved) { + string_set_str(app->file_path, LFRFID_APP_FOLDER); + scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey); + consumed = true; + } else if(event.event == SubmenuIndexAddManually) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType); + consumed = true; + } else if(event.event == SubmenuIndexExtraActions) { + scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, event.event); + } + + return consumed; +} + +void lfrfid_scene_start_on_exit(void* context) { + LfRfid* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_write.c b/applications/lfrfid/scenes/lfrfid_scene_write.c new file mode 100644 index 0000000000..4b03bac15a --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_write.c @@ -0,0 +1,96 @@ +#include "../lfrfid_i.h" + +static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* context) { + LfRfid* app = context; + uint32_t event = 0; + + if(result == LFRFIDWorkerWriteOK) { + event = LfRfidEventWriteOK; + } else if(result == LFRFIDWorkerWriteProtocolCannotBeWritten) { + event = LfRfidEventWriteProtocolCannotBeWritten; + } else if(result == LFRFIDWorkerWriteFobCannotBeWritten) { + event = LfRfidEventWriteFobCannotBeWritten; + } else if(result == LFRFIDWorkerWriteTooLongToWrite) { + event = LfRfidEventWriteTooLongToWrite; + } + + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void lfrfid_scene_write_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_header(popup, "Writing", 89, 30, AlignCenter, AlignTop); + if(!string_empty_p(app->file_name)) { + popup_set_text(popup, string_get_cstr(app->file_name), 89, 43, AlignCenter, AlignTop); + } else { + popup_set_text( + popup, + protocol_dict_get_name(app->dict, app->protocol_id), + 89, + 43, + AlignCenter, + AlignTop); + } + popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + app->old_key_data = (uint8_t*)malloc(size); + protocol_dict_get_data(app->dict, app->protocol_id, app->old_key_data, size); + + lfrfid_worker_start_thread(app->lfworker); + lfrfid_worker_write_start( + app->lfworker, (LFRFIDProtocol)app->protocol_id, lfrfid_write_callback, app); + notification_message(app->notifications, &sequence_blink_start_magenta); +} + +bool lfrfid_scene_write_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + Popup* popup = app->popup; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == LfRfidEventWriteOK) { + notification_message(app->notifications, &sequence_success); + scene_manager_next_scene(app->scene_manager, LfRfidSceneWriteSuccess); + consumed = true; + } else if(event.event == LfRfidEventWriteProtocolCannotBeWritten) { + popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_header(popup, "Error", 64, 3, AlignCenter, AlignTop); + popup_set_text(popup, "This protocol\ncannot be written", 3, 17, AlignLeft, AlignTop); + notification_message(app->notifications, &sequence_blink_start_red); + consumed = true; + } else if( + (event.event == LfRfidEventWriteFobCannotBeWritten) || + (event.event == LfRfidEventWriteTooLongToWrite)) { + popup_set_icon(popup, 72, 17, &I_DolphinCommon_56x48); + popup_set_header(popup, "Still trying to write...", 64, 3, AlignCenter, AlignTop); + popup_set_text( + popup, + "Make sure this\ncard is writable\nand not\nprotected.", + 3, + 17, + AlignLeft, + AlignTop); + notification_message(app->notifications, &sequence_blink_start_yellow); + consumed = true; + } + } + + return consumed; +} + +void lfrfid_scene_write_on_exit(void* context) { + LfRfid* app = context; + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); + lfrfid_worker_stop(app->lfworker); + lfrfid_worker_stop_thread(app->lfworker); + + size_t size = protocol_dict_get_data_size(app->dict, app->protocol_id); + protocol_dict_set_data(app->dict, app->protocol_id, app->old_key_data, size); + free(app->old_key_data); +} diff --git a/applications/lfrfid/scenes/lfrfid_scene_write_success.c b/applications/lfrfid/scenes/lfrfid_scene_write_success.c new file mode 100644 index 0000000000..52e30d6b66 --- /dev/null +++ b/applications/lfrfid/scenes/lfrfid_scene_write_success.c @@ -0,0 +1,38 @@ +#include "../lfrfid_i.h" + +void lfrfid_scene_write_success_on_enter(void* context) { + LfRfid* app = context; + Popup* popup = app->popup; + + popup_set_header(popup, "Successfully\nwritten!", 94, 3, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 6, &I_RFIDDolphinSuccess_108x57); + popup_set_context(popup, app); + popup_set_callback(popup, lfrfid_popup_timeout_callback); + popup_set_timeout(popup, 1500); + popup_enable_timeout(popup); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidViewPopup); + notification_message_block(app->notifications, &sequence_set_green_255); +} + +bool lfrfid_scene_write_success_on_event(void* context, SceneManagerEvent event) { + LfRfid* app = context; + bool consumed = false; + + const uint32_t prev_scenes[] = {LfRfidSceneReadKeyMenu, LfRfidSceneSelectKey}; + + if((event.type == SceneManagerEventTypeBack) || + ((event.type == SceneManagerEventTypeCustom) && (event.event == LfRfidEventPopupClosed))) { + scene_manager_search_and_switch_to_previous_scene_one_of( + app->scene_manager, prev_scenes, COUNT_OF(prev_scenes)); + consumed = true; + } + + return consumed; +} + +void lfrfid_scene_write_success_on_exit(void* context) { + LfRfid* app = context; + notification_message_block(app->notifications, &sequence_reset_green); + popup_reset(app->popup); +} diff --git a/applications/lfrfid/view/container_vm.cpp b/applications/lfrfid/view/container_vm.cpp deleted file mode 100644 index 3c01ba3042..0000000000 --- a/applications/lfrfid/view/container_vm.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "container_vm.h" -#include "elements/generic_element.h" -#include "elements/string_element.h" -#include "elements/icon_element.h" -#include "elements/button_element.h" -#include - -class ContainerVMData { -public: - ContainerVMData(){}; - - ~ContainerVMData() { - for(auto& it : elements) delete it; - }; - - std::list elements; - - template T add(const T element, View* view) { - elements.push_back(element); - element->set_parent_view(view); - return element; - } - - void clean() { - for(auto& it : elements) delete it; - elements.clear(); - } -}; - -struct ContainerVMModel { - ContainerVMData* data; -}; - -ContainerVM::ContainerVM() { - view = view_alloc(); - view_set_context(view, this); - view_allocate_model(view, ViewModelTypeLocking, sizeof(ContainerVMModel)); - - with_view_model_cpp(view, ContainerVMModel, model, { - model->data = new ContainerVMData(); - return true; - }); - - view_set_draw_callback(view, view_draw_callback); - view_set_input_callback(view, view_input_callback); -} - -ContainerVM::~ContainerVM() { - with_view_model_cpp(view, ContainerVMModel, model, { - delete model->data; - model->data = NULL; - return false; - }); - - view_free(view); -} - -View* ContainerVM::get_view() { - return view; -} - -void ContainerVM::clean() { - with_view_model_cpp(view, ContainerVMModel, model, { - model->data->clean(); - return true; - }); -} - -template T* ContainerVM::add() { - T* element = new T(); - - with_view_model_cpp(view, ContainerVMModel, model, { - model->data->add(element, view); - return true; - }); - - return element; -} - -void ContainerVM::view_draw_callback(Canvas* canvas, void* model) { - ContainerVMData* data = static_cast(model)->data; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - - for(const auto& element : data->elements) { - element->draw(canvas); - } -} - -bool ContainerVM::view_input_callback(InputEvent* event, void* context) { - bool consumed = false; - View* view = static_cast(context)->view; - - with_view_model_cpp(view, ContainerVMModel, model, { - for(const auto& element : model->data->elements) { - if(element->input(event)) { - consumed = true; - } - - if(consumed) { - break; - } - } - - return consumed; - }); - - return consumed; -} - -template StringElement* ContainerVM::add(); -template IconElement* ContainerVM::add(); -template ButtonElement* ContainerVM::add(); diff --git a/applications/lfrfid/view/container_vm.h b/applications/lfrfid/view/container_vm.h deleted file mode 100644 index 011baa2e98..0000000000 --- a/applications/lfrfid/view/container_vm.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include - -class ContainerVM : public GenericViewModule { -public: - ContainerVM(); - ~ContainerVM() final; - View* get_view() final; - void clean() final; - - template T* add(); - -private: - View* view; - static void view_draw_callback(Canvas* canvas, void* model); - static bool view_input_callback(InputEvent* event, void* context); -}; diff --git a/applications/lfrfid/view/elements/button_element.cpp b/applications/lfrfid/view/elements/button_element.cpp deleted file mode 100644 index 58e1ac3e1c..0000000000 --- a/applications/lfrfid/view/elements/button_element.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "button_element.h" -#include - -ButtonElement::ButtonElement() { -} - -ButtonElement::~ButtonElement() { -} - -void ButtonElement::draw(Canvas* canvas) { - if(text != nullptr) { - canvas_set_font(canvas, FontSecondary); - switch(type) { - case Type::Left: - elements_button_left(canvas, text); - break; - case Type::Center: - elements_button_center(canvas, text); - break; - case Type::Right: - elements_button_right(canvas, text); - break; - } - } -} - -bool ButtonElement::input(InputEvent* event) { - bool consumed = false; - if(event->type == InputTypeShort && callback != nullptr) { - switch(type) { - case Type::Left: - if(event->key == InputKeyLeft) { - callback(context); - consumed = true; - } - break; - case Type::Center: - if(event->key == InputKeyOk) { - callback(context); - consumed = true; - } - break; - case Type::Right: - if(event->key == InputKeyRight) { - callback(context); - consumed = true; - } - break; - } - } - - return consumed; -} - -void ButtonElement::set_type(Type _type, const char* _text) { - lock_model(); - type = _type; - text = _text; - unlock_model(true); -} - -void ButtonElement::set_callback(void* _context, ButtonElementCallback _callback) { - context = _context; - callback = _callback; -} diff --git a/applications/lfrfid/view/elements/button_element.h b/applications/lfrfid/view/elements/button_element.h deleted file mode 100644 index eb96442773..0000000000 --- a/applications/lfrfid/view/elements/button_element.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "generic_element.h" - -typedef void (*ButtonElementCallback)(void* context); - -class ButtonElement : public GenericElement { -public: - ButtonElement(); - ~ButtonElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - enum class Type : uint8_t { - Left, - Center, - Right, - }; - - void set_type(Type type, const char* text); - void set_callback(void* context, ButtonElementCallback callback); - -private: - Type type = Type::Left; - const char* text = nullptr; - - void* context = nullptr; - ButtonElementCallback callback = nullptr; -}; diff --git a/applications/lfrfid/view/elements/generic_element.cpp b/applications/lfrfid/view/elements/generic_element.cpp deleted file mode 100644 index e0f08d15c0..0000000000 --- a/applications/lfrfid/view/elements/generic_element.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "generic_element.h" - -void GenericElement::lock_model() { - furi_assert(view != nullptr); - view_get_model(view); -} - -void GenericElement::unlock_model(bool need_redraw) { - furi_assert(view != nullptr); - view_commit_model(view, need_redraw); -} - -void GenericElement::set_parent_view(View* _view) { - view = _view; -} diff --git a/applications/lfrfid/view/elements/generic_element.h b/applications/lfrfid/view/elements/generic_element.h deleted file mode 100644 index f5a58b2d92..0000000000 --- a/applications/lfrfid/view/elements/generic_element.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include - -class GenericElement { -public: - GenericElement(){}; - virtual ~GenericElement(){}; - virtual void draw(Canvas* canvas) = 0; - virtual bool input(InputEvent* event) = 0; - - // TODO that must be accessible only to ContainerVMData - void set_parent_view(View* view); - - // TODO that must be accessible only to inheritors - void lock_model(); - void unlock_model(bool need_redraw); - -private: - View* view = nullptr; -}; diff --git a/applications/lfrfid/view/elements/icon_element.cpp b/applications/lfrfid/view/elements/icon_element.cpp deleted file mode 100644 index 0b6fba7dad..0000000000 --- a/applications/lfrfid/view/elements/icon_element.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "icon_element.h" - -IconElement::IconElement() { -} - -IconElement::~IconElement() { -} - -void IconElement::draw(Canvas* canvas) { - if(icon != NULL) { - canvas_draw_icon(canvas, x, y, icon); - } -} - -bool IconElement::input(InputEvent* /* event */) { - return false; -} - -void IconElement::set_icon(uint8_t _x, uint8_t _y, const Icon* _icon) { - lock_model(); - icon = _icon; - x = _x; - y = _y; - unlock_model(true); -} diff --git a/applications/lfrfid/view/elements/icon_element.h b/applications/lfrfid/view/elements/icon_element.h deleted file mode 100644 index a08202741e..0000000000 --- a/applications/lfrfid/view/elements/icon_element.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "generic_element.h" - -class IconElement : public GenericElement { -public: - IconElement(); - ~IconElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - void set_icon(uint8_t x = 0, uint8_t y = 0, const Icon* icon = NULL); - -private: - const Icon* icon = NULL; - uint8_t x = 0; - uint8_t y = 0; -}; diff --git a/applications/lfrfid/view/elements/string_element.cpp b/applications/lfrfid/view/elements/string_element.cpp deleted file mode 100644 index 44c11e01a4..0000000000 --- a/applications/lfrfid/view/elements/string_element.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "string_element.h" -#include - -StringElement::StringElement() { -} - -StringElement::~StringElement() { -} - -void StringElement::draw(Canvas* canvas) { - if(text) { - string_t line; - string_init(line); - string_set_str(line, text); - - canvas_set_font(canvas, font); - if(fit_width != 0) { - elements_string_fit_width(canvas, line, fit_width); - } - elements_multiline_text_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(line)); - - string_clear(line); - } -} - -bool StringElement::input(InputEvent* /* event */) { - return false; -} - -void StringElement::set_text( - const char* _text, - uint8_t _x, - uint8_t _y, - uint8_t _fit_w, - Align _horizontal, - Align _vertical, - Font _font) { - lock_model(); - text = _text; - x = _x; - y = _y; - fit_width = _fit_w; - horizontal = _horizontal; - vertical = _vertical; - font = _font; - unlock_model(true); -} diff --git a/applications/lfrfid/view/elements/string_element.h b/applications/lfrfid/view/elements/string_element.h deleted file mode 100644 index 173fdd6019..0000000000 --- a/applications/lfrfid/view/elements/string_element.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "generic_element.h" - -class StringElement : public GenericElement { -public: - StringElement(); - ~StringElement() final; - void draw(Canvas* canvas) final; - bool input(InputEvent* event) final; - - void set_text( - const char* text = NULL, - uint8_t x = 0, - uint8_t y = 0, - uint8_t fit_width = 0, - Align horizontal = AlignLeft, - Align vertical = AlignTop, - Font font = FontPrimary); - -private: - const char* text = NULL; - uint8_t x = 0; - uint8_t y = 0; - uint8_t fit_width = 0; - Align horizontal = AlignLeft; - Align vertical = AlignTop; - Font font = FontPrimary; -}; diff --git a/applications/lfrfid/views/lfrfid_view_read.c b/applications/lfrfid/views/lfrfid_view_read.c new file mode 100644 index 0000000000..2b63175da2 --- /dev/null +++ b/applications/lfrfid/views/lfrfid_view_read.c @@ -0,0 +1,117 @@ +#include "lfrfid_view_read.h" +#include + +#define TEMP_STR_LEN 128 + +struct LfRfidReadView { + View* view; +}; + +typedef struct { + IconAnimation* icon; + LfRfidReadViewMode read_mode; +} LfRfidReadViewModel; + +static void lfrfid_view_read_draw_callback(Canvas* canvas, void* _model) { + LfRfidReadViewModel* model = _model; + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 0, 8, &I_NFC_manual); + + canvas_set_font(canvas, FontPrimary); + + if(model->read_mode == LfRfidReadAsk) { + canvas_draw_str(canvas, 70, 16, "Reading 1/2"); + + canvas_draw_str(canvas, 77, 29, "ASK"); + canvas_draw_icon(canvas, 70, 22, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 102, 21, model->icon); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 77, 43, "PSK"); + } else if(model->read_mode == LfRfidReadPsk) { + canvas_draw_str(canvas, 70, 16, "Reading 2/2"); + + canvas_draw_str(canvas, 77, 43, "PSK"); + canvas_draw_icon(canvas, 70, 36, &I_ButtonRight_4x7); + canvas_draw_icon_animation(canvas, 102, 35, model->icon); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 77, 29, "ASK"); + } else { + canvas_draw_str(canvas, 72, 16, "Reading"); + + if(model->read_mode == LfRfidReadAskOnly) { + canvas_draw_str(canvas, 77, 35, "ASK"); + } else { + canvas_draw_str(canvas, 77, 35, "PSK"); + } + canvas_draw_icon_animation(canvas, 102, 27, model->icon); + } + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 61, 56, "Don't move card"); +} + +void lfrfid_view_read_enter(void* context) { + LfRfidReadView* read_view = context; + with_view_model( + read_view->view, (LfRfidReadViewModel * model) { + icon_animation_start(model->icon); + return true; + }); +} + +void lfrfid_view_read_exit(void* context) { + LfRfidReadView* read_view = context; + with_view_model( + read_view->view, (LfRfidReadViewModel * model) { + icon_animation_stop(model->icon); + return false; + }); +} + +LfRfidReadView* lfrfid_view_read_alloc() { + LfRfidReadView* read_view = malloc(sizeof(LfRfidReadView)); + read_view->view = view_alloc(); + view_set_context(read_view->view, read_view); + view_allocate_model(read_view->view, ViewModelTypeLocking, sizeof(LfRfidReadViewModel)); + + with_view_model( + read_view->view, (LfRfidReadViewModel * model) { + model->icon = icon_animation_alloc(&A_Round_loader_8x8); + view_tie_icon_animation(read_view->view, model->icon); + return false; + }); + + view_set_draw_callback(read_view->view, lfrfid_view_read_draw_callback); + view_set_enter_callback(read_view->view, lfrfid_view_read_enter); + view_set_exit_callback(read_view->view, lfrfid_view_read_exit); + + return read_view; +} + +void lfrfid_view_read_free(LfRfidReadView* read_view) { + with_view_model( + read_view->view, (LfRfidReadViewModel * model) { + icon_animation_free(model->icon); + return false; + }); + + view_free(read_view->view); + free(read_view); +} + +View* lfrfid_view_read_get_view(LfRfidReadView* read_view) { + return read_view->view; +} + +void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode) { + with_view_model( + read_view->view, (LfRfidReadViewModel * model) { + icon_animation_stop(model->icon); + icon_animation_start(model->icon); + model->read_mode = mode; + return true; + }); +} diff --git a/applications/lfrfid/views/lfrfid_view_read.h b/applications/lfrfid/views/lfrfid_view_read.h new file mode 100644 index 0000000000..55bb1f230f --- /dev/null +++ b/applications/lfrfid/views/lfrfid_view_read.h @@ -0,0 +1,19 @@ +#pragma once +#include + +typedef enum { + LfRfidReadAsk, + LfRfidReadPsk, + LfRfidReadAskOnly, + LfRfidReadPskOnly +} LfRfidReadViewMode; + +typedef struct LfRfidReadView LfRfidReadView; + +LfRfidReadView* lfrfid_view_read_alloc(); + +void lfrfid_view_read_free(LfRfidReadView* read_view); + +View* lfrfid_view_read_get_view(LfRfidReadView* read_view); + +void lfrfid_view_read_set_read_mode(LfRfidReadView* read_view, LfRfidReadViewMode mode); diff --git a/applications/lfrfid_debug/lfrfid_debug.c b/applications/lfrfid_debug/lfrfid_debug.c new file mode 100644 index 0000000000..63d66b68b9 --- /dev/null +++ b/applications/lfrfid_debug/lfrfid_debug.c @@ -0,0 +1,81 @@ +#include "lfrfid_debug_i.h" + +static bool lfrfid_debug_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + LfRfidDebug* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool lfrfid_debug_back_event_callback(void* context) { + furi_assert(context); + LfRfidDebug* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static LfRfidDebug* lfrfid_debug_alloc() { + LfRfidDebug* app = malloc(sizeof(LfRfidDebug)); + + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&lfrfid_debug_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, lfrfid_debug_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, lfrfid_debug_back_event_callback); + + // Open GUI record + app->gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + // Submenu + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, LfRfidDebugViewSubmenu, submenu_get_view(app->submenu)); + + // Tune view + app->tune_view = lfrfid_debug_view_tune_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + LfRfidDebugViewTune, + lfrfid_debug_view_tune_get_view(app->tune_view)); + + return app; +} + +static void lfrfid_debug_free(LfRfidDebug* app) { + furi_assert(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, LfRfidDebugViewSubmenu); + submenu_free(app->submenu); + + // Tune view + view_dispatcher_remove_view(app->view_dispatcher, LfRfidDebugViewTune); + lfrfid_debug_view_tune_free(app->tune_view); + + // View Dispatcher + view_dispatcher_free(app->view_dispatcher); + + // Scene Manager + scene_manager_free(app->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + app->gui = NULL; + + free(app); +} + +int32_t lfrfid_debug_app(void* p) { + UNUSED(p); + LfRfidDebug* app = lfrfid_debug_alloc(); + + scene_manager_next_scene(app->scene_manager, LfRfidDebugSceneStart); + + view_dispatcher_run(app->view_dispatcher); + + lfrfid_debug_free(app); + + return 0; +} \ No newline at end of file diff --git a/applications/lfrfid_debug/lfrfid_debug_app.cpp b/applications/lfrfid_debug/lfrfid_debug_app.cpp deleted file mode 100644 index ef970e3617..0000000000 --- a/applications/lfrfid_debug/lfrfid_debug_app.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "lfrfid_debug_app.h" -#include "scene/lfrfid_debug_app_scene_start.h" -#include "scene/lfrfid_debug_app_scene_tune.h" - -LfRfidDebugApp::LfRfidDebugApp() - : scene_controller{this} { -} - -LfRfidDebugApp::~LfRfidDebugApp() { -} - -void LfRfidDebugApp::run() { - view_controller.attach_to_gui(ViewDispatcherTypeFullscreen); - scene_controller.add_scene(SceneType::Start, new LfRfidDebugAppSceneStart()); - scene_controller.add_scene(SceneType::TuneScene, new LfRfidDebugAppSceneTune()); - scene_controller.process(100); -} diff --git a/applications/lfrfid_debug/lfrfid_debug_app.h b/applications/lfrfid_debug/lfrfid_debug_app.h deleted file mode 100644 index fee183aec4..0000000000 --- a/applications/lfrfid_debug/lfrfid_debug_app.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include -#include - -#include -#include -#include - -#include -#include "view_modules/lfrfid_view_tune_vm.h" - -class LfRfidDebugApp { -public: - enum class EventType : uint8_t { - GENERIC_EVENT_ENUM_VALUES, - MenuSelected, - }; - - enum class SceneType : uint8_t { - GENERIC_SCENE_ENUM_VALUES, - TuneScene, - }; - - class Event { - public: - union { - int32_t menu_index; - } payload; - - EventType type; - }; - - SceneController, LfRfidDebugApp> scene_controller; - ViewController view_controller; - - ~LfRfidDebugApp(); - LfRfidDebugApp(); - - void run(); -}; diff --git a/applications/lfrfid_debug/lfrfid_debug_app_launcher.cpp b/applications/lfrfid_debug/lfrfid_debug_app_launcher.cpp deleted file mode 100644 index 4551a17cb4..0000000000 --- a/applications/lfrfid_debug/lfrfid_debug_app_launcher.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "lfrfid_debug_app.h" - -// app enter function -extern "C" int32_t lfrfid_debug_app(void* p) { - UNUSED(p); - LfRfidDebugApp* app = new LfRfidDebugApp(); - app->run(); - delete app; - - return 0; -} diff --git a/applications/lfrfid_debug/lfrfid_debug_i.h b/applications/lfrfid_debug/lfrfid_debug_i.h new file mode 100644 index 0000000000..368f1f1586 --- /dev/null +++ b/applications/lfrfid_debug/lfrfid_debug_i.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +typedef struct LfRfidDebug LfRfidDebug; + +struct LfRfidDebug { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + + // Common Views + Submenu* submenu; + LfRfidTuneView* tune_view; +}; + +typedef enum { + LfRfidDebugViewSubmenu, + LfRfidDebugViewTune, +} LfRfidDebugView; diff --git a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.cpp b/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.cpp deleted file mode 100644 index 873e152a10..0000000000 --- a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "lfrfid_debug_app_scene_start.h" - -typedef enum { - SubmenuTune, -} SubmenuIndex; - -void LfRfidDebugAppSceneStart::on_enter(LfRfidDebugApp* app, bool need_restore) { - auto submenu = app->view_controller.get(); - auto callback = cbc::obtain_connector(this, &LfRfidDebugAppSceneStart::submenu_callback); - - submenu->add_item("Tune", SubmenuTune, callback, app); - - if(need_restore) { - submenu->set_selected_item(submenu_item_selected); - } - app->view_controller.switch_to(); -} - -bool LfRfidDebugAppSceneStart::on_event(LfRfidDebugApp* app, LfRfidDebugApp::Event* event) { - bool consumed = false; - - if(event->type == LfRfidDebugApp::EventType::MenuSelected) { - submenu_item_selected = event->payload.menu_index; - switch(event->payload.menu_index) { - case SubmenuTune: - app->scene_controller.switch_to_next_scene(LfRfidDebugApp::SceneType::TuneScene); - break; - } - consumed = true; - } - - return consumed; -} - -void LfRfidDebugAppSceneStart::on_exit(LfRfidDebugApp* app) { - app->view_controller.get()->clean(); -} - -void LfRfidDebugAppSceneStart::submenu_callback(void* context, uint32_t index) { - LfRfidDebugApp* app = static_cast(context); - LfRfidDebugApp::Event event; - - event.type = LfRfidDebugApp::EventType::MenuSelected; - event.payload.menu_index = index; - - app->view_controller.send_event(&event); -} diff --git a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.h b/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.h deleted file mode 100644 index 7fc0b07d97..0000000000 --- a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_start.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "../lfrfid_debug_app.h" - -class LfRfidDebugAppSceneStart : public GenericScene { -public: - void on_enter(LfRfidDebugApp* app, bool need_restore) final; - bool on_event(LfRfidDebugApp* app, LfRfidDebugApp::Event* event) final; - void on_exit(LfRfidDebugApp* app) final; - -private: - void submenu_callback(void* context, uint32_t index); - uint32_t submenu_item_selected = 0; -}; diff --git a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.h b/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.h deleted file mode 100644 index 53399efc9b..0000000000 --- a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include "../lfrfid_debug_app.h" - -class LfRfidDebugAppSceneTune : public GenericScene { -public: - void on_enter(LfRfidDebugApp* app, bool need_restore) final; - bool on_event(LfRfidDebugApp* app, LfRfidDebugApp::Event* event) final; - void on_exit(LfRfidDebugApp* app) final; -}; diff --git a/applications/lfrfid_debug/scenes/lfrfid_debug_app_scene_start.c b/applications/lfrfid_debug/scenes/lfrfid_debug_app_scene_start.c new file mode 100644 index 0000000000..2fc6706e38 --- /dev/null +++ b/applications/lfrfid_debug/scenes/lfrfid_debug_app_scene_start.c @@ -0,0 +1,44 @@ +#include "../lfrfid_debug_i.h" + +typedef enum { + SubmenuIndexTune, +} SubmenuIndex; + +static void lfrfid_debug_scene_start_submenu_callback(void* context, uint32_t index) { + LfRfidDebug* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void lfrfid_debug_scene_start_on_enter(void* context) { + LfRfidDebug* app = context; + Submenu* submenu = app->submenu; + + submenu_add_item( + submenu, "Tune", SubmenuIndexTune, lfrfid_debug_scene_start_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, LfRfidDebugSceneStart)); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewSubmenu); +} + +bool lfrfid_debug_scene_start_on_event(void* context, SceneManagerEvent event) { + LfRfidDebug* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexTune) { + scene_manager_next_scene(app->scene_manager, LfRfidDebugSceneTune); + consumed = true; + } + } + + return consumed; +} + +void lfrfid_debug_scene_start_on_exit(void* context) { + LfRfidDebug* app = context; + + submenu_reset(app->submenu); +} diff --git a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.cpp b/applications/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c similarity index 53% rename from applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.cpp rename to applications/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c index 4b6276497f..c7f3bf24fd 100644 --- a/applications/lfrfid_debug/scene/lfrfid_debug_app_scene_tune.cpp +++ b/applications/lfrfid_debug/scenes/lfrfid_debug_app_scene_tune.c @@ -1,4 +1,4 @@ -#include "lfrfid_debug_app_scene_tune.h" +#include "../lfrfid_debug_i.h" #include static void comparator_trigger_callback(bool level, void* comp_ctx) { @@ -6,32 +6,38 @@ static void comparator_trigger_callback(bool level, void* comp_ctx) { furi_hal_gpio_write(&gpio_ext_pa7, !level); } -void LfRfidDebugAppSceneTune::on_enter(LfRfidDebugApp* app, bool /* need_restore */) { - app->view_controller.switch_to(); +void lfrfid_debug_scene_tune_on_enter(void* context) { + LfRfidDebug* app = context; + furi_hal_gpio_init_simple(&gpio_ext_pa7, GpioModeOutputPushPull); - furi_hal_rfid_comp_set_callback(comparator_trigger_callback, this); + furi_hal_rfid_comp_set_callback(comparator_trigger_callback, app); furi_hal_rfid_comp_start(); furi_hal_rfid_pins_read(); furi_hal_rfid_tim_read(125000, 0.5); furi_hal_rfid_tim_read_start(); + + view_dispatcher_switch_to_view(app->view_dispatcher, LfRfidDebugViewTune); } -bool LfRfidDebugAppSceneTune::on_event(LfRfidDebugApp* app, LfRfidDebugApp::Event* /* event */) { - bool consumed = false; +bool lfrfid_debug_scene_tune_on_event(void* context, SceneManagerEvent event) { + UNUSED(event); - LfRfidViewTuneVM* tune = app->view_controller; + LfRfidDebug* app = context; + bool consumed = false; - if(tune->is_dirty()) { - furi_hal_rfid_set_read_period(tune->get_ARR()); - furi_hal_rfid_set_read_pulse(tune->get_CCR()); + if(lfrfid_debug_view_tune_is_dirty(app->tune_view)) { + furi_hal_rfid_set_read_period(lfrfid_debug_view_tune_get_arr(app->tune_view)); + furi_hal_rfid_set_read_pulse(lfrfid_debug_view_tune_get_ccr(app->tune_view)); } return consumed; } -void LfRfidDebugAppSceneTune::on_exit(LfRfidDebugApp* /* app */) { +void lfrfid_debug_scene_tune_on_exit(void* context) { + UNUSED(context); + furi_hal_rfid_comp_stop(); furi_hal_rfid_comp_set_callback(NULL, NULL); diff --git a/applications/lfrfid_debug/scenes/lfrfid_debug_scene.c b/applications/lfrfid_debug/scenes/lfrfid_debug_scene.c new file mode 100644 index 0000000000..e6288e923b --- /dev/null +++ b/applications/lfrfid_debug/scenes/lfrfid_debug_scene.c @@ -0,0 +1,30 @@ +#include "lfrfid_debug_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const lfrfid_debug_on_enter_handlers[])(void*) = { +#include "lfrfid_debug_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 lfrfid_debug_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "lfrfid_debug_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 lfrfid_debug_on_exit_handlers[])(void* context) = { +#include "lfrfid_debug_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers lfrfid_debug_scene_handlers = { + .on_enter_handlers = lfrfid_debug_on_enter_handlers, + .on_event_handlers = lfrfid_debug_on_event_handlers, + .on_exit_handlers = lfrfid_debug_on_exit_handlers, + .scene_num = LfRfidDebugSceneNum, +}; diff --git a/applications/lfrfid_debug/scenes/lfrfid_debug_scene.h b/applications/lfrfid_debug/scenes/lfrfid_debug_scene.h new file mode 100644 index 0000000000..8fc74f7240 --- /dev/null +++ b/applications/lfrfid_debug/scenes/lfrfid_debug_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) LfRfidDebugScene##id, +typedef enum { +#include "lfrfid_debug_scene_config.h" + LfRfidDebugSceneNum, +} LfRfidDebugScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers lfrfid_debug_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "lfrfid_debug_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 "lfrfid_debug_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 "lfrfid_debug_scene_config.h" +#undef ADD_SCENE diff --git a/applications/lfrfid_debug/scenes/lfrfid_debug_scene_config.h b/applications/lfrfid_debug/scenes/lfrfid_debug_scene_config.h new file mode 100644 index 0000000000..f3cca47b88 --- /dev/null +++ b/applications/lfrfid_debug/scenes/lfrfid_debug_scene_config.h @@ -0,0 +1,2 @@ +ADD_SCENE(lfrfid_debug, start, Start) +ADD_SCENE(lfrfid_debug, tune, Tune) diff --git a/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.cpp b/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.cpp deleted file mode 100644 index 5c244b92ca..0000000000 --- a/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include "lfrfid_view_tune_vm.h" -#include -#include - -struct LfRfidViewTuneVMModel { - bool dirty; - bool fine; - uint32_t ARR; - uint32_t CCR; - int pos; -}; - -void LfRfidViewTuneVM::view_draw_callback(Canvas* canvas, void* _model) { - LfRfidViewTuneVMModel* model = reinterpret_cast(_model); - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - if(model->fine) { - canvas_draw_box( - canvas, - 128 - canvas_string_width(canvas, "Fine") - 4, - 0, - canvas_string_width(canvas, "Fine") + 4, - canvas_current_font_height(canvas) + 1); - canvas_set_color(canvas, ColorWhite); - } - canvas_draw_str_aligned(canvas, 128 - 2, 2, AlignRight, AlignTop, "Fine"); - canvas_set_color(canvas, ColorBlack); - - constexpr uint8_t buffer_size = 128; - char buffer[buffer_size + 1]; - double freq = ((float)SystemCoreClock / ((float)model->ARR + 1)); - double duty = ((float)model->CCR + 1) / ((float)model->ARR + 1) * 100.0f; - snprintf( - buffer, - buffer_size, - "%sARR: %lu\n" - "freq = %.4f\n" - "%sCCR: %lu\n" - "duty = %.4f", - model->pos == 0 ? ">" : "", - model->ARR, - freq, - model->pos == 1 ? ">" : "", - model->CCR, - duty); - elements_multiline_text_aligned(canvas, 2, 2, AlignLeft, AlignTop, buffer); -} - -bool LfRfidViewTuneVM::view_input_callback(InputEvent* event, void* context) { - LfRfidViewTuneVM* _this = reinterpret_cast(context); - bool consumed = false; - - // Process key presses only - if(event->type == InputTypeShort || event->type == InputTypeRepeat) { - consumed = true; - - switch(event->key) { - case InputKeyLeft: - _this->button_left(); - break; - case InputKeyRight: - _this->button_right(); - break; - case InputKeyUp: - _this->button_up(); - break; - case InputKeyDown: - _this->button_down(); - break; - case InputKeyOk: - _this->button_ok(); - break; - default: - consumed = false; - break; - } - } - - return consumed; -} - -void LfRfidViewTuneVM::button_up() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - if(model->pos > 0) model->pos--; - return true; - }); -} - -void LfRfidViewTuneVM::button_down() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - if(model->pos < 1) model->pos++; - return true; - }); -} - -void LfRfidViewTuneVM::button_left() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - if(model->pos == 0) { - if(model->fine) { - model->ARR -= 1; - } else { - model->ARR -= 10; - } - } else if(model->pos == 1) { - if(model->fine) { - model->CCR -= 1; - } else { - model->CCR -= 10; - } - } - - model->dirty = true; - return true; - }); -} - -void LfRfidViewTuneVM::button_right() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - if(model->pos == 0) { - if(model->fine) { - model->ARR += 1; - } else { - model->ARR += 10; - } - } else if(model->pos == 1) { - if(model->fine) { - model->CCR += 1; - } else { - model->CCR += 10; - } - } - - model->dirty = true; - return true; - }); -} - -void LfRfidViewTuneVM::button_ok() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - model->fine = !model->fine; - return true; - }); -} - -LfRfidViewTuneVM::LfRfidViewTuneVM() { - view = view_alloc(); - view_set_context(view, this); - view_allocate_model(view, ViewModelTypeLocking, sizeof(LfRfidViewTuneVMModel)); - - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - model->dirty = true; - model->fine = false; - model->ARR = 511; - model->CCR = 255; - model->pos = 0; - return true; - }); - - view_set_draw_callback( - view, cbc::obtain_connector(this, &LfRfidViewTuneVM::view_draw_callback)); - view_set_input_callback( - view, cbc::obtain_connector(this, &LfRfidViewTuneVM::view_input_callback)); -} - -LfRfidViewTuneVM::~LfRfidViewTuneVM() { - view_free(view); -} - -View* LfRfidViewTuneVM::get_view() { - return view; -} - -void LfRfidViewTuneVM::clean() { - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - model->dirty = true; - model->fine = false; - model->ARR = 511; - model->CCR = 255; - model->pos = 0; - return true; - }); -} - -bool LfRfidViewTuneVM::is_dirty() { - bool result; - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - result = model->dirty; - model->dirty = false; - return false; - }); - - return result; -} - -uint32_t LfRfidViewTuneVM::get_ARR() { - uint32_t result; - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - result = model->ARR; - return false; - }); - - return result; -} - -uint32_t LfRfidViewTuneVM::get_CCR() { - uint32_t result; - with_view_model_cpp(view, LfRfidViewTuneVMModel, model, { - result = model->CCR; - return false; - }); - - return result; -} diff --git a/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.h b/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.h deleted file mode 100644 index 7fb1856548..0000000000 --- a/applications/lfrfid_debug/view_modules/lfrfid_view_tune_vm.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include -#include - -class LfRfidViewTuneVM : public GenericViewModule { -public: - LfRfidViewTuneVM(); - ~LfRfidViewTuneVM() final; - View* get_view() final; - void clean() final; - - bool is_dirty(); - uint32_t get_ARR(); - uint32_t get_CCR(); - -private: - View* view; - void view_draw_callback(Canvas* canvas, void* _model); - bool view_input_callback(InputEvent* event, void* context); - - void button_up(); - void button_down(); - void button_left(); - void button_right(); - void button_ok(); -}; diff --git a/applications/lfrfid_debug/views/lfrfid_debug_view_tune.c b/applications/lfrfid_debug/views/lfrfid_debug_view_tune.c new file mode 100644 index 0000000000..38fe360365 --- /dev/null +++ b/applications/lfrfid_debug/views/lfrfid_debug_view_tune.c @@ -0,0 +1,229 @@ +#include "lfrfid_debug_view_tune.h" +#include + +#define TEMP_STR_LEN 128 + +struct LfRfidTuneView { + View* view; +}; + +typedef struct { + bool dirty; + bool fine; + uint32_t ARR; + uint32_t CCR; + int pos; +} LfRfidTuneViewModel; + +static void lfrfid_debug_view_tune_draw_callback(Canvas* canvas, void* _model) { + LfRfidTuneViewModel* model = _model; + canvas_set_color(canvas, ColorBlack); + + if(model->fine) { + canvas_draw_box( + canvas, + 128 - canvas_string_width(canvas, "Fine") - 4, + 0, + canvas_string_width(canvas, "Fine") + 4, + canvas_current_font_height(canvas) + 1); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_str_aligned(canvas, 128 - 2, 2, AlignRight, AlignTop, "Fine"); + canvas_set_color(canvas, ColorBlack); + + char buffer[TEMP_STR_LEN + 1]; + double freq = ((float)SystemCoreClock / ((float)model->ARR + 1)); + double duty = ((float)model->CCR + 1) / ((float)model->ARR + 1) * 100.0f; + snprintf( + buffer, + TEMP_STR_LEN, + "%sARR: %lu\n" + "freq = %.4f\n" + "%sCCR: %lu\n" + "duty = %.4f", + model->pos == 0 ? ">" : "", + model->ARR, + freq, + model->pos == 1 ? ">" : "", + model->CCR, + duty); + elements_multiline_text_aligned(canvas, 2, 2, AlignLeft, AlignTop, buffer); +} + +static void lfrfid_debug_view_tune_button_up(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + if(model->pos > 0) model->pos--; + return true; + }); +} + +static void lfrfid_debug_view_tune_button_down(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + if(model->pos < 1) model->pos++; + return true; + }); +} + +static void lfrfid_debug_view_tune_button_left(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + if(model->pos == 0) { + if(model->fine) { + model->ARR -= 1; + } else { + model->ARR -= 10; + } + } else if(model->pos == 1) { + if(model->fine) { + model->CCR -= 1; + } else { + model->CCR -= 10; + } + } + + model->dirty = true; + return true; + }); +} + +static void lfrfid_debug_view_tune_button_right(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + if(model->pos == 0) { + if(model->fine) { + model->ARR += 1; + } else { + model->ARR += 10; + } + } else if(model->pos == 1) { + if(model->fine) { + model->CCR += 1; + } else { + model->CCR += 10; + } + } + + model->dirty = true; + return true; + }); +} + +static void lfrfid_debug_view_tune_button_ok(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + model->fine = !model->fine; + return true; + }); +} + +static bool lfrfid_debug_view_tune_input_callback(InputEvent* event, void* context) { + LfRfidTuneView* tune_view = context; + bool consumed = false; + + // Process key presses only + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + consumed = true; + + switch(event->key) { + case InputKeyLeft: + lfrfid_debug_view_tune_button_left(tune_view); + break; + case InputKeyRight: + lfrfid_debug_view_tune_button_right(tune_view); + break; + case InputKeyUp: + lfrfid_debug_view_tune_button_up(tune_view); + break; + case InputKeyDown: + lfrfid_debug_view_tune_button_down(tune_view); + break; + case InputKeyOk: + lfrfid_debug_view_tune_button_ok(tune_view); + break; + default: + consumed = false; + break; + } + } + + return consumed; +} + +LfRfidTuneView* lfrfid_debug_view_tune_alloc() { + LfRfidTuneView* tune_view = malloc(sizeof(LfRfidTuneView)); + tune_view->view = view_alloc(); + view_set_context(tune_view->view, tune_view); + view_allocate_model(tune_view->view, ViewModelTypeLocking, sizeof(LfRfidTuneViewModel)); + + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + model->dirty = true; + model->fine = false; + model->ARR = 511; + model->CCR = 255; + model->pos = 0; + return true; + }); + + view_set_draw_callback(tune_view->view, lfrfid_debug_view_tune_draw_callback); + view_set_input_callback(tune_view->view, lfrfid_debug_view_tune_input_callback); + + return tune_view; +} + +void lfrfid_debug_view_tune_free(LfRfidTuneView* tune_view) { + view_free(tune_view->view); + free(tune_view); +} + +View* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view) { + return tune_view->view; +} + +void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view) { + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + model->dirty = true; + model->fine = false; + model->ARR = 511; + model->CCR = 255; + model->pos = 0; + return true; + }); +} + +bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view) { + bool result = false; + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + result = model->dirty; + model->dirty = false; + return false; + }); + + return result; +} + +uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view) { + uint32_t result = false; + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + result = model->ARR; + return false; + }); + + return result; +} + +uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view) { + uint32_t result = false; + with_view_model( + tune_view->view, (LfRfidTuneViewModel * model) { + result = model->CCR; + return false; + }); + + return result; +} diff --git a/applications/lfrfid_debug/views/lfrfid_debug_view_tune.h b/applications/lfrfid_debug/views/lfrfid_debug_view_tune.h new file mode 100644 index 0000000000..fd6d0b1fe9 --- /dev/null +++ b/applications/lfrfid_debug/views/lfrfid_debug_view_tune.h @@ -0,0 +1,18 @@ +#pragma once +#include + +typedef struct LfRfidTuneView LfRfidTuneView; + +LfRfidTuneView* lfrfid_debug_view_tune_alloc(); + +void lfrfid_debug_view_tune_free(LfRfidTuneView* tune_view); + +View* lfrfid_debug_view_tune_get_view(LfRfidTuneView* tune_view); + +void lfrfid_debug_view_tune_clean(LfRfidTuneView* tune_view); + +bool lfrfid_debug_view_tune_is_dirty(LfRfidTuneView* tune_view); + +uint32_t lfrfid_debug_view_tune_get_arr(LfRfidTuneView* tune_view); + +uint32_t lfrfid_debug_view_tune_get_ccr(LfRfidTuneView* tune_view); diff --git a/applications/meta/application.fam b/applications/meta/application.fam index 5def77573b..48442da310 100644 --- a/applications/meta/application.fam +++ b/applications/meta/application.fam @@ -84,5 +84,6 @@ App( #"wav_player", "multi_converter", "flipfrid", + "subbrute", ], ) \ No newline at end of file diff --git a/applications/multi_converter/multi_converter.c b/applications/multi_converter/multi_converter.c index 69b3c71f69..5907303575 100644 --- a/applications/multi_converter/multi_converter.c +++ b/applications/multi_converter/multi_converter.c @@ -8,152 +8,158 @@ #include "multi_converter_mode_select.h" static void multi_converter_render_callback(Canvas* const canvas, void* ctx) { - - const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25); - if(multi_converter_state == NULL) { - return; - } - - if (multi_converter_state->mode == ModeDisplay) { - multi_converter_mode_display_draw(canvas, multi_converter_state); - } else { - multi_converter_mode_select_draw(canvas, multi_converter_state); - } - - release_mutex((ValueMutex*)ctx, multi_converter_state); + const MultiConverterState* multi_converter_state = acquire_mutex((ValueMutex*)ctx, 25); + if(multi_converter_state == NULL) { + return; + } + + if(multi_converter_state->mode == ModeDisplay) { + multi_converter_mode_display_draw(canvas, multi_converter_state); + } else { + multi_converter_mode_select_draw(canvas, multi_converter_state); + } + + release_mutex((ValueMutex*)ctx, multi_converter_state); } -static void multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { - furi_assert(event_queue); +static void + multi_converter_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); - MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event}; + MultiConverterEvent event = {.type = EventTypeKey, .input = *input_event}; furi_message_queue_put(event_queue, &event, FuriWaitForever); } static void multi_converter_init(MultiConverterState* const multi_converter_state) { - // initial default values + // initial default values - multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; - multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators + multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; + multi_converter_state->buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS] = '\0'; // null terminators - multi_converter_state->unit_type_orig = UnitTypeDec; - multi_converter_state->unit_type_dest = UnitTypeHex; + multi_converter_state->unit_type_orig = UnitTypeDec; + multi_converter_state->unit_type_dest = UnitTypeHex; - multi_converter_state->keyboard_lock = 0; + multi_converter_state->keyboard_lock = 0; - // init the display view - multi_converter_mode_display_reset(multi_converter_state); + // init the display view + multi_converter_mode_display_reset(multi_converter_state); - // init the select view - multi_converter_mode_select_reset(multi_converter_state); + // init the select view + multi_converter_mode_select_reset(multi_converter_state); - // set ModeDisplay as the current mode - multi_converter_state->mode = ModeDisplay; + // set ModeDisplay as the current mode + multi_converter_state->mode = ModeDisplay; } // main entry point int32_t multi_converter_app(void* p) { UNUSED(p); - // get event queue - FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent)); - - // allocate state - MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState)); - - // set mutex for plugin state (different threads can access it) - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) { - FURI_LOG_E("MultiConverter", "cannot create mutex\r\n"); - free(multi_converter_state); - return 255; - } - - // register callbacks for drawing and input processing - ViewPort* view_port = view_port_alloc(); - view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex); - view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue); - - // open GUI and register view_port - Gui* gui = furi_record_open("gui"); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - multi_converter_init(multi_converter_state); - - // main loop - MultiConverterEvent event; - for (bool processing = true; processing;) { - FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); - MultiConverterState* multi_converter_state = (MultiConverterState*)acquire_mutex_block(&state_mutex); - - if (event_status == FuriStatusOk) { - // press events - if (event.type == EventTypeKey && !multi_converter_state->keyboard_lock) { - if (multi_converter_state->mode == ModeDisplay) { - - if (event.input.key == InputKeyBack) { - if (event.input.type == InputTypePress) processing = false; - } else if (event.input.key == InputKeyOk) { // the "ok" press can be short or long - MultiConverterModeTrigger t = None; - - if (event.input.type == InputTypeLong) t = multi_converter_mode_display_ok(1, multi_converter_state); - else if (event.input.type == InputTypeShort) t = multi_converter_mode_display_ok(0, multi_converter_state); - - if (t == Reset) { - multi_converter_mode_select_reset(multi_converter_state); - multi_converter_state->mode = ModeSelector; - } - } else { - if (event.input.type == InputTypePress) multi_converter_mode_display_navigation(event.input.key, multi_converter_state); - } - - } else { // ModeSelect - if (event.input.type == InputTypePress) { - switch (event.input.key) { - default: - break; - case InputKeyBack: - case InputKeyOk: { - MultiConverterModeTrigger t = multi_converter_mode_select_exit(event.input.key == InputKeyOk ? 1 : 0, multi_converter_state); - - if (t == Reset) { - multi_converter_mode_display_reset(multi_converter_state); - } else if (t == Convert) { - multi_converter_mode_display_convert(multi_converter_state); - } - - multi_converter_state->keyboard_lock = 1; - multi_converter_state->mode = ModeDisplay; - break; - } - case InputKeyLeft: - case InputKeyRight: - multi_converter_mode_select_switch(multi_converter_state); - break; - case InputKeyUp: - multi_converter_mode_select_change_unit(-1, multi_converter_state); - break; - case InputKeyDown: - multi_converter_mode_select_change_unit(1, multi_converter_state); - break; - } - } - } - } else if (multi_converter_state->keyboard_lock) { - multi_converter_state->keyboard_lock = 0; - } - } else { - // event timeout - } - - view_port_update(view_port); - release_mutex(&state_mutex, multi_converter_state); - } - - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - furi_record_close("gui"); - view_port_free(view_port); + // get event queue + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(MultiConverterEvent)); + + // allocate state + MultiConverterState* multi_converter_state = malloc(sizeof(MultiConverterState)); + + // set mutex for plugin state (different threads can access it) + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, multi_converter_state, sizeof(multi_converter_state))) { + FURI_LOG_E("MultiConverter", "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + free(multi_converter_state); + return 255; + } + + // register callbacks for drawing and input processing + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, multi_converter_render_callback, &state_mutex); + view_port_input_callback_set(view_port, multi_converter_input_callback, event_queue); + + // open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + multi_converter_init(multi_converter_state); + + // main loop + MultiConverterEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + MultiConverterState* multi_converter_state = + (MultiConverterState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey && !multi_converter_state->keyboard_lock) { + if(multi_converter_state->mode == ModeDisplay) { + if(event.input.key == InputKeyBack) { + if(event.input.type == InputTypePress) processing = false; + } else if(event.input.key == InputKeyOk) { // the "ok" press can be short or long + MultiConverterModeTrigger t = None; + + if(event.input.type == InputTypeLong) + t = multi_converter_mode_display_ok(1, multi_converter_state); + else if(event.input.type == InputTypeShort) + t = multi_converter_mode_display_ok(0, multi_converter_state); + + if(t == Reset) { + multi_converter_mode_select_reset(multi_converter_state); + multi_converter_state->mode = ModeSelector; + } + } else { + if(event.input.type == InputTypePress) + multi_converter_mode_display_navigation( + event.input.key, multi_converter_state); + } + + } else { // ModeSelect + if(event.input.type == InputTypePress) { + switch(event.input.key) { + default: + break; + case InputKeyBack: + case InputKeyOk: { + MultiConverterModeTrigger t = multi_converter_mode_select_exit( + event.input.key == InputKeyOk ? 1 : 0, multi_converter_state); + + if(t == Reset) { + multi_converter_mode_display_reset(multi_converter_state); + } else if(t == Convert) { + multi_converter_mode_display_convert(multi_converter_state); + } + + multi_converter_state->keyboard_lock = 1; + multi_converter_state->mode = ModeDisplay; + break; + } + case InputKeyLeft: + case InputKeyRight: + multi_converter_mode_select_switch(multi_converter_state); + break; + case InputKeyUp: + multi_converter_mode_select_change_unit(-1, multi_converter_state); + break; + case InputKeyDown: + multi_converter_mode_select_change_unit(1, multi_converter_state); + break; + } + } + } + } else if(multi_converter_state->keyboard_lock) { + multi_converter_state->keyboard_lock = 0; + } + } else { + // event timeout + } + + view_port_update(view_port); + release_mutex(&state_mutex, multi_converter_state); + } + + 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); delete_mutex(&state_mutex); free(multi_converter_state); diff --git a/applications/multi_converter/multi_converter_definitions.h b/applications/multi_converter/multi_converter_definitions.h index fe904342d9..3bed192a09 100644 --- a/applications/multi_converter/multi_converter_definitions.h +++ b/applications/multi_converter/multi_converter_definitions.h @@ -1,82 +1,82 @@ #pragma once -#define MULTI_CONVERTER_NUMBER_DIGITS 9 +#define MULTI_CONVERTER_NUMBER_DIGITS 9 typedef enum { - EventTypeKey, + EventTypeKey, } EventType; typedef struct { - InputEvent input; - EventType type; + InputEvent input; + EventType type; } MultiConverterEvent; typedef enum { - ModeDisplay, - ModeSelector, + ModeDisplay, + ModeSelector, } MultiConverterMode; typedef enum { - None, - Reset, - Convert, + None, + Reset, + Convert, } MultiConverterModeTrigger; // new units goes here, used as index to the main multi_converter_available_units array (multi_converter_units.h) typedef enum { - UnitTypeDec, - UnitTypeHex, - UnitTypeBin, + UnitTypeDec, + UnitTypeHex, + UnitTypeBin, - UnitTypeCelsius, - UnitTypeFahernheit, - UnitTypeKelvin, + UnitTypeCelsius, + UnitTypeFahernheit, + UnitTypeKelvin, - UnitTypeKilometers, - UnitTypeMeters, - UnitTypeCentimeters, - UnitTypeMiles, - UnitTypeFeet, - UnitTypeInches, + UnitTypeKilometers, + UnitTypeMeters, + UnitTypeCentimeters, + UnitTypeMiles, + UnitTypeFeet, + UnitTypeInches, - UnitTypeDegree, - UnitTypeRadian, + UnitTypeDegree, + UnitTypeRadian, } MultiConverterUnitType; typedef struct { - MultiConverterUnitType selected_unit_type_orig; - MultiConverterUnitType selected_unit_type_dest; - uint8_t select_orig; + MultiConverterUnitType selected_unit_type_orig; + MultiConverterUnitType selected_unit_type_dest; + uint8_t select_orig; } MultiConverterModeSelect; typedef struct { - uint8_t cursor; // cursor position when typing - int8_t key; // hover key - uint8_t comma; // comma already added? (only one comma allowed) - uint8_t negative; // is negative? + uint8_t cursor; // cursor position when typing + int8_t key; // hover key + uint8_t comma; // comma already added? (only one comma allowed) + uint8_t negative; // is negative? } MultiConverterModeDisplay; typedef struct MultiConverterUnit MultiConverterUnit; typedef struct MultiConverterState MultiConverterState; struct MultiConverterUnit { - uint8_t allow_comma; - uint8_t allow_negative; - uint8_t max_number_keys; - char mini_name[4]; - char name[12]; - void (*convert_function)(MultiConverterState * const); - uint8_t (*allowed_function)(MultiConverterUnitType); + uint8_t allow_comma; + uint8_t allow_negative; + uint8_t max_number_keys; + char mini_name[4]; + char name[12]; + void (*convert_function)(MultiConverterState* const); + uint8_t (*allowed_function)(MultiConverterUnitType); }; struct MultiConverterState { - char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1]; - char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1]; - MultiConverterUnitType unit_type_orig; - MultiConverterUnitType unit_type_dest; - MultiConverterMode mode; - MultiConverterModeDisplay display; - MultiConverterModeSelect select; - uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes - // (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!) + char buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS + 1]; + char buffer_dest[MULTI_CONVERTER_NUMBER_DIGITS + 1]; + MultiConverterUnitType unit_type_orig; + MultiConverterUnitType unit_type_dest; + MultiConverterMode mode; + MultiConverterModeDisplay display; + MultiConverterModeSelect select; + uint8_t keyboard_lock; // used to create a small lock when switching from SELECT to DISPLAY modes + // (debouncing, basically; otherwise it switch modes twice 'cause it's too fast!) }; diff --git a/applications/multi_converter/multi_converter_mode_display.c b/applications/multi_converter/multi_converter_mode_display.c index 2462cf60c8..c72a954f70 100644 --- a/applications/multi_converter/multi_converter_mode_display.c +++ b/applications/multi_converter/multi_converter_mode_display.c @@ -1,284 +1,326 @@ #include "multi_converter_mode_display.h" -#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT] - -#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press -#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press -#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16 -#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17 +#define MULTI_CONVERTER_DISPLAY_KEYS 18 // [0] to [F] + [BACK] + [SELECT] -#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.' -#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-' -#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<' -#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#' -#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' ' +#define MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE 0 // long press +#define MULTI_CONVERTER_DISPLAY_KEY_COMMA 1 // long press +#define MULTI_CONVERTER_DISPLAY_KEY_DEL 16 +#define MULTI_CONVERTER_DISPLAY_KEY_SELECT 17 -#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3 -#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8 +#define MULTI_CONVERTER_DISPLAY_CHAR_COMMA '.' +#define MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE '-' +#define MULTI_CONVERTER_DISPLAY_CHAR_DEL '<' +#define MULTI_CONVERTER_DISPLAY_CHAR_SELECT '#' +#define MULTI_CONVERTER_DISPLAY_CHAR_BLANK ' ' +#define MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN 3 +#define MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT 8 void multi_converter_mode_display_convert(MultiConverterState* const multi_converter_state) { - - // 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...) - // just copy buffer_orig to buffer_dest and that's it - - if (multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) { - memcpy(multi_converter_state->buffer_dest, multi_converter_state->buffer_orig, MULTI_CONVERTER_NUMBER_DIGITS); - return; - } - - // 2.- origin_buffer has not null functions - if (multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL || multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL) return; - - // 3.- valid destination type (using allowed_destinations function) - if (!multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function(multi_converter_state->unit_type_dest)) return; - - multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function(multi_converter_state); - + // 1.- if origin == destination (in theory user won't be allowed to choose the same options, but it's kinda "valid"...) + // just copy buffer_orig to buffer_dest and that's it + + if(multi_converter_state->unit_type_orig == multi_converter_state->unit_type_dest) { + memcpy( + multi_converter_state->buffer_dest, + multi_converter_state->buffer_orig, + MULTI_CONVERTER_NUMBER_DIGITS); + return; + } + + // 2.- origin_buffer has not null functions + if(multi_converter_get_unit(multi_converter_state->unit_type_orig).convert_function == NULL || + multi_converter_get_unit(multi_converter_state->unit_type_orig).allowed_function == NULL) + return; + + // 3.- valid destination type (using allowed_destinations function) + if(!multi_converter_get_unit(multi_converter_state->unit_type_orig) + .allowed_function(multi_converter_state->unit_type_dest)) + return; + + multi_converter_get_unit(multi_converter_state->unit_type_orig) + .convert_function(multi_converter_state); } -void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) { - - canvas_set_color(canvas, ColorBlack); - - // ORIGIN - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig); - - // DESTINATION - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 10 + 12, multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name); - - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest); - - // SEPARATOR_LINE - canvas_draw_line(canvas, 2, 25, 128 - 3, 25); - - // KEYBOARD - uint8_t _x = 5; - uint8_t _y = 25 + 15; // line + 10 - - for (int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) { - - char g; - if (i < 10) g = (i + '0'); - else if (i < 16) g = ((i - 10) + 'A'); - else if (i == MULTI_CONVERTER_DISPLAY_KEY_DEL) g = MULTI_CONVERTER_DISPLAY_CHAR_DEL; - else g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT; - - uint8_t g_w = canvas_glyph_width(canvas, g); - - if (i < 16 && i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) { - // some units won't use the full [0] - [F] keyboard, in those situations just hide the char - // (won't be selectable anyway, so no worries here; this is just about drawing stuff) - g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; - } - - // currently hover key is highlighted - if ((multi_converter_state->display).key == i) { - canvas_draw_box(canvas, - _x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, - _y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN), - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, - MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 - ); - canvas_set_color(canvas, ColorWhite); - } else { - canvas_draw_frame(canvas, - _x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, - _y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN), - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, - MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 - ); - } - - // draw key - canvas_draw_glyph(canvas, _x, _y, g); - - // certain keys have long_press features, draw whatever they're using there too - if (i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) { - canvas_draw_box(canvas, - _x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4, - _y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2, - 4, - 2 - ); - } else if (i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) { - canvas_draw_box(canvas, - _x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2, - _y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2, - 2, - 2 - ); - } - - // back to black - canvas_set_color(canvas, ColorBlack); - - if (i < 8) { - _x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2; - } else if (i == 8) { - _y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) + 3; - _x = 8; // some padding at the beginning on second line - } else { - _x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1; - } - } - +void multi_converter_mode_display_draw( + Canvas* const canvas, + const MultiConverterState* multi_converter_state) { + canvas_set_color(canvas, ColorBlack); + + // ORIGIN + canvas_set_font(canvas, FontPrimary); + canvas_draw_str( + canvas, 2, 10, multi_converter_get_unit(multi_converter_state->unit_type_orig).mini_name); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2 + 30, 10, multi_converter_state->buffer_orig); + + // DESTINATION + canvas_set_font(canvas, FontPrimary); + canvas_draw_str( + canvas, + 2, + 10 + 12, + multi_converter_get_unit(multi_converter_state->unit_type_dest).mini_name); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2 + 30, 10 + 12, multi_converter_state->buffer_dest); + + // SEPARATOR_LINE + canvas_draw_line(canvas, 2, 25, 128 - 3, 25); + + // KEYBOARD + uint8_t _x = 5; + uint8_t _y = 25 + 15; // line + 10 + + for(int i = 0; i < MULTI_CONVERTER_DISPLAY_KEYS; i++) { + char g; + if(i < 10) + g = (i + '0'); + else if(i < 16) + g = ((i - 10) + 'A'); + else if(i == MULTI_CONVERTER_DISPLAY_KEY_DEL) + g = MULTI_CONVERTER_DISPLAY_CHAR_DEL; + else + g = MULTI_CONVERTER_DISPLAY_CHAR_SELECT; + + uint8_t g_w = canvas_glyph_width(canvas, g); + + if(i < 16 && + i > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys - + 1) { + // some units won't use the full [0] - [F] keyboard, in those situations just hide the char + // (won't be selectable anyway, so no worries here; this is just about drawing stuff) + g = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + } + + // currently hover key is highlighted + if((multi_converter_state->display).key == i) { + canvas_draw_box( + canvas, + _x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + _y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN), + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_draw_frame( + canvas, + _x - MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + _y - (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN), + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w + + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN, + MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2); + } + + // draw key + canvas_draw_glyph(canvas, _x, _y, g); + + // certain keys have long_press features, draw whatever they're using there too + if(i == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) { + canvas_draw_box( + canvas, + _x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 4, + _y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2, + 4, + 2); + } else if(i == MULTI_CONVERTER_DISPLAY_KEY_COMMA) { + canvas_draw_box( + canvas, + _x + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN + g_w - 2, + _y + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN - 2, + 2, + 2); + } + + // back to black + canvas_set_color(canvas, ColorBlack); + + if(i < 8) { + _x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 2; + } else if(i == 8) { + _y += (MULTI_CONVERTER_DISPLAY_KEY_CHAR_HEIGHT + + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2) + + 3; + _x = 8; // some padding at the beginning on second line + } else { + _x += g_w + MULTI_CONVERTER_DISPLAY_KEY_FRAME_MARGIN * 2 + 1; + } + } } -void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state) { - - // first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one" - switch (key) { - - default: - break; - - case InputKeyUp: - case InputKeyDown: - if ((multi_converter_state->display).key >= 9) (multi_converter_state->display).key -= 9; - else (multi_converter_state->display).key += 9; - break; - - case InputKeyLeft: - case InputKeyRight: - - (multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1); - - if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0; - else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1; - break; - } - - // if destination key is disabled by max_number_keys, move to the closest one - // (this could be improved with more accurate keys movements, probably...) - if (multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16) return; // weird, since this means "do not show any number on the keyboard, but just in case..." - - int8_t i = -1; - if (key == InputKeyRight || key == InputKeyDown) i = 1; - - while ((multi_converter_state->display).key < 16 && (multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) { - (multi_converter_state->display).key += i; - if ((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS-1) (multi_converter_state->display).key = 0; - else if ((multi_converter_state->display).key < 0) (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS-1; - } - +void multi_converter_mode_display_navigation( + InputKey key, + MultiConverterState* const multi_converter_state) { + // first move to keyboard position, then check if the ORIGIN allows that specific key, if not jump to the "closest one" + switch(key) { + default: + break; + + case InputKeyUp: + case InputKeyDown: + if((multi_converter_state->display).key >= 9) + (multi_converter_state->display).key -= 9; + else + (multi_converter_state->display).key += 9; + break; + + case InputKeyLeft: + case InputKeyRight: + + (multi_converter_state->display).key += (key == InputKeyLeft ? -1 : 1); + + if((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS - 1) + (multi_converter_state->display).key = 0; + else if((multi_converter_state->display).key < 0) + (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS - 1; + break; + } + + // if destination key is disabled by max_number_keys, move to the closest one + // (this could be improved with more accurate keys movements, probably...) + if(multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys >= 16) + return; // weird, since this means "do not show any number on the keyboard, but just in case..." + + int8_t i = -1; + if(key == InputKeyRight || key == InputKeyDown) i = 1; + + while((multi_converter_state->display).key < 16 && + (multi_converter_state->display).key > + multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys - + 1) { + (multi_converter_state->display).key += i; + if((multi_converter_state->display).key > MULTI_CONVERTER_DISPLAY_KEYS - 1) + (multi_converter_state->display).key = 0; + else if((multi_converter_state->display).key < 0) + (multi_converter_state->display).key = MULTI_CONVERTER_DISPLAY_KEYS - 1; + } } void multi_converter_mode_display_reset(MultiConverterState* const multi_converter_state) { - - // clean the buffers - for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) { - multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; - multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; - } - - // reset the display flags and index - multi_converter_state->display.cursor = 0; - multi_converter_state->display.key = 0; - multi_converter_state->display.comma = 0; - multi_converter_state->display.negative = 0; + // clean the buffers + for(int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS; i++) { + multi_converter_state->buffer_orig[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + multi_converter_state->buffer_dest[i] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + } + + // reset the display flags and index + multi_converter_state->display.cursor = 0; + multi_converter_state->display.key = 0; + multi_converter_state->display.comma = 0; + multi_converter_state->display.negative = 0; } -void multi_converter_mode_display_toggle_negative(MultiConverterState* const multi_converter_state) { - if (multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) { - - if (!(multi_converter_state->display).negative) { - // shift origin buffer one to right + add the "-" sign (last digit will be lost) - for (int i = MULTI_CONVERTER_NUMBER_DIGITS-1; i > 0; i--) { - // we could avoid the blanks, but nevermind - multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i-1]; - } - multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE; - - // only increment cursor if we're not out of bound - if ((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS) (multi_converter_state->display).cursor++; - } else { - // shift origin buffer one to left, append ' ' on the end - for (int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS-1; i++) { - if (multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK) break; - - multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i+1]; - } - multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS-1] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; - - (multi_converter_state->display).cursor--; - } - - // toggle flag - (multi_converter_state->display).negative ^= 1; - } +void multi_converter_mode_display_toggle_negative( + MultiConverterState* const multi_converter_state) { + if(multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_negative) { + if(!(multi_converter_state->display).negative) { + // shift origin buffer one to right + add the "-" sign (last digit will be lost) + for(int i = MULTI_CONVERTER_NUMBER_DIGITS - 1; i > 0; i--) { + // we could avoid the blanks, but nevermind + multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i - 1]; + } + multi_converter_state->buffer_orig[0] = MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE; + + // only increment cursor if we're not out of bound + if((multi_converter_state->display).cursor < MULTI_CONVERTER_NUMBER_DIGITS) + (multi_converter_state->display).cursor++; + } else { + // shift origin buffer one to left, append ' ' on the end + for(int i = 0; i < MULTI_CONVERTER_NUMBER_DIGITS - 1; i++) { + if(multi_converter_state->buffer_orig[i] == MULTI_CONVERTER_DISPLAY_CHAR_BLANK) + break; + + multi_converter_state->buffer_orig[i] = multi_converter_state->buffer_orig[i + 1]; + } + multi_converter_state->buffer_orig[MULTI_CONVERTER_NUMBER_DIGITS - 1] = + MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + + (multi_converter_state->display).cursor--; + } + + // toggle flag + (multi_converter_state->display).negative ^= 1; + } } void multi_converter_mode_display_add_comma(MultiConverterState* const multi_converter_state) { - if ( - !multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma || - (multi_converter_state->display).comma || - !(multi_converter_state->display).cursor || - ((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1)) - ) return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars - - // set flag to one - (multi_converter_state->display).comma = 1; - - multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_COMMA; - (multi_converter_state->display).cursor++; -} - -void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state) { - if ((multi_converter_state->display).key > multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys-1) return; + if(!multi_converter_get_unit(multi_converter_state->unit_type_orig).allow_comma || + (multi_converter_state->display).comma || !(multi_converter_state->display).cursor || + ((multi_converter_state->display).cursor == (MULTI_CONVERTER_NUMBER_DIGITS - 1))) + return; // maybe not allowerd; or one comma already in place; also cannot add commas as first or last chars - if ((multi_converter_state->display).key < 10) { - multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = (multi_converter_state->display).key + '0'; - } else { - multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = ((multi_converter_state->display).key - 10) + 'A'; - } + // set flag to one + (multi_converter_state->display).comma = 1; - (multi_converter_state->display).cursor++; + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = + MULTI_CONVERTER_DISPLAY_CHAR_COMMA; + (multi_converter_state->display).cursor++; } -MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state) { - - if ((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) { - if ((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS) return None; // limit reached, ignore - - // long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed - if (long_press) { - - if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) { - // toggle negative - multi_converter_mode_display_toggle_negative(multi_converter_state); - } else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) { - // add comma - multi_converter_mode_display_add_comma(multi_converter_state); - } - - } else { - // regular keys - multi_converter_mode_display_add_number(multi_converter_state); - } - - multi_converter_mode_display_convert(multi_converter_state); - - } else if ((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) { - if ((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--; - - if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_COMMA) (multi_converter_state->display).comma = 0; - if (multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE) (multi_converter_state->display).negative = 0; - - multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = MULTI_CONVERTER_DISPLAY_CHAR_BLANK; - - multi_converter_mode_display_convert(multi_converter_state); - - } else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT - return Reset; - } - - return None; +void multi_converter_mode_display_add_number(MultiConverterState* const multi_converter_state) { + if((multi_converter_state->display).key > + multi_converter_get_unit(multi_converter_state->unit_type_orig).max_number_keys - 1) + return; + + if((multi_converter_state->display).key < 10) { + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = + (multi_converter_state->display).key + '0'; + } else { + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = + ((multi_converter_state->display).key - 10) + 'A'; + } + + (multi_converter_state->display).cursor++; +} +MultiConverterModeTrigger multi_converter_mode_display_ok( + uint8_t long_press, + MultiConverterState* const multi_converter_state) { + if((multi_converter_state->display).key < MULTI_CONVERTER_DISPLAY_KEY_DEL) { + if((multi_converter_state->display).cursor >= MULTI_CONVERTER_NUMBER_DIGITS) + return None; // limit reached, ignore + + // long press on 0 toggle NEGATIVE if allowed, on 1 adds COMMA if allowed + if(long_press) { + if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_NEGATIVE) { + // toggle negative + multi_converter_mode_display_toggle_negative(multi_converter_state); + } else if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_COMMA) { + // add comma + multi_converter_mode_display_add_comma(multi_converter_state); + } + + } else { + // regular keys + multi_converter_mode_display_add_number(multi_converter_state); + } + + multi_converter_mode_display_convert(multi_converter_state); + + } else if((multi_converter_state->display).key == MULTI_CONVERTER_DISPLAY_KEY_DEL) { + if((multi_converter_state->display).cursor > 0) (multi_converter_state->display).cursor--; + + if(multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == + MULTI_CONVERTER_DISPLAY_CHAR_COMMA) + (multi_converter_state->display).comma = 0; + if(multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] == + MULTI_CONVERTER_DISPLAY_CHAR_NEGATIVE) + (multi_converter_state->display).negative = 0; + + multi_converter_state->buffer_orig[(multi_converter_state->display).cursor] = + MULTI_CONVERTER_DISPLAY_CHAR_BLANK; + + multi_converter_mode_display_convert(multi_converter_state); + + } else { // MULTI_CONVERTER_DISPLAY_KEY_SELECT + return Reset; + } + + return None; } \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_mode_display.h b/applications/multi_converter/multi_converter_mode_display.h index 7c23e230b7..cae929d354 100644 --- a/applications/multi_converter/multi_converter_mode_display.h +++ b/applications/multi_converter/multi_converter_mode_display.h @@ -16,12 +16,16 @@ void multi_converter_mode_display_convert(MultiConverterState* const multi_conve // // draw the main DISPLAY view with the current multi_converter_state values // -void multi_converter_mode_display_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state); +void multi_converter_mode_display_draw( + Canvas* const canvas, + const MultiConverterState* multi_converter_state); // // keyboard navigation on DISPLAY mode (NAVIGATION only, no BACK nor OK - InputKey guaranteed to be left/right/up/down) // -void multi_converter_mode_display_navigation(InputKey key, MultiConverterState* const multi_converter_state); +void multi_converter_mode_display_navigation( + InputKey key, + MultiConverterState* const multi_converter_state); // // reset the DISPLAY mode with the current units, cleaning the buffers and different flags; @@ -52,4 +56,6 @@ void multi_converter_mode_display_add_number(MultiConverterState* const multi_co // handle the OK action when selecting a specific key on the keyboard (add a number, a symbol, change mode...) // returns a ModeTrigger enum value: may or may not let to a mode change on the main loop (WON'T change the mode here) // -MultiConverterModeTrigger multi_converter_mode_display_ok(uint8_t long_press, MultiConverterState* const multi_converter_state); \ No newline at end of file +MultiConverterModeTrigger multi_converter_mode_display_ok( + uint8_t long_press, + MultiConverterState* const multi_converter_state); \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_mode_select.c b/applications/multi_converter/multi_converter_mode_select.c index c30a361e51..a56ccd58c1 100644 --- a/applications/multi_converter/multi_converter_mode_select.c +++ b/applications/multi_converter/multi_converter_mode_select.c @@ -1,160 +1,210 @@ #include "multi_converter_mode_select.h" -#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3 - -#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:" -#define MULTI_CONVERTER_INFO_STRING_TO "TO:" -#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change" -#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel" - -void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state) { - int i = 1; - while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...) - int ut = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_dest, i * d); - if ( - multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(ut) && - (multi_converter_state->select).selected_unit_type_orig != ut - ) { - canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name); - break; - } - i++; - } +#define MULTI_CONVERTER_LIST_ENTRIES_COUNT 3 + +#define MULTI_CONVERTER_INFO_STRING_FROM "FROM:" +#define MULTI_CONVERTER_INFO_STRING_TO "TO:" +#define MULTI_CONVERTER_INFO_STRING_OK "OK: Change" +#define MULTI_CONVERTER_INFO_STRING_BACK "BACK: Cancel" + +void multi_converter_mode_select_draw_destination_offset( + uint8_t x, + uint8_t y, + int8_t d, + Canvas* const canvas, + const MultiConverterState* multi_converter_state) { + int i = 1; + while( + i < + MULTI_CONVERTER_AVAILABLE_UNITS) { // in case there's no match, to avoid an endless loop (in theory shouldn't happen, but...) + int ut = multi_converter_get_unit_type_offset( + (multi_converter_state->select).selected_unit_type_dest, i * d); + if(multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig] + .allowed_function(ut) && + (multi_converter_state->select).selected_unit_type_orig != ut) { + canvas_draw_str(canvas, x, y, multi_converter_available_units[ut].name); + break; + } + i++; + } } -void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas) { - canvas_draw_box(canvas, x - 2 , y - 10, canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4, 13); - canvas_set_color(canvas, ColorWhite); - canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name); - canvas_set_color(canvas, ColorBlack); +void multi_converter_mode_select_draw_selected_unit( + uint8_t x, + uint8_t y, + MultiConverterUnitType unit_type, + Canvas* const canvas) { + canvas_draw_box( + canvas, + x - 2, + y - 10, + canvas_string_width(canvas, multi_converter_available_units[unit_type].name) + 4, + 13); + canvas_set_color(canvas, ColorWhite); + canvas_draw_str(canvas, x, y, multi_converter_available_units[unit_type].name); + canvas_set_color(canvas, ColorBlack); } -void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state) { +void multi_converter_mode_select_draw( + Canvas* const canvas, + const MultiConverterState* multi_converter_state) { + int y = 10; + int x = 10; - int y = 10; - int x = 10; + canvas_set_color(canvas, ColorBlack); - canvas_set_color(canvas, ColorBlack); + // FROM + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM); - // FROM - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_FROM); + canvas_set_font(canvas, FontSecondary); - canvas_set_font(canvas, FontSecondary); + // offset -1 + y += 12; - // offset -1 - y += 12; + canvas_draw_str( + canvas, + x, + y, + multi_converter_available_units[multi_converter_get_unit_type_offset( + (multi_converter_state->select).selected_unit_type_orig, + -1)] + .name); - canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, -1) ].name); + // current selected element + y += 12; - // current selected element - y += 12; + multi_converter_mode_select_draw_selected_unit( + x, y, (multi_converter_state->select).selected_unit_type_orig, canvas); - multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_orig, canvas); + if((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">"); - if ((multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">"); + // offset +1 + y += 12; - // offset +1 - y += 12; + canvas_draw_str( + canvas, + x, + y, + multi_converter_available_units[multi_converter_get_unit_type_offset( + (multi_converter_state->select).selected_unit_type_orig, + 1)] + .name); - canvas_draw_str(canvas, x, y, multi_converter_available_units[ multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, 1) ].name); + // TO + y = 10; + x = 70; - // TO - y = 10; - x = 70; + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, x, y, MULTI_CONVERTER_INFO_STRING_TO); - - canvas_set_font(canvas, FontSecondary); + canvas_set_font(canvas, FontSecondary); - // offset -1: go back from current selected destination and find the first one valid (even if it's itself) - y += 12; + // offset -1: go back from current selected destination and find the first one valid (even if it's itself) + y += 12; - multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state); + multi_converter_mode_select_draw_destination_offset(x, y, -1, canvas, multi_converter_state); - // current selected element - y += 12; + // current selected element + y += 12; - multi_converter_mode_select_draw_selected_unit(x, y, (multi_converter_state->select).selected_unit_type_dest, canvas); + multi_converter_mode_select_draw_selected_unit( + x, y, (multi_converter_state->select).selected_unit_type_dest, canvas); - if (!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">"); + if(!(multi_converter_state->select).select_orig) canvas_draw_str(canvas, x - 6, y, ">"); - // offset +1: same but on the opposite direction - y += 12; + // offset +1: same but on the opposite direction + y += 12; - multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state); + multi_converter_mode_select_draw_destination_offset(x, y, 1, canvas, multi_converter_state); - // OK / CANCEL + // OK / CANCEL - canvas_set_color(canvas, ColorBlack); - canvas_draw_box(canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12); - canvas_draw_box(canvas, 128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 12, canvas_string_width(canvas, "BACK: Cancel") + 4, 12); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, 0, 64 - 12, canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_OK) + 4, 12); + canvas_draw_box( + canvas, + 128 - 4 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), + 64 - 12, + canvas_string_width(canvas, "BACK: Cancel") + 4, + 12); - canvas_set_color(canvas, ColorWhite); - canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK); - canvas_draw_str(canvas, 128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), 64 - 3, MULTI_CONVERTER_INFO_STRING_BACK); + canvas_set_color(canvas, ColorWhite); + canvas_draw_str(canvas, 2, 64 - 3, MULTI_CONVERTER_INFO_STRING_OK); + canvas_draw_str( + canvas, + 128 - 2 - canvas_string_width(canvas, MULTI_CONVERTER_INFO_STRING_BACK), + 64 - 3, + MULTI_CONVERTER_INFO_STRING_BACK); } void multi_converter_mode_select_reset(MultiConverterState* const multi_converter_state) { + // initial pre-selected values are equal to the current selected values + (multi_converter_state->select).selected_unit_type_orig = + multi_converter_state->unit_type_orig; + (multi_converter_state->select).selected_unit_type_dest = + multi_converter_state->unit_type_dest; - // initial pre-selected values are equal to the current selected values - (multi_converter_state->select).selected_unit_type_orig = multi_converter_state->unit_type_orig; - (multi_converter_state->select).selected_unit_type_dest = multi_converter_state->unit_type_dest; - - (multi_converter_state->select).select_orig = 1; + (multi_converter_state->select).select_orig = 1; } -MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state) { - if (save_changes) { - - multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest; - - if (multi_converter_state->unit_type_orig == (multi_converter_state->select).selected_unit_type_orig) { - // if the ORIGIN unit didn't changed, just trigger the convert - - return Convert; - } else { - multi_converter_state->unit_type_orig = (multi_converter_state->select).selected_unit_type_orig; - multi_converter_state->unit_type_dest = (multi_converter_state->select).selected_unit_type_dest; - - return Reset; - } - - } - - return None; +MultiConverterModeTrigger multi_converter_mode_select_exit( + uint8_t save_changes, + MultiConverterState* const multi_converter_state) { + if(save_changes) { + multi_converter_state->unit_type_dest = + (multi_converter_state->select).selected_unit_type_dest; + + if(multi_converter_state->unit_type_orig == + (multi_converter_state->select).selected_unit_type_orig) { + // if the ORIGIN unit didn't changed, just trigger the convert + + return Convert; + } else { + multi_converter_state->unit_type_orig = + (multi_converter_state->select).selected_unit_type_orig; + multi_converter_state->unit_type_dest = + (multi_converter_state->select).selected_unit_type_dest; + + return Reset; + } + } + + return None; } void multi_converter_mode_select_switch(MultiConverterState* const multi_converter_state) { - (multi_converter_state->select).select_orig ^= 1; + (multi_converter_state->select).select_orig ^= 1; } -void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state) { - - MultiConverterUnitType d; - if ((multi_converter_state->select).select_orig) { - (multi_converter_state->select).selected_unit_type_orig = multi_converter_get_unit_type_offset((multi_converter_state->select).selected_unit_type_orig, direction); - d = (multi_converter_state->select).selected_unit_type_dest; - } else { - d = ((multi_converter_state->select).selected_unit_type_dest + direction) % MULTI_CONVERTER_AVAILABLE_UNITS; - } - - // check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION - // (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME); - // also notice that ORIGIN must be DIFFERENT than DESTINATION - int i = 0; - while (i < MULTI_CONVERTER_AVAILABLE_UNITS) { - if ( - multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig].allowed_function(d) && - (multi_converter_state->select).selected_unit_type_orig != d - ) { - (multi_converter_state->select).selected_unit_type_dest = d; - break; - } - - d = multi_converter_get_unit_type_offset(d, direction); - i++; - } - +void multi_converter_mode_select_change_unit( + int8_t direction, + MultiConverterState* const multi_converter_state) { + MultiConverterUnitType d; + if((multi_converter_state->select).select_orig) { + (multi_converter_state->select).selected_unit_type_orig = + multi_converter_get_unit_type_offset( + (multi_converter_state->select).selected_unit_type_orig, direction); + d = (multi_converter_state->select).selected_unit_type_dest; + } else { + d = ((multi_converter_state->select).selected_unit_type_dest + direction) % + MULTI_CONVERTER_AVAILABLE_UNITS; + } + + // check each unit with the ORIGIN allowed_function() to make sure we're selecting a valid DESTINATION + // (when changing the ORIGIN unit the DIRECTION in which we'll switch the DESTINATION will be the SAME); + // also notice that ORIGIN must be DIFFERENT than DESTINATION + int i = 0; + while(i < MULTI_CONVERTER_AVAILABLE_UNITS) { + if(multi_converter_available_units[(multi_converter_state->select).selected_unit_type_orig] + .allowed_function(d) && + (multi_converter_state->select).selected_unit_type_orig != d) { + (multi_converter_state->select).selected_unit_type_dest = d; + break; + } + + d = multi_converter_get_unit_type_offset(d, direction); + i++; + } } diff --git a/applications/multi_converter/multi_converter_mode_select.h b/applications/multi_converter/multi_converter_mode_select.h index 2e4a9ec855..c10ab8e01d 100644 --- a/applications/multi_converter/multi_converter_mode_select.h +++ b/applications/multi_converter/multi_converter_mode_select.h @@ -10,14 +10,25 @@ // // aux draw function for units offsets and draw stuff // -void multi_converter_mode_select_draw_destination_offset(uint8_t x, uint8_t y, int8_t d, Canvas* const canvas, const MultiConverterState* multi_converter_state); +void multi_converter_mode_select_draw_destination_offset( + uint8_t x, + uint8_t y, + int8_t d, + Canvas* const canvas, + const MultiConverterState* multi_converter_state); -void multi_converter_mode_select_draw_selected_unit(uint8_t x, uint8_t y, MultiConverterUnitType unit_type, Canvas* const canvas); +void multi_converter_mode_select_draw_selected_unit( + uint8_t x, + uint8_t y, + MultiConverterUnitType unit_type, + Canvas* const canvas); // // draw the main SELECT view with the current multi_converter_state values // -void multi_converter_mode_select_draw(Canvas* const canvas, const MultiConverterState* multi_converter_state); +void multi_converter_mode_select_draw( + Canvas* const canvas, + const MultiConverterState* multi_converter_state); // // reset the SELECT mode view, showing as "pre-selected" the current working units @@ -33,11 +44,13 @@ void multi_converter_mode_select_reset(MultiConverterState* const multi_converte // prevent weird behaviours, so for now we're trusting the selected_unit_orig/dest_type values) // // returns an enum code MultiConverterDisplayTrigger based on doing nothing (cancel), triggering the display -// convert method or reseting the whole display mode (when fully changing the units) +// convert method or reseting the whole display mode (when fully changing the units) // // notice the MODE CHANGE itself is not done here but in the main loop (outside the call) via the ModeTrigger enum element // -MultiConverterModeTrigger multi_converter_mode_select_exit(uint8_t save_changes, MultiConverterState* const multi_converter_state); +MultiConverterModeTrigger multi_converter_mode_select_exit( + uint8_t save_changes, + MultiConverterState* const multi_converter_state); // // switch between selecting the ORIGIN or the DESTINATION unit on DISPLAY mode (since there're only @@ -48,11 +61,13 @@ void multi_converter_mode_select_switch(MultiConverterState* const multi_convert // // change the selected unit on SELECTED mode, using the select_orig flag to check if we're switching the // ORIGIN or the DESTINATION unit; the DIRECTION (up or down to travel the array) is set as a param -// +// // when switching the ORIGIN one, reset the DESTINATION to the first valid unit (if the current one is not // valid anymore); when switching the DESTINATION one, an allowed_function() check is performed in order to // properly set a valid destination unit. // // (notice the draw step also perform which units are valid to display, so no worries about that here) // -void multi_converter_mode_select_change_unit(int8_t direction, MultiConverterState* const multi_converter_state); +void multi_converter_mode_select_change_unit( + int8_t direction, + MultiConverterState* const multi_converter_state); diff --git a/applications/multi_converter/multi_converter_units.c b/applications/multi_converter/multi_converter_units.c index 2812849231..4381f0e6e0 100644 --- a/applications/multi_converter/multi_converter_units.c +++ b/applications/multi_converter/multi_converter_units.c @@ -1,124 +1,126 @@ #include "multi_converter_units.h" -#define MULTI_CONVERTER_CHAR_OVERFLOW '#' -#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999 +#define MULTI_CONVERTER_CHAR_OVERFLOW '#' +#define MULTI_CONVERTER_MAX_SUPORTED_INT 999999999 -#define multi_converter_unit_set_overflow(b) for (int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW; +#define multi_converter_unit_set_overflow(b) \ + for(int _i = 0; _i < MULTI_CONVERTER_NUMBER_DIGITS; _i++) \ + b[_i] = MULTI_CONVERTER_CHAR_OVERFLOW; // // DEC / HEX / BIN conversion -// +// void multi_converter_unit_dec_hex_bin_convert(MultiConverterState* const multi_converter_state) { - - char dest[MULTI_CONVERTER_NUMBER_DIGITS]; - - int i = 0; - uint8_t overflow = 0; - - int a = 0; - int r = 0; - uint8_t f = 1; - - switch(multi_converter_state->unit_type_orig) { - default: - break; - case UnitTypeDec: { - a = atoi(multi_converter_state->buffer_orig); - f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2); - - break; - } - case UnitTypeHex: - a = strtol(multi_converter_state->buffer_orig, NULL, 16); - f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2); - - break; - case UnitTypeBin: - a = strtol(multi_converter_state->buffer_orig, NULL, 2); - f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16); - - break; - } - - while (a > 0) { - r = a % f; - dest[i] = r + (r < 10 ? '0' : ('A' - 10) ); - a /= f; - if (i++ >= MULTI_CONVERTER_NUMBER_DIGITS) { - overflow = 1; - break; - } - } - - if (overflow) { - multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); - } else { - // copy DEST (reversed) to destination and append empty chars at the end - for (int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) { - if (i >= 1) multi_converter_state->buffer_dest[j] = dest[--i]; - else multi_converter_state->buffer_dest[j] = ' '; - } - } - + char dest[MULTI_CONVERTER_NUMBER_DIGITS]; + + int i = 0; + uint8_t overflow = 0; + + int a = 0; + int r = 0; + uint8_t f = 1; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeDec: { + a = atoi(multi_converter_state->buffer_orig); + f = (multi_converter_state->unit_type_dest == UnitTypeHex ? 16 : 2); + + break; + } + case UnitTypeHex: + a = strtol(multi_converter_state->buffer_orig, NULL, 16); + f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 2); + + break; + case UnitTypeBin: + a = strtol(multi_converter_state->buffer_orig, NULL, 2); + f = (multi_converter_state->unit_type_dest == UnitTypeDec ? 10 : 16); + + break; + } + + while(a > 0) { + r = a % f; + dest[i] = r + (r < 10 ? '0' : ('A' - 10)); + a /= f; + if(i++ >= MULTI_CONVERTER_NUMBER_DIGITS) { + overflow = 1; + break; + } + } + + if(overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + // copy DEST (reversed) to destination and append empty chars at the end + for(int j = 0; j < MULTI_CONVERTER_NUMBER_DIGITS; j++) { + if(i >= 1) + multi_converter_state->buffer_dest[j] = dest[--i]; + else + multi_converter_state->buffer_dest[j] = ' '; + } + } } uint8_t multi_converter_unit_dec_hex_bin_allowed(MultiConverterUnitType unit_type) { - return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin); + return (unit_type == UnitTypeDec || unit_type == UnitTypeHex || unit_type == UnitTypeBin); } // // CEL / FAR / KEL // void multi_converter_unit_temperature_convert(MultiConverterState* const multi_converter_state) { - - double a = strtof(multi_converter_state->buffer_orig, NULL); - uint8_t overflow = 0; - - switch(multi_converter_state->unit_type_orig) { - default: - break; - case UnitTypeCelsius: - if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) { - // celsius to fahrenheit - a = (a * ((double) 1.8)) + 32; - } else { // UnitTypeKelvin - a += ((double) 273.15); - } - - break; - case UnitTypeFahernheit: - // fahrenheit to celsius, always - a = (a - 32) / ((double) 1.8); - if (multi_converter_state->unit_type_dest == UnitTypeKelvin) { - // if kelvin, add - a += ((double) 273.15); - } - - break; - case UnitTypeKelvin: - // kelvin to celsius, always - a -= ((double) 273.15); - if (multi_converter_state->unit_type_dest == UnitTypeFahernheit) { - // if fahernheit, convert - a = (a * ((double) 1.8)) + 32; - } - - break; - } - - if (overflow) { - multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); - } else { - - int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a); - - if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); - } - + double a = strtof(multi_converter_state->buffer_orig, NULL); + uint8_t overflow = 0; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeCelsius: + if(multi_converter_state->unit_type_dest == UnitTypeFahernheit) { + // celsius to fahrenheit + a = (a * ((double)1.8)) + 32; + } else { // UnitTypeKelvin + a += ((double)273.15); + } + + break; + case UnitTypeFahernheit: + // fahrenheit to celsius, always + a = (a - 32) / ((double)1.8); + if(multi_converter_state->unit_type_dest == UnitTypeKelvin) { + // if kelvin, add + a += ((double)273.15); + } + + break; + case UnitTypeKelvin: + // kelvin to celsius, always + a -= ((double)273.15); + if(multi_converter_state->unit_type_dest == UnitTypeFahernheit) { + // if fahernheit, convert + a = (a * ((double)1.8)) + 32; + } + + break; + } + + if(overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + int ret = snprintf( + multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%.3lf", a); + + if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } } uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_type) { - return (unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit || unit_type == UnitTypeKelvin); + return ( + unit_type == UnitTypeCelsius || unit_type == UnitTypeFahernheit || + unit_type == UnitTypeKelvin); } // @@ -126,73 +128,102 @@ uint8_t multi_converter_unit_temperature_allowed(MultiConverterUnitType unit_typ // void multi_converter_unit_distance_convert(MultiConverterState* const multi_converter_state) { - double a = strtof(multi_converter_state->buffer_orig, NULL); - uint8_t overflow = 0; - - switch(multi_converter_state->unit_type_orig) { - default: - break; - case UnitTypeKilometers: - if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1000); - else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100000); - else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.6213711); - else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3280.839895013); - else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39370.078740157); - break; - case UnitTypeMeters: - if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 1000); - else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 100); - else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0006213711); - else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 3.280839895013); - else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 39.370078740157); - break; - case UnitTypeCentimeters: - if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a /= ((double) 100000); - else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a /= ((double) 100); - else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000006213711); - else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.03280839895013); - else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 0.39370078740157); - break; - - case UnitTypeMiles: - if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 1.609344); - else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 1609.344); - else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 160934.4); - else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 5280); - else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 63360); - break; - case UnitTypeFeet: - if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0003048); - else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.3048); - else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 30.48); - else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.000189393939394); - else if (multi_converter_state->unit_type_dest == UnitTypeInches) a *= ((double) 12); - break; - case UnitTypeInches: - if (multi_converter_state->unit_type_dest == UnitTypeKilometers) a *= ((double) 0.0000254); - else if (multi_converter_state->unit_type_dest == UnitTypeMeters) a *= ((double) 0.0254); - else if (multi_converter_state->unit_type_dest == UnitTypeCentimeters) a *= ((double) 2.54); - else if (multi_converter_state->unit_type_dest == UnitTypeMiles) a *= ((double) 0.0000157828282828); - else if (multi_converter_state->unit_type_dest == UnitTypeFeet) a *= ((double) 0.0833333333333); - break; - - } - - if (overflow) { - multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); - } else { - - int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a); - - if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); - } + double a = strtof(multi_converter_state->buffer_orig, NULL); + uint8_t overflow = 0; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeKilometers: + if(multi_converter_state->unit_type_dest == UnitTypeMeters) + a *= ((double)1000); + else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters) + a *= ((double)100000); + else if(multi_converter_state->unit_type_dest == UnitTypeMiles) + a *= ((double)0.6213711); + else if(multi_converter_state->unit_type_dest == UnitTypeFeet) + a *= ((double)3280.839895013); + else if(multi_converter_state->unit_type_dest == UnitTypeInches) + a *= ((double)39370.078740157); + break; + case UnitTypeMeters: + if(multi_converter_state->unit_type_dest == UnitTypeKilometers) + a /= ((double)1000); + else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters) + a *= ((double)100); + else if(multi_converter_state->unit_type_dest == UnitTypeMiles) + a *= ((double)0.0006213711); + else if(multi_converter_state->unit_type_dest == UnitTypeFeet) + a *= ((double)3.280839895013); + else if(multi_converter_state->unit_type_dest == UnitTypeInches) + a *= ((double)39.370078740157); + break; + case UnitTypeCentimeters: + if(multi_converter_state->unit_type_dest == UnitTypeKilometers) + a /= ((double)100000); + else if(multi_converter_state->unit_type_dest == UnitTypeMeters) + a /= ((double)100); + else if(multi_converter_state->unit_type_dest == UnitTypeMiles) + a *= ((double)0.000006213711); + else if(multi_converter_state->unit_type_dest == UnitTypeFeet) + a *= ((double)0.03280839895013); + else if(multi_converter_state->unit_type_dest == UnitTypeInches) + a *= ((double)0.39370078740157); + break; + + case UnitTypeMiles: + if(multi_converter_state->unit_type_dest == UnitTypeKilometers) + a *= ((double)1.609344); + else if(multi_converter_state->unit_type_dest == UnitTypeMeters) + a *= ((double)1609.344); + else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters) + a *= ((double)160934.4); + else if(multi_converter_state->unit_type_dest == UnitTypeFeet) + a *= ((double)5280); + else if(multi_converter_state->unit_type_dest == UnitTypeInches) + a *= ((double)63360); + break; + case UnitTypeFeet: + if(multi_converter_state->unit_type_dest == UnitTypeKilometers) + a *= ((double)0.0003048); + else if(multi_converter_state->unit_type_dest == UnitTypeMeters) + a *= ((double)0.3048); + else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters) + a *= ((double)30.48); + else if(multi_converter_state->unit_type_dest == UnitTypeMiles) + a *= ((double)0.000189393939394); + else if(multi_converter_state->unit_type_dest == UnitTypeInches) + a *= ((double)12); + break; + case UnitTypeInches: + if(multi_converter_state->unit_type_dest == UnitTypeKilometers) + a *= ((double)0.0000254); + else if(multi_converter_state->unit_type_dest == UnitTypeMeters) + a *= ((double)0.0254); + else if(multi_converter_state->unit_type_dest == UnitTypeCentimeters) + a *= ((double)2.54); + else if(multi_converter_state->unit_type_dest == UnitTypeMiles) + a *= ((double)0.0000157828282828); + else if(multi_converter_state->unit_type_dest == UnitTypeFeet) + a *= ((double)0.0833333333333); + break; + } + + if(overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + int ret = snprintf( + multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a); + + if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } } uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type) { - return ( - unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters || unit_type == UnitTypeCentimeters || - unit_type == UnitTypeMiles || unit_type == UnitTypeFeet || unit_type == UnitTypeInches - ); + return ( + unit_type == UnitTypeKilometers || unit_type == UnitTypeMeters || + unit_type == UnitTypeCentimeters || unit_type == UnitTypeMiles || + unit_type == UnitTypeFeet || unit_type == UnitTypeInches); } // @@ -200,31 +231,31 @@ uint8_t multi_converter_unit_distance_allowed(MultiConverterUnitType unit_type) // void multi_converter_unit_angle_convert(MultiConverterState* const multi_converter_state) { - double a = strtof(multi_converter_state->buffer_orig, NULL); - uint8_t overflow = 0; - - switch(multi_converter_state->unit_type_orig) { - default: - break; - case UnitTypeDegree: - if (multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double) 0.0174532925199); - break; - - case UnitTypeRadian: - if (multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double) 57.2957795131); - break; - } - - if (overflow) { - multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); - } else { - - int ret = snprintf(multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a); - - if (ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); - } + double a = strtof(multi_converter_state->buffer_orig, NULL); + uint8_t overflow = 0; + + switch(multi_converter_state->unit_type_orig) { + default: + break; + case UnitTypeDegree: + if(multi_converter_state->unit_type_dest == UnitTypeRadian) a *= ((double)0.0174532925199); + break; + + case UnitTypeRadian: + if(multi_converter_state->unit_type_dest == UnitTypeDegree) a *= ((double)57.2957795131); + break; + } + + if(overflow) { + multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } else { + int ret = snprintf( + multi_converter_state->buffer_dest, MULTI_CONVERTER_NUMBER_DIGITS + 1, "%lf", a); + + if(ret < 0) multi_converter_unit_set_overflow(multi_converter_state->buffer_dest); + } } uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type) { - return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian); + return (unit_type == UnitTypeDegree || unit_type == UnitTypeRadian); } \ No newline at end of file diff --git a/applications/multi_converter/multi_converter_units.h b/applications/multi_converter/multi_converter_units.h index da281627eb..6d3b477bb0 100644 --- a/applications/multi_converter/multi_converter_units.h +++ b/applications/multi_converter/multi_converter_units.h @@ -8,7 +8,9 @@ #define MULTI_CONVERTER_AVAILABLE_UNITS 14 #define multi_converter_get_unit(unit_type) multi_converter_available_units[unit_type] -#define multi_converter_get_unit_type_offset(unit_type, offset) (((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % MULTI_CONVERTER_AVAILABLE_UNITS) +#define multi_converter_get_unit_type_offset(unit_type, offset) \ + (((unit_type + offset) % MULTI_CONVERTER_AVAILABLE_UNITS + MULTI_CONVERTER_AVAILABLE_UNITS) % \ + MULTI_CONVERTER_AVAILABLE_UNITS) // the modulo operation will fail with extremely large values on the units array // DEC / HEX / BIN @@ -31,41 +33,139 @@ uint8_t multi_converter_unit_angle_allowed(MultiConverterUnitType unit_type); // each unit is made of comma? + negative? + keyboard_length + mini_name + name + convert function + allowed function // (setting functions as NULL will cause convert / select options to be ignored) // -static const MultiConverterUnit multi_converter_unit_dec = { 0, 0, 10, "DEC\0", "Decimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed }; -static const MultiConverterUnit multi_converter_unit_hex = { 0, 0, 16, "HEX\0", "Hexadecimal\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed }; -static const MultiConverterUnit multi_converter_unit_bin = { 0, 0, 2, "BIN\0", "Binary\0", multi_converter_unit_dec_hex_bin_convert, multi_converter_unit_dec_hex_bin_allowed }; - -static const MultiConverterUnit multi_converter_unit_cel = { 1, 1, 10, "CEL\0", "Celsius\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed }; -static const MultiConverterUnit multi_converter_unit_far = { 1, 1, 10, "FAR\0", "Fahernheit\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed }; -static const MultiConverterUnit multi_converter_unit_kel = { 1, 1, 10, "KEL\0", "Kelvin\0", multi_converter_unit_temperature_convert, multi_converter_unit_temperature_allowed }; - -static const MultiConverterUnit multi_converter_unit_km = { 1, 0, 10, "KM\0", "Kilometers\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; -static const MultiConverterUnit multi_converter_unit_m = { 1, 0, 10, "M\0", "Meters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; -static const MultiConverterUnit multi_converter_unit_cm = { 1, 0, 10, "CM\0", "Centimeters\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; -static const MultiConverterUnit multi_converter_unit_mi = { 1, 0, 10, "MI\0", "Miles\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; -static const MultiConverterUnit multi_converter_unit_ft = { 1, 0, 10, "FT\0", "Feet\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; -static const MultiConverterUnit multi_converter_unit_in = { 1, 0, 10, " \"\0", "Inches\0", multi_converter_unit_distance_convert, multi_converter_unit_distance_allowed }; - -static const MultiConverterUnit multi_converter_unit_deg = { 1, 0, 10, "DEG\0", "Degree\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed }; -static const MultiConverterUnit multi_converter_unit_rad = { 1, 0, 10, "RAD\0", "Radian\0", multi_converter_unit_angle_convert, multi_converter_unit_angle_allowed }; +static const MultiConverterUnit multi_converter_unit_dec = { + 0, + 0, + 10, + "DEC\0", + "Decimal\0", + multi_converter_unit_dec_hex_bin_convert, + multi_converter_unit_dec_hex_bin_allowed}; +static const MultiConverterUnit multi_converter_unit_hex = { + 0, + 0, + 16, + "HEX\0", + "Hexadecimal\0", + multi_converter_unit_dec_hex_bin_convert, + multi_converter_unit_dec_hex_bin_allowed}; +static const MultiConverterUnit multi_converter_unit_bin = { + 0, + 0, + 2, + "BIN\0", + "Binary\0", + multi_converter_unit_dec_hex_bin_convert, + multi_converter_unit_dec_hex_bin_allowed}; + +static const MultiConverterUnit multi_converter_unit_cel = { + 1, + 1, + 10, + "CEL\0", + "Celsius\0", + multi_converter_unit_temperature_convert, + multi_converter_unit_temperature_allowed}; +static const MultiConverterUnit multi_converter_unit_far = { + 1, + 1, + 10, + "FAR\0", + "Fahernheit\0", + multi_converter_unit_temperature_convert, + multi_converter_unit_temperature_allowed}; +static const MultiConverterUnit multi_converter_unit_kel = { + 1, + 1, + 10, + "KEL\0", + "Kelvin\0", + multi_converter_unit_temperature_convert, + multi_converter_unit_temperature_allowed}; + +static const MultiConverterUnit multi_converter_unit_km = { + 1, + 0, + 10, + "KM\0", + "Kilometers\0", + multi_converter_unit_distance_convert, + multi_converter_unit_distance_allowed}; +static const MultiConverterUnit multi_converter_unit_m = { + 1, + 0, + 10, + "M\0", + "Meters\0", + multi_converter_unit_distance_convert, + multi_converter_unit_distance_allowed}; +static const MultiConverterUnit multi_converter_unit_cm = { + 1, + 0, + 10, + "CM\0", + "Centimeters\0", + multi_converter_unit_distance_convert, + multi_converter_unit_distance_allowed}; +static const MultiConverterUnit multi_converter_unit_mi = { + 1, + 0, + 10, + "MI\0", + "Miles\0", + multi_converter_unit_distance_convert, + multi_converter_unit_distance_allowed}; +static const MultiConverterUnit multi_converter_unit_ft = { + 1, + 0, + 10, + "FT\0", + "Feet\0", + multi_converter_unit_distance_convert, + multi_converter_unit_distance_allowed}; +static const MultiConverterUnit multi_converter_unit_in = { + 1, + 0, + 10, + " \"\0", + "Inches\0", + multi_converter_unit_distance_convert, + multi_converter_unit_distance_allowed}; + +static const MultiConverterUnit multi_converter_unit_deg = { + 1, + 0, + 10, + "DEG\0", + "Degree\0", + multi_converter_unit_angle_convert, + multi_converter_unit_angle_allowed}; +static const MultiConverterUnit multi_converter_unit_rad = { + 1, + 0, + 10, + "RAD\0", + "Radian\0", + multi_converter_unit_angle_convert, + multi_converter_unit_angle_allowed}; // index order set by the MultiConverterUnitType enum element (multi_converter_definitions.h) static const MultiConverterUnit multi_converter_available_units[MULTI_CONVERTER_AVAILABLE_UNITS] = { - [UnitTypeDec] = multi_converter_unit_dec, - [UnitTypeHex] = multi_converter_unit_hex, - [UnitTypeBin] = multi_converter_unit_bin, - - [UnitTypeCelsius] = multi_converter_unit_cel, - [UnitTypeFahernheit] = multi_converter_unit_far, - [UnitTypeKelvin] = multi_converter_unit_kel, - - [UnitTypeKilometers] = multi_converter_unit_km, - [UnitTypeMeters] = multi_converter_unit_m, - [UnitTypeCentimeters] = multi_converter_unit_cm, - [UnitTypeMiles] = multi_converter_unit_mi, - [UnitTypeFeet] = multi_converter_unit_ft, - [UnitTypeInches] = multi_converter_unit_in, - - [UnitTypeDegree] = multi_converter_unit_deg, - [UnitTypeRadian] = multi_converter_unit_rad, + [UnitTypeDec] = multi_converter_unit_dec, + [UnitTypeHex] = multi_converter_unit_hex, + [UnitTypeBin] = multi_converter_unit_bin, + + [UnitTypeCelsius] = multi_converter_unit_cel, + [UnitTypeFahernheit] = multi_converter_unit_far, + [UnitTypeKelvin] = multi_converter_unit_kel, + + [UnitTypeKilometers] = multi_converter_unit_km, + [UnitTypeMeters] = multi_converter_unit_m, + [UnitTypeCentimeters] = multi_converter_unit_cm, + [UnitTypeMiles] = multi_converter_unit_mi, + [UnitTypeFeet] = multi_converter_unit_ft, + [UnitTypeInches] = multi_converter_unit_in, + + [UnitTypeDegree] = multi_converter_unit_deg, + [UnitTypeRadian] = multi_converter_unit_rad, }; \ No newline at end of file diff --git a/applications/music_player/application.fam b/applications/music_player/application.fam index 70e2974323..9b57028ac5 100644 --- a/applications/music_player/application.fam +++ b/applications/music_player/application.fam @@ -1,7 +1,7 @@ App( appid="music_player", name="Music Player", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.GAME, entry_point="music_player_app", cdefines=["APP_MUSIC_PLAYER"], requires=[ @@ -10,7 +10,7 @@ App( ], provides=["music_player_start"], stack_size=2 * 1024, - order=20, + order=45, ) App( diff --git a/applications/nfc/nfc.c b/applications/nfc/nfc.c index 3422e91af8..57e25f81ef 100644 --- a/applications/nfc/nfc.c +++ b/applications/nfc/nfc.c @@ -94,6 +94,11 @@ Nfc* nfc_alloc() { view_dispatcher_add_view( nfc->view_dispatcher, NfcViewDictAttack, dict_attack_get_view(nfc->dict_attack)); + // Detect Reader + nfc->detect_reader = detect_reader_alloc(); + view_dispatcher_add_view( + nfc->view_dispatcher, NfcViewDetectReader, detect_reader_get_view(nfc->detect_reader)); + // Generator nfc->generator = NULL; @@ -158,6 +163,10 @@ void nfc_free(Nfc* nfc) { view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack); dict_attack_free(nfc->dict_attack); + // Detect Reader + view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDetectReader); + detect_reader_free(nfc->detect_reader); + // Worker nfc_worker_stop(nfc->worker); nfc_worker_free(nfc->worker); diff --git a/applications/nfc/nfc_i.h b/applications/nfc/nfc_i.h index bcfe4a2192..c9ba7fff71 100644 --- a/applications/nfc/nfc_i.h +++ b/applications/nfc/nfc_i.h @@ -28,6 +28,7 @@ #include #include "views/dict_attack.h" +#include "views/detect_reader.h" #include #include @@ -71,6 +72,7 @@ struct Nfc { TextBox* text_box; Widget* widget; DictAttack* dict_attack; + DetectReader* detect_reader; const NfcGenerator* generator; }; @@ -85,6 +87,7 @@ typedef enum { NfcViewTextBox, NfcViewWidget, NfcViewDictAttack, + NfcViewDetectReader, } NfcView; Nfc* nfc_alloc(); diff --git a/applications/nfc/scenes/nfc_scene_config.h b/applications/nfc/scenes/nfc_scene_config.h index ff34a11d84..540fe10987 100644 --- a/applications/nfc/scenes/nfc_scene_config.h +++ b/applications/nfc/scenes/nfc_scene_config.h @@ -48,4 +48,6 @@ ADD_SCENE(nfc, rpc, Rpc) ADD_SCENE(nfc, exit_confirm, ExitConfirm) ADD_SCENE(nfc, retry_confirm, RetryConfirm) ADD_SCENE(nfc, detect_reader, DetectReader) +ADD_SCENE(nfc, mfkey_nonces_info, MfkeyNoncesInfo) +ADD_SCENE(nfc, mfkey_complete, MfkeyComplete) ADD_SCENE(nfc, nfc_data_info, NfcDataInfo) diff --git a/applications/nfc/scenes/nfc_scene_detect_reader.c b/applications/nfc/scenes/nfc_scene_detect_reader.c index f734f04cbd..8945febafa 100644 --- a/applications/nfc/scenes/nfc_scene_detect_reader.c +++ b/applications/nfc/scenes/nfc_scene_detect_reader.c @@ -1,126 +1,48 @@ #include "../nfc_i.h" #include -#define NFC_SCENE_DETECT_READER_LOG_SIZE_MAX (200) - -enum { - NfcSceneDetectReaderStateWidget, - NfcSceneDetectReaderStateTextBox, -}; - bool nfc_detect_reader_worker_callback(NfcWorkerEvent event, void* context) { UNUSED(event); furi_assert(context); Nfc* nfc = context; - view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventWorkerExit); + view_dispatcher_send_custom_event(nfc->view_dispatcher, event); return true; } -void nfc_scene_detect_reader_widget_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - Nfc* nfc = context; - if(type == InputTypeShort) { - view_dispatcher_send_custom_event(nfc->view_dispatcher, result); - } -} - -void nfc_detect_reader_textbox_callback(void* context) { +void nfc_scene_detect_reader_callback(void* context) { furi_assert(context); Nfc* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); } -// Add widget with device name or inform that data received -static void nfc_scene_detect_reader_widget_config(Nfc* nfc, bool data_received) { - Widget* widget = nfc->widget; - widget_reset(widget); - - widget_add_icon_element(widget, 0, 14, &I_Reader_detect); - widget_add_string_element( - widget, 64, 3, AlignCenter, AlignTop, FontSecondary, "Hold Near Reader"); - widget_add_string_element(widget, 55, 22, AlignLeft, AlignTop, FontPrimary, "Emulating..."); - - if(data_received) { - widget_add_button_element( - widget, GuiButtonTypeCenter, "Log", nfc_scene_detect_reader_widget_callback, nfc); - } -} - void nfc_scene_detect_reader_on_enter(void* context) { Nfc* nfc = context; DOLPHIN_DEED(DolphinDeedNfcEmulate); - FuriHalNfcDevData nfc_params = { - .uid = {0x36, 0x9C, 0xe7, 0xb1, 0x0A, 0xC1, 0x34}, - .uid_len = 7, - .atqa = {0x44, 0x00}, - .sak = 0x08, - .type = FuriHalNfcTypeA, - }; - nfc->dev->dev_data.nfc_data = nfc_params; - - // Setup Widget - nfc_scene_detect_reader_widget_config(nfc, false); - // Setup TextBox - TextBox* text_box = nfc->text_box; - text_box_set_font(text_box, TextBoxFontHex); - text_box_set_focus(text_box, TextBoxFocusEnd); - string_reset(nfc->text_box_store); - // Set Widget state and view - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - // Start worker - memset(&nfc->dev->dev_data.reader_data, 0, sizeof(NfcReaderRequestData)); + detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc); nfc_worker_start( nfc->worker, - NfcWorkerStateUidEmulate, + NfcWorkerStateAnalyzeReader, &nfc->dev->dev_data, nfc_detect_reader_worker_callback, nfc); + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDetectReader); + nfc_blink_start(nfc); } bool nfc_scene_detect_reader_on_event(void* context, SceneManagerEvent event) { Nfc* nfc = context; - NfcReaderRequestData* reader_data = &nfc->dev->dev_data.reader_data; - uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDetectReader); bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == NfcCustomEventWorkerExit) { - // Add data button to widget if data is received for the first time - if(!string_size(nfc->text_box_store)) { - nfc_scene_detect_reader_widget_config(nfc, true); - } - // Update TextBox data - if(string_size(nfc->text_box_store) < NFC_SCENE_DETECT_READER_LOG_SIZE_MAX) { - string_cat_printf(nfc->text_box_store, "R:"); - for(uint16_t i = 0; i < reader_data->size; i++) { - string_cat_printf(nfc->text_box_store, " %02X", reader_data->data[i]); - } - string_push_back(nfc->text_box_store, '\n'); - text_box_set_text(nfc->text_box, string_get_cstr(nfc->text_box_store)); - } - memset(reader_data, 0, sizeof(NfcReaderRequestData)); - consumed = true; - } else if(event.event == GuiButtonTypeCenter && state == NfcSceneDetectReaderStateWidget) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateTextBox); + if(event.event == NfcCustomEventViewExit) { + nfc_worker_stop(nfc->worker); + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyNoncesInfo); consumed = true; - } else if(event.event == NfcCustomEventViewExit && state == NfcSceneDetectReaderStateTextBox) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeBack) { - if(state == NfcSceneDetectReaderStateTextBox) { - view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); - scene_manager_set_scene_state( - nfc->scene_manager, NfcSceneDetectReader, NfcSceneDetectReaderStateWidget); + } else if(event.event == NfcWorkerEventDetectReaderMfkeyCollected) { + detect_reader_inc_nonce_cnt(nfc->detect_reader); consumed = true; } } @@ -135,9 +57,7 @@ void nfc_scene_detect_reader_on_exit(void* context) { nfc_worker_stop(nfc->worker); // Clear view - widget_reset(nfc->widget); - text_box_reset(nfc->text_box); - string_reset(nfc->text_box_store); + detect_reader_reset(nfc->detect_reader); nfc_blink_stop(nfc); } diff --git a/applications/nfc/scenes/nfc_scene_mfkey_complete.c b/applications/nfc/scenes/nfc_scene_mfkey_complete.c new file mode 100644 index 0000000000..3c4f9dba19 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mfkey_complete.c @@ -0,0 +1,49 @@ +#include "../nfc_i.h" + +void nfc_scene_mfkey_complete_callback(GuiButtonType result, InputType type, void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mfkey_complete_on_enter(void* context) { + Nfc* nfc = context; + + widget_add_string_element(nfc->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, "Complete!"); + widget_add_string_multiline_element( + nfc->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + "Now use mfkey32v2\nto extract keys"); + widget_add_button_element( + nfc->widget, GuiButtonTypeCenter, "OK", nfc_scene_mfkey_complete_callback, nfc); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mfkey_complete_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeCenter) { + consumed = scene_manager_search_and_switch_to_previous_scene( + nfc->scene_manager, NfcSceneStart); + } + } else if(event.event == SceneManagerEventTypeBack) { + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); + } + + return consumed; +} + +void nfc_scene_mfkey_complete_on_exit(void* context) { + Nfc* nfc = context; + + widget_reset(nfc->widget); +} \ No newline at end of file diff --git a/applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c b/applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c new file mode 100644 index 0000000000..b45b690d38 --- /dev/null +++ b/applications/nfc/scenes/nfc_scene_mfkey_nonces_info.c @@ -0,0 +1,55 @@ +#include "../nfc_i.h" +#include + +void nfc_scene_mfkey_nonces_info_callback(GuiButtonType result, InputType type, void* context) { + Nfc* nfc = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(nfc->view_dispatcher, result); + } +} + +void nfc_scene_mfkey_nonces_info_on_enter(void* context) { + Nfc* nfc = context; + + string_t temp_str; + string_init(temp_str); + + uint16_t nonces_saved = mfkey32_get_auth_sectors(temp_str); + widget_add_text_scroll_element(nfc->widget, 0, 22, 128, 42, string_get_cstr(temp_str)); + string_printf(temp_str, "Nonces saved %d", nonces_saved); + widget_add_string_element( + nfc->widget, 0, 0, AlignLeft, AlignTop, FontPrimary, string_get_cstr(temp_str)); + widget_add_string_element( + nfc->widget, 0, 12, AlignLeft, AlignTop, FontSecondary, "Authenticated sectors:"); + + widget_add_button_element( + nfc->widget, GuiButtonTypeRight, "Next", nfc_scene_mfkey_nonces_info_callback, nfc); + + string_clear(temp_str); + + view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget); +} + +bool nfc_scene_mfkey_nonces_info_on_event(void* context, SceneManagerEvent event) { + Nfc* nfc = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == GuiButtonTypeRight) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneMfkeyComplete); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeBack) { + consumed = + scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart); + } + + return consumed; +} + +void nfc_scene_mfkey_nonces_info_on_exit(void* context) { + Nfc* nfc = context; + + // Clear view + widget_reset(nfc->widget); +} diff --git a/applications/nfc/scenes/nfc_scene_start.c b/applications/nfc/scenes/nfc_scene_start.c index 01ffb46b82..1a9051dfd6 100644 --- a/applications/nfc/scenes/nfc_scene_start.c +++ b/applications/nfc/scenes/nfc_scene_start.c @@ -49,7 +49,12 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; + if(sd_exist) { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader); + } else { + scene_manager_next_scene(nfc->scene_manager, NfcSceneDictNotFound); + } consumed = true; } else if(event.event == SubmenuIndexSaved) { scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); @@ -61,7 +66,6 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(nfc->scene_manager, NfcSceneSetType); consumed = true; } else if(event.event == SubmenuIndexDebug) { - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexDebug); scene_manager_next_scene(nfc->scene_manager, NfcSceneDebug); consumed = true; } diff --git a/applications/nfc/views/detect_reader.c b/applications/nfc/views/detect_reader.c new file mode 100644 index 0000000000..177c13f75d --- /dev/null +++ b/applications/nfc/views/detect_reader.c @@ -0,0 +1,115 @@ +#include "detect_reader.h" + +#include + +struct DetectReader { + View* view; + DetectReaderDoneCallback callback; + void* context; +}; + +typedef struct { + uint16_t nonces; +} DetectReaderViewModel; + +static void detect_reader_draw_callback(Canvas* canvas, void* model) { + DetectReaderViewModel* m = model; + char text[32] = {}; + + snprintf(text, sizeof(text), "Tap the reader several times"); + canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Tap the reader several times"); + + if(m->nonces == 0) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 52, 22, AlignLeft, AlignTop, "Emulating..."); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 52, 35, AlignLeft, AlignTop, "MIFARE Classic"); + canvas_draw_icon(canvas, 0, 13, &I_Tap_reader_36x38); + } else { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 54, 22, AlignLeft, AlignTop, "Collecting..."); + canvas_set_font(canvas, FontSecondary); + snprintf(text, sizeof(text), "Nonces: %d", m->nonces); + canvas_draw_str_aligned(canvas, 54, 35, AlignLeft, AlignTop, text); + elements_button_right(canvas, "Next"); + canvas_draw_icon(canvas, 6, 15, &I_ArrowC_1_36x36); + } +} + +static bool detect_reader_input_callback(InputEvent* event, void* context) { + DetectReader* detect_reader = context; + furi_assert(detect_reader->callback); + bool consumed = false; + + uint8_t nonces = 0; + with_view_model( + detect_reader->view, (DetectReaderViewModel * model) { + nonces = model->nonces; + return false; + }); + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + if(nonces > 0) { + detect_reader->callback(detect_reader->context); + consumed = true; + } + } + } + + return consumed; +} + +DetectReader* detect_reader_alloc() { + DetectReader* detect_reader = malloc(sizeof(DetectReader)); + detect_reader->view = view_alloc(); + view_allocate_model(detect_reader->view, ViewModelTypeLocking, sizeof(DetectReaderViewModel)); + view_set_draw_callback(detect_reader->view, detect_reader_draw_callback); + view_set_input_callback(detect_reader->view, detect_reader_input_callback); + view_set_context(detect_reader->view, detect_reader); + + return detect_reader; +} + +void detect_reader_free(DetectReader* detect_reader) { + furi_assert(detect_reader); + + view_free(detect_reader->view); + free(detect_reader); +} + +void detect_reader_reset(DetectReader* detect_reader) { + furi_assert(detect_reader); + + with_view_model( + detect_reader->view, (DetectReaderViewModel * model) { + model->nonces = 0; + return false; + }); +} + +View* detect_reader_get_view(DetectReader* detect_reader) { + furi_assert(detect_reader); + + return detect_reader->view; +} + +void detect_reader_set_callback( + DetectReader* detect_reader, + DetectReaderDoneCallback callback, + void* context) { + furi_assert(detect_reader); + furi_assert(callback); + + detect_reader->callback = callback; + detect_reader->context = context; +} + +void detect_reader_inc_nonce_cnt(DetectReader* detect_reader) { + furi_assert(detect_reader); + with_view_model( + detect_reader->view, (DetectReaderViewModel * model) { + model->nonces++; + return false; + }); +} diff --git a/applications/nfc/views/detect_reader.h b/applications/nfc/views/detect_reader.h new file mode 100644 index 0000000000..12cd03db49 --- /dev/null +++ b/applications/nfc/views/detect_reader.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include +#include + +typedef struct DetectReader DetectReader; + +typedef void (*DetectReaderDoneCallback)(void* context); + +DetectReader* detect_reader_alloc(); + +void detect_reader_free(DetectReader* detect_reader); + +void detect_reader_reset(DetectReader* detect_reader); + +View* detect_reader_get_view(DetectReader* detect_reader); + +void detect_reader_set_callback( + DetectReader* detect_reader, + DetectReaderDoneCallback callback, + void* context); + +void detect_reader_inc_nonce_cnt(DetectReader* detect_reader); diff --git a/applications/nrfsniff/nrfsniff.c b/applications/nrfsniff/nrfsniff.c index c56ee9fde2..ab074f87fe 100644 --- a/applications/nrfsniff/nrfsniff.c +++ b/applications/nrfsniff/nrfsniff.c @@ -35,13 +35,13 @@ typedef struct { char rate_text_fmt[] = "Transfer rate: %dMbps"; char sample_text_fmt[] = "Sample Time: %d ms"; -char channel_text_fmt[] = "Channel: %d"; +char channel_text_fmt[] = "Channel: %d Sniffing: %s"; char preamble_text_fmt[] = "Preamble: %02X"; -char sniff_text_fmt[] = "Sniffing: %s Found: %d"; +char sniff_text_fmt[] = "Found: %d Unique: %u"; char addresses_header_text[] = "Address,rate"; char sniffed_address_fmt[] = "%s,%d"; char rate_text[46]; -char channel_text[14]; +char channel_text[38]; char sample_text[32]; char preamble_text[14]; char sniff_text[38]; @@ -49,6 +49,7 @@ char sniffed_address[14]; uint8_t target_channel = 0; uint32_t found_count = 0; +uint32_t unique_saved_count = 0; uint32_t sample_time = DEFAULT_SAMPLE_TIME; uint8_t target_rate = 8; // rate can be either 8 (2Mbps) or 0 (1Mbps) uint8_t target_preamble[] = {0xAA, 0x00}; @@ -111,10 +112,10 @@ static void render_callback(Canvas* const canvas, void* ctx) { if(!sniffing_state) strcpy(sniffing, "No"); snprintf(rate_text, sizeof(rate_text), rate_text_fmt, (int)rate); - snprintf(channel_text, sizeof(channel_text), channel_text_fmt, (int)target_channel); + snprintf(channel_text, sizeof(channel_text), channel_text_fmt, (int)target_channel, sniffing); snprintf(sample_text, sizeof(sample_text), sample_text_fmt, (int)sample_time); //snprintf(preamble_text, sizeof(preamble_text), preamble_text_fmt, target_preamble[0]); - snprintf(sniff_text, sizeof(sniff_text), sniff_text_fmt, sniffing, found_count); + snprintf(sniff_text, sizeof(sniff_text), sniff_text_fmt, found_count, unique_saved_count); snprintf( sniffed_address, sizeof(sniffed_address), sniffed_address_fmt, top_address, (int)rate); canvas_draw_str_aligned(canvas, 10, 10, AlignLeft, AlignBottom, rate_text); @@ -203,6 +204,7 @@ static bool save_addr_to_file( notification_message(notification, &sequence_success); stream_free(stream); + unique_saved_count++; return true; } } @@ -287,8 +289,8 @@ static void wrap_up(Storage* storage, NotificationApp* notification) { if(ch <= LOGITECH_MAX_CHANNEL) { hexlify(addr, 5, top_address); - save_addr_to_file(storage, addr, 5, notification); found_count++; + save_addr_to_file(storage, addr, 5, notification); if(confirmed_idx < MAX_CONFIRMED) memcpy(confirmed[confirmed_idx++], addr, 5); break; } @@ -297,6 +299,7 @@ static void wrap_up(Storage* storage, NotificationApp* notification) { static void clear_cache() { found_count = 0; + unique_saved_count = 0; confirmed_idx = 0; candidate_idx = 0; target_channel = 2; diff --git a/applications/power/power_service/power.c b/applications/power/power_service/power.c index 3a2c6cf3b1..a47a4e926c 100644 --- a/applications/power/power_service/power.c +++ b/applications/power/power_service/power.c @@ -1,5 +1,6 @@ #include "power_i.h" #include "views/power_off.h" +#include "desktop/desktop_settings/desktop_settings.h" #include #include @@ -14,7 +15,59 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { canvas_draw_icon(canvas, 0, 0, &I_Battery_26x8); if(power->info.gauge_is_ok) { - canvas_draw_box(canvas, 2, 2, (power->info.charge + 4) / 5, 4); + char batteryPercentile[5]; + snprintf(batteryPercentile, sizeof(batteryPercentile), "%d", power->info.charge); + strcat(batteryPercentile, "%"); + + if((power->displayBatteryPercentage == 1) && + (power->state != + PowerStateCharging)) { //if display battery percentage, black background white text + canvas_set_font(canvas, FontBatteryPercent); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 1, 1, 22, 6); + canvas_set_color(canvas, ColorWhite); + canvas_draw_str_aligned(canvas, 12, 4, AlignCenter, AlignCenter, batteryPercentile); + } else if( + (power->displayBatteryPercentage == 2) && + (power->state != + PowerStateCharging)) { //if display inverted percentage, white background black text + canvas_set_font(canvas, FontBatteryPercent); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, 12, 4, AlignCenter, AlignCenter, batteryPercentile); + } else if( + (power->displayBatteryPercentage == 3) && + (power->state != PowerStateCharging)) { //Retro style segmented display, 3 parts + if(power->info.charge > 25) { + canvas_draw_box(canvas, 2, 2, 6, 4); + } + if(power->info.charge > 50) { + canvas_draw_box(canvas, 9, 2, 6, 4); + } + if(power->info.charge > 75) { + canvas_draw_box(canvas, 16, 2, 6, 4); + } + } else if( + (power->displayBatteryPercentage == 4) && + (power->state != PowerStateCharging)) { //Retro style segmented display, 5 parts + if(power->info.charge > 10) { + canvas_draw_box(canvas, 2, 2, 3, 4); + } + if(power->info.charge > 30) { + canvas_draw_box(canvas, 6, 2, 3, 4); + } + if(power->info.charge > 50) { + canvas_draw_box(canvas, 10, 2, 3, 4); + } + if(power->info.charge > 70) { + canvas_draw_box(canvas, 14, 2, 3, 4); + } + if(power->info.charge > 90) { + canvas_draw_box(canvas, 18, 2, 3, 4); + } + } else { //default bar display, added here to serve as fallback/default behaviour. + canvas_draw_box(canvas, 2, 2, (power->info.charge + 4) / 5, 4); + } + if(power->state == PowerStateCharging) { canvas_set_bitmap_mode(canvas, 1); canvas_set_color(canvas, ColorWhite); @@ -207,6 +260,11 @@ int32_t power_srv(void* p) { power_update_info(power); furi_record_create(RECORD_POWER, power); + DesktopSettings* settings = malloc(sizeof(DesktopSettings)); + LOAD_DESKTOP_SETTINGS(settings); + power->displayBatteryPercentage = settings->displayBatteryPercentage; + free(settings); + while(1) { // Update data from gauge and charger bool need_refresh = power_update_info(power); @@ -221,7 +279,13 @@ int32_t power_srv(void* p) { power_check_battery_level_change(power); // Update battery view port - if(need_refresh) view_port_update(power->battery_view_port); + if(need_refresh) { + DesktopSettings* settings = malloc(sizeof(DesktopSettings)); + LOAD_DESKTOP_SETTINGS(settings); + power->displayBatteryPercentage = settings->displayBatteryPercentage; + free(settings); + view_port_update(power->battery_view_port); + } // Check OTG status and disable it in case of fault if(furi_hal_power_is_otg_enabled()) { diff --git a/applications/power/power_service/power_i.h b/applications/power/power_service/power_i.h index 66ced885b1..4344728960 100644 --- a/applications/power/power_service/power_i.h +++ b/applications/power/power_service/power_i.h @@ -36,6 +36,7 @@ struct Power { bool battery_low; bool show_low_bat_level_message; + uint8_t displayBatteryPercentage; uint8_t battery_level; uint8_t power_off_timeout; diff --git a/applications/storage/storages/storage_int.c b/applications/storage/storages/storage_int.c index 0765a92dc5..cae61f16e4 100644 --- a/applications/storage/storages/storage_int.c +++ b/applications/storage/storages/storage_int.c @@ -9,7 +9,7 @@ /* When less than LFS_RESERVED_PAGES_COUNT are left free, creation & * modification of non-dot files is restricted */ -#define LFS_RESERVED_PAGES_COUNT 5 +#define LFS_RESERVED_PAGES_COUNT 3 typedef struct { const size_t start_address; diff --git a/applications/subbrute/LICENSE.md b/applications/subbrute/LICENSE.md new file mode 100644 index 0000000000..a856581c9f --- /dev/null +++ b/applications/subbrute/LICENSE.md @@ -0,0 +1,8 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * @G4N4P4T1 wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ \ No newline at end of file diff --git a/applications/subbrute/README.md b/applications/subbrute/README.md new file mode 100644 index 0000000000..04a0c975d0 --- /dev/null +++ b/applications/subbrute/README.md @@ -0,0 +1,4 @@ +# FlipFrid + +SubGhz Fuzzer +select your base message, the field to fuzz and let's get fuzzy ! \ No newline at end of file diff --git a/applications/subbrute/application.fam b/applications/subbrute/application.fam new file mode 100644 index 0000000000..2608cba8fe --- /dev/null +++ b/applications/subbrute/application.fam @@ -0,0 +1,10 @@ +App( + appid="subbrute", + name="SubGHz Bruteforcer", + apptype=FlipperAppType.PLUGIN, + entry_point="subbrute_start", + cdefines=["APP_SUB_BRUTE"], + requires=["gui","dialogs"], + stack_size=2 * 1024, + order=11, +) diff --git a/applications/subbrute/scene/subbrute_scene_entrypoint.c b/applications/subbrute/scene/subbrute_scene_entrypoint.c new file mode 100644 index 0000000000..a568ec1522 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_entrypoint.c @@ -0,0 +1,198 @@ +#include "subbrute_scene_entrypoint.h" +#include "../subbrute_utils.h" + +string_t subbrute_menu_items[9]; + +void subbrute_scene_entrypoint_menu_callback(SubBruteState* context, uint32_t index) { + string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); + string_set_str(context->protocol, "RAW"); + context->repeat = 5; + context->te = 0; + context->attack = index; + switch(index) { + case SubBruteAttackLoadFile: + context->current_scene = SceneSelectFile; + break; + case SubBruteAttackCAME12bit433: + context->frequency = 433920000; + context->bit = 12; + string_set_str(context->protocol, "CAME"); + string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); + if(!subbrute_is_frequency_allowed(context)) { + return; + } + context->current_scene = SceneAttack; + break; + case SubBruteAttackCAME12bit868: + context->frequency = 868350000; + context->bit = 12; + string_set_str(context->protocol, "CAME"); + string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); + if(!subbrute_is_frequency_allowed(context)) { + return; + } + context->current_scene = SceneAttack; + break; + case SubBruteAttackChamberlain9bit315: + context->frequency = 315000000; + context->bit = 9; + string_set_str(context->protocol, "Cham_Code"); + string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); + + if(!subbrute_is_frequency_allowed(context)) { + return; + } + context->current_scene = SceneAttack; + break; + case SubBruteAttackChamberlain9bit390: + context->frequency = 390000000; + context->bit = 9; + string_set_str(context->protocol, "Cham_Code"); + string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); + + if(!subbrute_is_frequency_allowed(context)) { + return; + } + context->current_scene = SceneAttack; + break; + case SubBruteAttackLinear10bit300: + context->frequency = 300000000; + context->bit = 10; + string_set_str(context->protocol, "Linear"); + string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); + if(!subbrute_is_frequency_allowed(context)) { + return; + } + context->current_scene = SceneAttack; + break; + case SubBruteAttackLinear10bit310: + context->frequency = 310000000; + context->bit = 10; + string_set_str(context->protocol, "Linear"); + string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); + if(!subbrute_is_frequency_allowed(context)) { + return; + } + context->current_scene = SceneAttack; + break; + case SubBruteAttackNICE12bit433: + context->frequency = 433920000; + context->bit = 12; + string_set_str(context->protocol, "Nice FLO"); + string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); + if(!subbrute_is_frequency_allowed(context)) { + return; + } + context->current_scene = SceneAttack; + break; + case SubBruteAttackNICE12bit868: + context->frequency = 868350000; + context->bit = 12; + string_set_str(context->protocol, "Nice FLO"); + string_set_str(context->preset, "FuriHalSubGhzPresetOok650Async"); + if(!subbrute_is_frequency_allowed(context)) { + return; + } + context->current_scene = SceneAttack; + break; + default: + break; + } +} + +void subbrute_scene_entrypoint_on_enter(SubBruteState* context) { + // Clear the previous payload + context->menu_index = 0; + for(uint32_t i = 0; i < 9; i++) { + string_init(subbrute_menu_items[i]); + } + + string_set(subbrute_menu_items[0], "BF existing dump"); + string_set(subbrute_menu_items[1], "CAME 12bit 433mhz"); + string_set(subbrute_menu_items[2], "CAME 12bit 868mhz"); + string_set(subbrute_menu_items[3], "Chamberlain 9bit 315mhz"); + string_set(subbrute_menu_items[4], "Chamberlain 9bit 390mhz"); + string_set(subbrute_menu_items[5], "Linear 10bit 300mhz"); + string_set(subbrute_menu_items[6], "Linear 10bit 310mhz"); + string_set(subbrute_menu_items[7], "NICE 12bit 433mhz"); + string_set(subbrute_menu_items[8], "NICE 12bit 868mhz"); +} + +void subbrute_scene_entrypoint_on_exit(SubBruteState* context) { + UNUSED(context); + for(uint32_t i = 0; i < 9; i++) { + string_clear(subbrute_menu_items[i]); + } +} + +void subbrute_scene_entrypoint_on_tick(SubBruteState* context) { + UNUSED(context); +} + +void subbrute_scene_entrypoint_on_event(SubBruteEvent event, SubBruteState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + if(context->menu_index < SubBruteAttackNICE12bit868) { + context->menu_index++; + } + break; + case InputKeyUp: + if(context->menu_index > SubBruteAttackLoadFile) { + context->menu_index--; + } + break; + case InputKeyLeft: + case InputKeyRight: + break; + case InputKeyOk: + subbrute_scene_entrypoint_menu_callback(context, context->menu_index); + break; + case InputKeyBack: + context->is_running = false; + break; + } + } + } +} + +void subbrute_scene_entrypoint_on_draw(Canvas* canvas, SubBruteState* context) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 6, AlignCenter, AlignTop, "SubGHz Fuzzer"); + + if(context->menu_index > SubBruteAttackLoadFile) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 24, + AlignCenter, + AlignTop, + string_get_cstr(subbrute_menu_items[context->menu_index - 1])); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, + 64, + 36, + AlignCenter, + AlignTop, + string_get_cstr(subbrute_menu_items[context->menu_index])); + + if(context->menu_index < SubBruteAttackNICE12bit868) { + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, + 64, + 48, + AlignCenter, + AlignTop, + string_get_cstr(subbrute_menu_items[context->menu_index + 1])); + } +} \ No newline at end of file diff --git a/applications/subbrute/scene/subbrute_scene_entrypoint.h b/applications/subbrute/scene/subbrute_scene_entrypoint.h new file mode 100644 index 0000000000..af6b3bc49c --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_entrypoint.h @@ -0,0 +1,8 @@ +#pragma once +#include "../subbrute.h" + +void subbrute_scene_entrypoint_on_enter(SubBruteState* context); +void subbrute_scene_entrypoint_on_exit(SubBruteState* context); +void subbrute_scene_entrypoint_on_tick(SubBruteState* context); +void subbrute_scene_entrypoint_on_event(SubBruteEvent event, SubBruteState* context); +void subbrute_scene_entrypoint_on_draw(Canvas* canvas, SubBruteState* context); \ No newline at end of file diff --git a/applications/subbrute/scene/subbrute_scene_load_file.c b/applications/subbrute/scene/subbrute_scene_load_file.c new file mode 100644 index 0000000000..0f6d953f77 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_load_file.c @@ -0,0 +1,212 @@ +#include "subbrute_scene_load_file.h" +#include "subbrute_scene_entrypoint.h" +#include "../subbrute_utils.h" +#include + +#define SUBGHZ_APP_PATH_FOLDER "/ext/subghz" + +bool subbrute_load(SubBruteState* context, const char* file_path) { + bool result = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + + string_t temp_str; + string_init(temp_str); + uint32_t temp_data32; + + do { + if(!flipper_format_file_open_existing(fff_data_file, file_path)) { + FURI_LOG_E(TAG, "Error open file %s", file_path); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Error open file"); + break; + } + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Missing or incorrect header"); + break; + } + // Frequency + if(flipper_format_read_uint32(fff_data_file, "Frequency", &temp_data32, 1)) { + FURI_LOG_I(TAG, "Frequency: %d", temp_data32); + context->frequency = temp_data32; + if(!subbrute_is_frequency_allowed(context)) { + break; + } + } else { + FURI_LOG_E(TAG, "Missing or incorrect Frequency"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Missing or incorrect Frequency"); + break; + } + // Preset + if(!flipper_format_read_string(fff_data_file, "Preset", context->preset)) { + FURI_LOG_E(TAG, "Preset FAIL"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Preset FAIL"); + } + // Protocol + if(!flipper_format_read_string(fff_data_file, "Protocol", context->protocol)) { + FURI_LOG_E(TAG, "Missing Protocol"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Missing Protocol"); + break; + } else { + FURI_LOG_I(TAG, "Protocol: %s", string_get_cstr(context->protocol)); + } + + if(strcmp(string_get_cstr(context->protocol), "RAW") == 0) { + FURI_LOG_E(TAG, "RAW unsupported"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "RAW unsupported"); + break; + } + + const SubGhzProtocol* registry = + subghz_protocol_registry_get_by_name(string_get_cstr(context->protocol)); + + if(registry && registry->type == SubGhzProtocolTypeDynamic) { + FURI_LOG_D(TAG, "Protocol is dynamic - not supported"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Dynamic protocol unsupported"); + break; + } + + context->decoder_result = subghz_receiver_search_decoder_base_by_name( + context->receiver, string_get_cstr(context->protocol)); + + if(context->decoder_result) { + FURI_LOG_I(TAG, "Found decoder"); + } else { + FURI_LOG_E(TAG, "Protocol not found"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Protocol not found"); + break; + } + + // Bit + if(!flipper_format_read_uint32(fff_data_file, "Bit", &temp_data32, 1)) { + FURI_LOG_E(TAG, "Missing or incorrect Bit"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Missing or incorrect Bit"); + break; + } else { + FURI_LOG_I(TAG, "Bit: %d", temp_data32); + context->bit = temp_data32; + } + + // Key + if(!flipper_format_read_string(fff_data_file, "Key", temp_str)) { + FURI_LOG_E(TAG, "Missing or incorrect Key"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "Missing or incorrect Key"); + break; + } else { + FURI_LOG_I(TAG, "Key: %s", string_get_cstr(temp_str)); + string_set(context->key, string_get_cstr(temp_str)); + } + + // TE + if(!flipper_format_read_uint32(fff_data_file, "TE", &temp_data32, 1)) { + FURI_LOG_E(TAG, "Missing or incorrect TE"); + //string_reset(context->notification_msg); + //string_set_str(context->notification_msg, "Missing or incorrect TE"); + //break; + } else { + FURI_LOG_I(TAG, "TE: %d", temp_data32); + context->te = temp_data32; + } + + // Repeat + if(flipper_format_read_uint32(fff_data_file, "Repeat", &temp_data32, 1)) { + FURI_LOG_I(TAG, "Repeat: %d", temp_data32); + context->repeat = temp_data32; + } else { + FURI_LOG_I(TAG, "Repeat: 3 (default)"); + context->repeat = 3; + } + + result = true; + } while(0); + + string_clear(temp_str); + flipper_format_file_close(fff_data_file); + flipper_format_free(fff_data_file); + if(result) { + FURI_LOG_I(TAG, "Loaded successfully"); + string_reset(context->notification_msg); + string_set_str(context->notification_msg, "File looks ok."); + } + + return result; +} + +void subbrute_scene_load_file_on_enter(SubBruteState* context) { + if(subbrute_load_protocol_from_file(context)) { + context->current_scene = SceneSelectField; + } else { + subbrute_scene_entrypoint_on_enter(context); + context->current_scene = SceneEntryPoint; + } +} + +void subbrute_scene_load_file_on_exit(SubBruteState* context) { + UNUSED(context); +} + +void subbrute_scene_load_file_on_tick(SubBruteState* context) { + UNUSED(context); +} + +void subbrute_scene_load_file_on_event(SubBruteEvent event, SubBruteState* context) { + UNUSED(context); + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + case InputKeyUp: + case InputKeyLeft: + case InputKeyRight: + case InputKeyOk: + break; + case InputKeyBack: + context->current_scene = SceneEntryPoint; + break; + } + } + } +} + +void subbrute_scene_load_file_on_draw(Canvas* canvas, SubBruteState* context) { + UNUSED(context); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Frame + //canvas_draw_frame(canvas, 0, 0, 128, 64); + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 16, AlignCenter, AlignTop, "SubGHz Fuzzer"); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Error: Press back"); +} + +bool subbrute_load_protocol_from_file(SubBruteState* context) { + string_t file_path; + string_init(file_path); + string_set_str(file_path, SUBGHZ_APP_PATH_FOLDER); + + // Input events and views are managed by file_select + bool res = dialog_file_browser_show( + context->dialogs, file_path, file_path, SUBGHZ_APP_EXTENSION, true, &I_sub1_10px, true); + + if(res) { + res = subbrute_load(context, string_get_cstr(file_path)); + } + + string_clear(file_path); + + return res; +} \ No newline at end of file diff --git a/applications/subbrute/scene/subbrute_scene_load_file.h b/applications/subbrute/scene/subbrute_scene_load_file.h new file mode 100644 index 0000000000..9e186b1c95 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_load_file.h @@ -0,0 +1,8 @@ +#include "../subbrute.h" + +void subbrute_scene_load_file_on_enter(SubBruteState* context); +void subbrute_scene_load_file_on_exit(SubBruteState* context); +void subbrute_scene_load_file_on_tick(SubBruteState* context); +void subbrute_scene_load_file_on_event(SubBruteEvent event, SubBruteState* context); +void subbrute_scene_load_file_on_draw(Canvas* canvas, SubBruteState* context); +bool subbrute_load_protocol_from_file(SubBruteState* context); \ No newline at end of file diff --git a/applications/subbrute/scene/subbrute_scene_run_attack.c b/applications/subbrute/scene/subbrute_scene_run_attack.c new file mode 100644 index 0000000000..89d8c291c1 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_run_attack.c @@ -0,0 +1,366 @@ +#include "subbrute_scene_run_attack.h" +#include +#include + +uint64_t subbrute_counter = 0; +uint64_t max_value; +bool locked = false; +bool toSave = false; +char subbrute_payload_byte[4]; +#define SUBBRUTE_DELAY 1 + +FuriHalSubGhzPreset str_to_preset(string_t preset) { + if(string_cmp_str(preset, "FuriHalSubGhzPresetOok270Async") == 0) { + return FuriHalSubGhzPresetOok270Async; + } + if(string_cmp_str(preset, "FuriHalSubGhzPresetOok650Async") == 0) { + return FuriHalSubGhzPresetOok650Async; + } + if(string_cmp_str(preset, "FuriHalSubGhzPreset2FSKDev238Async") == 0) { + return FuriHalSubGhzPreset2FSKDev238Async; + } + if(string_cmp_str(preset, "FuriHalSubGhzPreset2FSKDev476Async") == 0) { + return FuriHalSubGhzPreset2FSKDev476Async; + } + if(string_cmp_str(preset, "FuriHalSubGhzPresetMSK99_97KbAsync") == 0) { + return FuriHalSubGhzPresetMSK99_97KbAsync; + } + if(string_cmp_str(preset, "FuriHalSubGhzPresetMSK99_97KbAsync") == 0) { + return FuriHalSubGhzPresetMSK99_97KbAsync; + } + return FuriHalSubGhzPresetCustom; +} + +void subbrute_emit(SubBruteState* context) { + //FURI_LOG_D(TAG, string_get_cstr(context->flipper_format_string)); + + context->transmitter = + subghz_transmitter_alloc_init(context->environment, string_get_cstr(context->protocol)); + + subghz_transmitter_deserialize(context->transmitter, context->flipper_format); + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(str_to_preset(context->preset)); + + context->frequency_cal = furi_hal_subghz_set_frequency_and_path(context->frequency); + + furi_hal_subghz_start_async_tx(subghz_transmitter_yield, context->transmitter); + while(!(furi_hal_subghz_is_async_tx_complete())) { + furi_delay_ms(1); + } + + furi_hal_subghz_stop_async_tx(); + subghz_transmitter_stop(context->transmitter); + furi_hal_subghz_idle(); + subghz_transmitter_free(context->transmitter); +} + +void prepare_emit(SubBruteState* context) { + UNUSED(context); + + furi_hal_subghz_init(); +} + +void clear_emit(SubBruteState* context) { + UNUSED(context); + + furi_hal_subghz_stop_async_tx(); + furi_hal_subghz_idle(); + furi_hal_subghz_sleep(); +} +/* +void subbrute_send_raw_packet(SubBruteState* context) { + string_reset(context->candidate); + + // Payload to padded binary string + int* binaryNum = (int*)malloc(sizeof(int) * context->bit); + uint32_t i = 0; + for(i = 0; i < context->bit; i++) { + binaryNum[i] = 0; + } + i = 0; + uint64_t counter = context->payload; + while(counter > 0) { + binaryNum[i] = counter % 2; + counter = counter / 2; + i++; + } + + // printing binary array in reverse order and build raw payload + for(uint32_t loop = 0; loop < context->repeat; loop++) { + for(int j = (int)context->bit - 1; j >= 0; j--) { + if(binaryNum[j] == 1) { + string_cat(context->candidate, context->subbrute_raw_one); + } else { + string_cat(context->candidate, context->subbrute_raw_zero); + } + } + string_cat(context->candidate, context->subbrute_raw_stop); + } + + free(binaryNum); + + string_init_printf( + context->flipper_format_string, + "Filetype: Flipper SubGhz RAW File\n" + "Version: 1\n" + "Frequency: %d\n" + "Preset: %s\n" + "Protocol: RAW\n" + "RAW_Data: %s", + context->frequency, + string_get_cstr(context->preset), + string_get_cstr(context->candidate)); + + subbrute_emit(context); +} +*/ +void subbrute_send_packet_parsed(SubBruteState* context) { + if(context->attack == SubBruteAttackLoadFile) { + snprintf(subbrute_payload_byte, 4, "%02X ", (uint8_t)context->payload); + string_replace_at(context->candidate, context->str_index, 3, subbrute_payload_byte); + } else { + string_t buffer; + string_init(buffer); + string_init_printf(buffer, "%16X", context->payload); + int j = 0; + string_set_str(context->candidate, " "); + for(uint8_t i = 0; i < 16; i++) { + if(string_get_char(buffer, i) != ' ') { + string_set_char(context->candidate, i + j, string_get_char(buffer, i)); + } else { + string_set_char(context->candidate, i + j, '0'); + } + if(i % 2 != 0) { + j++; + } + } + string_clear(buffer); + } + if(strcmp(string_get_cstr(context->protocol), "Princeton") == 0) { + string_init_printf( + context->flipper_format_string, + "Filetype: Flipper SubGhz Key File\n" + "Version: 1\n" + "Frequency: %u\n" + "Preset: %s\n" + "Protocol: %s\n" + "Bit: %d\n" + "Key: %s\n" + "TE: %d\n", + context->frequency, + string_get_cstr(context->preset), + string_get_cstr(context->protocol), + context->bit, + string_get_cstr(context->candidate), + context->te); + } else { + string_init_printf( + context->flipper_format_string, + "Filetype: Flipper SubGhz Key File\n" + "Version: 1\n" + "Frequency: %u\n" + "Preset: %s\n" + "Protocol: %s\n" + "Bit: %d\n" + "Key: %s\n", + context->frequency, + string_get_cstr(context->preset), + string_get_cstr(context->protocol), + context->bit, + string_get_cstr(context->candidate)); + } + + stream_clean(context->stream); + stream_write_string(context->stream, context->flipper_format_string); + + subbrute_emit(context); +} + +void subbrute_send_packet(SubBruteState* context) { + ///if(string_cmp_str(context->protocol, "RAW") == 0) { + // subbrute_send_raw_packet(context); + //} else { + subbrute_send_packet_parsed(context); + //} + string_clear(context->flipper_format_string); +} + +void subbrute_scene_run_attack_on_exit(SubBruteState* context) { + if(!toSave) { + clear_emit(context); + furi_thread_free(context->bruthread); + } +} + +void subbrute_scene_run_attack_on_tick(SubBruteState* context) { + if(!context->is_attacking || locked) { + return; + } + //if(0 != subbrute_counter) { + locked = true; + subbrute_send_packet(context); + + if(context->payload == max_value) { + context->payload = 0x00; + subbrute_counter = 0; + context->is_attacking = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + } else { + context->payload++; + } + locked = false; + //} + /*if(subbrute_counter > SUBBRUTE_DELAY) { + subbrute_counter = 0; + } else { + subbrute_counter++; + }*/ +} +void subbrute_run_timer(SubBruteState* context) { + while(true) { + if(context->close_thread_please) { + context->is_thread_running = false; + break; + } + //furi_delay_ms(10); + subbrute_scene_run_attack_on_tick(context); + } +} + +// entrypoint for worker +static int32_t subbrute_worker_thread(void* ctx) { + SubBruteState* app = ctx; + subbrute_run_timer(app); + return 0; +} + +void start_bruthread(SubBruteState* app) { + if(!app->is_thread_running) { + furi_thread_start(app->bruthread); + app->is_thread_running = true; + } +} + +void subbrute_scene_run_attack_on_enter(SubBruteState* context) { + if(!toSave) { + if(context->attack == SubBruteAttackLoadFile) { + max_value = 0xFF; + } else { + string_t max_value_s; + string_init(max_value_s); + for(uint8_t i = 0; i < context->bit; i++) { + string_cat_printf(max_value_s, "1"); + } + max_value = (uint64_t)strtol(string_get_cstr(max_value_s), NULL, 2); + string_clear(max_value_s); + } + context->str_index = (context->key_index * 3); + string_init_set(context->candidate, context->key); + context->flipper_format = flipper_format_string_alloc(); + context->stream = flipper_format_get_raw_stream(context->flipper_format); + context->environment = subghz_environment_alloc(); + + prepare_emit(context); + context->bruthread = furi_thread_alloc(); + furi_thread_set_name(context->bruthread, "SubBrute Worker"); + furi_thread_set_stack_size(context->bruthread, 2048); + furi_thread_set_context(context->bruthread, context); + furi_thread_set_callback(context->bruthread, subbrute_worker_thread); + } else { + toSave = false; + } +} + +void subbrute_scene_run_attack_on_event(SubBruteEvent event, SubBruteState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + break; + case InputKeyUp: + if(!context->is_attacking) { + toSave = true; + context->current_scene = SceneSaveName; + } + break; + case InputKeyLeft: + if(!context->is_attacking && context->payload > 0x00) { + context->payload--; + subbrute_send_packet(context); + notification_message(context->notify, &sequence_blink_blue_10); + } + break; + case InputKeyRight: + if(!context->is_attacking && context->payload < max_value) { + context->payload++; + subbrute_send_packet(context); + notification_message(context->notify, &sequence_blink_blue_10); + } + break; + case InputKeyOk: + if(!context->is_attacking) { + context->is_attacking = true; + start_bruthread(context); + notification_message(context->notify, &sequence_blink_start_blue); + } else { + context->is_attacking = false; + context->close_thread_please = true; + if(context->is_thread_running && context->bruthread) { + furi_thread_join(context->bruthread); // wait until thread is finished + } + context->close_thread_please = false; + notification_message(context->notify, &sequence_blink_stop); + notification_message(context->notify, &sequence_single_vibro); + } + break; + case InputKeyBack: + locked = false; + context->close_thread_please = true; + context->is_attacking = false; + if(context->is_thread_running && context->bruthread) { + furi_thread_join(context->bruthread); // wait until thread is finished + } + context->close_thread_please = false; + string_reset(context->notification_msg); + context->payload = 0x00; + subbrute_counter = 0; + notification_message(context->notify, &sequence_blink_stop); + if(context->attack == SubBruteAttackLoadFile) { + context->current_scene = SceneSelectField; + } else { + context->current_scene = SceneEntryPoint; + } + break; + } + } + } +} + +void subbrute_scene_run_attack_on_draw(Canvas* canvas, SubBruteState* context) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Frame + //canvas_draw_frame(canvas, 0, 0, 128, 64); + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignTop, "Fire in the hole!"); + + char msg_index[26]; + snprintf( + msg_index, sizeof(msg_index), "< %04d / %04d >", (int)context->payload, (int)max_value); + + canvas_draw_str_aligned(canvas, 64, 24, AlignCenter, AlignTop, msg_index); + + canvas_set_font(canvas, FontSecondary); + char start_stop_msg[20]; + snprintf(start_stop_msg, sizeof(start_stop_msg), " Press (^) to save "); + if(context->is_attacking) { + elements_button_center(canvas, "Stop"); + } else { + elements_button_center(canvas, "Start"); + } + canvas_draw_str_aligned(canvas, 64, 39, AlignCenter, AlignTop, start_stop_msg); +} diff --git a/applications/subbrute/scene/subbrute_scene_run_attack.h b/applications/subbrute/scene/subbrute_scene_run_attack.h new file mode 100644 index 0000000000..1eb9637d02 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_run_attack.h @@ -0,0 +1,8 @@ +#include "../subbrute.h" + +void subbrute_scene_run_attack_on_enter(SubBruteState* context); +void subbrute_scene_run_attack_on_exit(SubBruteState* context); +void subbrute_scene_run_attack_on_tick(SubBruteState* context); +void subbrute_scene_run_attack_on_event(SubBruteEvent event, SubBruteState* context); +void subbrute_scene_run_attack_on_draw(Canvas* canvas, SubBruteState* context); +void send_packet(); \ No newline at end of file diff --git a/applications/subbrute/scene/subbrute_scene_save_name.c b/applications/subbrute/scene/subbrute_scene_save_name.c new file mode 100644 index 0000000000..e79cf70ed5 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_save_name.c @@ -0,0 +1,222 @@ +#include "../subbrute.h" +#include "m-string.h" +#include "subghz/types.h" +#include +#include +#include + +#define MAX_TEXT_INPUT_LEN 22 + +bool backpressed = false; + +bool subbrute_path_is_file(string_t path) { + return string_end_with_str_p(path, ".sub"); +} +// method modified from subghz_i.c +// https://github.com/flipperdevices/flipperzero-firmware/blob/b0daa601ad5b87427a45f9089c8b403a01f72c2a/applications/subghz/subghz_i.c#L417-L456 +bool subbrute_save_protocol_to_file(Stream* flipper_format_stream, const char* dev_file_name) { + furi_assert(dev_file_name); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + bool saved = false; + string_t file_dir; + string_init(file_dir); + + path_extract_dirname(dev_file_name, file_dir); + do { + if(!storage_simply_mkdir(storage, string_get_cstr(file_dir))) { + FURI_LOG_E(TAG, "(save) Cannot mkdir"); + break; + } + + if(!storage_simply_remove(storage, dev_file_name)) { + FURI_LOG_E(TAG, "(save) Cannot remove"); + break; + } + + stream_seek(flipper_format_stream, 0, StreamOffsetFromStart); + stream_save_to_file(flipper_format_stream, storage, dev_file_name, FSOM_CREATE_ALWAYS); + + saved = true; + FURI_LOG_D(TAG, "(save) OK Save"); + } while(0); + string_clear(file_dir); + furi_record_close(RECORD_STORAGE); + return saved; +} + +void custom_callback(SubBruteState* context) { + if(strcmp(context->file_name_tmp, "")) { + string_cat_printf(context->file_path, "/%s%s", context->file_name_tmp, ".sub"); + if(subbrute_path_is_file(context->file_path_tmp)) { + context->current_scene = SceneAttack; + return; //false; + + } else { + subbrute_save_protocol_to_file(context->stream, string_get_cstr(context->file_path)); + } + + string_set_str(context->file_path, EXT_PATH("subghz")); + string_reset(context->file_path_tmp); + + //scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess); + context->current_scene = SceneAttack; + return; //true; + } else { + //error no file name + context->current_scene = SceneAttack; + return; //true; + } +} + +void subbrute_scene_save_name_text_input_callback(void* context) { + furi_assert(context); + SubBruteState* statee = context; + custom_callback(statee); +} + +void subbrute_scene_save_name_on_tick(SubBruteState* context) { + if(backpressed) { + void* validator_context = text_input_get_validator_callback_context(context->text_input); + text_input_set_validator(context->text_input, NULL, NULL); + validator_is_file_free(validator_context); + + // Clear view + text_input_reset(context->text_input); + + // TextInput + view_dispatcher_remove_view(context->view_dispatcher, 0); + text_input_free(context->text_input); + + // Popup + view_dispatcher_remove_view(context->view_dispatcher, 1); + popup_free(context->popup); + + context->current_scene = SceneAttack; + } +} + +bool subbrute_back_event_callback(void* context) { + UNUSED(context); + backpressed = true; + return true; +} + +void subbrute_scene_save_name_on_enter(SubBruteState* context) { + // Text Input + context->text_input = text_input_alloc(); + view_dispatcher_add_view( + context->view_dispatcher, 0, text_input_get_view(context->text_input)); + + // Popup + context->popup = popup_alloc(); + view_dispatcher_add_view(context->view_dispatcher, 1, popup_get_view(context->popup)); + + // Setup view + TextInput* text_input = context->text_input; + bool dev_name_empty = false; + + string_t file_name; + string_t dir_name; + string_init(file_name); + string_init(dir_name); + + if(!subbrute_path_is_file(context->file_path)) { + char file_name_buf[64] = {0}; + set_random_name(file_name_buf, 64); + string_set_str(file_name, file_name_buf); + string_set_str(context->file_path, EXT_PATH("subghz")); + //highlighting the entire filename by default + dev_name_empty = true; + } else { + string_set(context->file_path_tmp, context->file_path); + path_extract_dirname(string_get_cstr(context->file_path), dir_name); + path_extract_filename(context->file_path, file_name, true); + string_set(context->file_path, dir_name); + } + + strncpy(context->file_name_tmp, string_get_cstr(file_name), 64); + text_input_set_header_text(text_input, "Name signal"); + text_input_set_result_callback( + text_input, + subbrute_scene_save_name_text_input_callback, + context, + context->file_name_tmp, + MAX_TEXT_INPUT_LEN, // buffer size + dev_name_empty); + + ValidatorIsFile* validator_is_file = + validator_is_file_alloc_init(string_get_cstr(context->file_path), ".sub", ""); + text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + + string_clear(file_name); + string_clear(dir_name); + + view_dispatcher_set_navigation_event_callback( + context->view_dispatcher, subbrute_back_event_callback); + + view_dispatcher_switch_to_view(context->view_dispatcher, 0); +} + +void subbrute_scene_save_name_on_event(SubBruteEvent event, SubBruteState* context) { + UNUSED(context); + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + switch(event.key) { + case InputKeyDown: + case InputKeyUp: + case InputKeyLeft: + case InputKeyRight: + case InputKeyOk: + break; + case InputKeyBack: + //context->current_scene = SceneAttack; + break; + } + } + } +} + +void subbrute_scene_save_name_on_exit(SubBruteState* context) { + if(!backpressed) { + // Clear validator + void* validator_context = text_input_get_validator_callback_context(context->text_input); + text_input_set_validator(context->text_input, NULL, NULL); + validator_is_file_free(validator_context); + + // Clear view + text_input_reset(context->text_input); + + // Setup view + Popup* popup = context->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, context); + popup_set_callback(popup, NULL); + popup_enable_timeout(popup); + view_dispatcher_switch_to_view(context->view_dispatcher, 1); + + furi_delay_ms(1050); + // Clear view + //Popup* popup = subghz->popup; + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + popup_set_icon(popup, 0, 0, NULL); + popup_set_callback(popup, NULL); + popup_set_context(popup, NULL); + popup_set_timeout(popup, 0); + popup_disable_timeout(popup); + + // TextInput + view_dispatcher_remove_view(context->view_dispatcher, 0); + text_input_free(context->text_input); + + // Popup + view_dispatcher_remove_view(context->view_dispatcher, 1); + popup_free(context->popup); + } else { + backpressed = false; + } +} \ No newline at end of file diff --git a/applications/subbrute/scene/subbrute_scene_save_name.h b/applications/subbrute/scene/subbrute_scene_save_name.h new file mode 100644 index 0000000000..18a931ad89 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_save_name.h @@ -0,0 +1,6 @@ +#include "../subbrute.h" + +void subbrute_scene_save_name_on_enter(SubBruteState* context); +void subbrute_scene_save_name_on_exit(SubBruteState* context); +void subbrute_scene_save_name_on_event(SubBruteEvent event, SubBruteState* context); +void subbrute_scene_save_name_on_tick(SubBruteState* context); \ No newline at end of file diff --git a/applications/subbrute/scene/subbrute_scene_select_field.c b/applications/subbrute/scene/subbrute_scene_select_field.c new file mode 100644 index 0000000000..c65cd16639 --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_select_field.c @@ -0,0 +1,121 @@ +#include "subbrute_scene_select_field.h" + +void center_displayed_key(SubBruteState* context, uint8_t index) { + const char* key_cstr = string_get_cstr(context->key); + uint8_t str_index = (index * 3); + + char display_menu[17] = { + 'X', 'X', ' ', 'X', 'X', ' ', '<', 'X', 'X', '>', ' ', 'X', 'X', ' ', 'X', 'X', '\0'}; + + if(index > 1) { + display_menu[0] = key_cstr[str_index - 6]; + display_menu[1] = key_cstr[str_index - 5]; + } else { + display_menu[0] = ' '; + display_menu[1] = ' '; + } + + if(index > 0) { + display_menu[3] = key_cstr[str_index - 3]; + display_menu[4] = key_cstr[str_index - 2]; + } else { + display_menu[3] = ' '; + display_menu[4] = ' '; + } + + display_menu[7] = key_cstr[str_index]; + display_menu[8] = key_cstr[str_index + 1]; + + if((str_index + 4) <= (uint8_t)strlen(key_cstr)) { + display_menu[11] = key_cstr[str_index + 3]; + display_menu[12] = key_cstr[str_index + 4]; + } else { + display_menu[11] = ' '; + display_menu[12] = ' '; + } + + if((str_index + 8) <= (uint8_t)strlen(key_cstr)) { + display_menu[14] = key_cstr[str_index + 6]; + display_menu[15] = key_cstr[str_index + 7]; + } else { + display_menu[14] = ' '; + display_menu[15] = ' '; + } + + string_reset(context->notification_msg); + string_set_str(context->notification_msg, display_menu); +} + +void subbrute_scene_select_field_on_enter(SubBruteState* context) { + string_clear(context->notification_msg); +} + +void subbrute_scene_select_field_on_exit(SubBruteState* context) { + UNUSED(context); +} + +void subbrute_scene_select_field_on_tick(SubBruteState* context) { + UNUSED(context); +} + +void subbrute_scene_select_field_on_event(SubBruteEvent event, SubBruteState* context) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + //const char* key_cstr = string_get_cstr(context->key); + + // don't look, it's ugly but I'm a python dev so... + /*uint8_t nb_bytes = 0; + for(uint8_t i = 0; i < strlen(key_cstr); i++) { + if(' ' == key_cstr[i]) { + nb_bytes++; + } + }*/ + + switch(event.key) { + case InputKeyDown: + case InputKeyUp: + break; + case InputKeyLeft: + if(context->key_index > 0) { + context->key_index--; + } + break; + case InputKeyRight: + if(context->key_index < 7) { + context->key_index++; + } + break; + case InputKeyOk: + string_reset(context->notification_msg); + context->current_scene = SceneAttack; + break; + case InputKeyBack: + string_reset(context->notification_msg); + context->current_scene = SceneSelectFile; + break; + } + //FURI_LOG_D(TAG, "Position: %d/%d", context->key_index, nb_bytes); + } + } +} + +void subbrute_scene_select_field_on_draw(Canvas* canvas, SubBruteState* context) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Frame + //canvas_draw_frame(canvas, 0, 0, 128, 64); + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "use < > to select field"); + + char msg_index[18]; + snprintf(msg_index, sizeof(msg_index), "Field index : %d", context->key_index); + canvas_draw_str_aligned(canvas, 64, 26, AlignCenter, AlignTop, msg_index); + + center_displayed_key(context, context->key_index); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, 64, 40, AlignCenter, AlignTop, string_get_cstr(context->notification_msg)); +} diff --git a/applications/subbrute/scene/subbrute_scene_select_field.h b/applications/subbrute/scene/subbrute_scene_select_field.h new file mode 100644 index 0000000000..e02a07ee0d --- /dev/null +++ b/applications/subbrute/scene/subbrute_scene_select_field.h @@ -0,0 +1,8 @@ +#include "../subbrute.h" + +void subbrute_scene_select_field_on_enter(SubBruteState* context); +void subbrute_scene_select_field_on_exit(SubBruteState* context); +void subbrute_scene_select_field_on_tick(SubBruteState* context); +void subbrute_scene_select_field_on_event(SubBruteEvent event, SubBruteState* context); +void subbrute_scene_select_field_on_draw(Canvas* canvas, SubBruteState* context); +void center_displayed_key(SubBruteState* context, uint8_t index); \ No newline at end of file diff --git a/applications/subbrute/subbrute.c b/applications/subbrute/subbrute.c new file mode 100644 index 0000000000..bf5c2295d0 --- /dev/null +++ b/applications/subbrute/subbrute.c @@ -0,0 +1,269 @@ +#include "subbrute.h" + +#include "scene/subbrute_scene_load_file.h" +#include "scene/subbrute_scene_select_field.h" +#include "scene/subbrute_scene_run_attack.h" +#include "scene/subbrute_scene_entrypoint.h" +#include "scene/subbrute_scene_save_name.h" + +static void draw_callback(Canvas* const canvas, void* ctx) { + SubBruteState* subbrute_state = (SubBruteState*)acquire_mutex((ValueMutex*)ctx, 100); + + if(subbrute_state == NULL) { + return; + } + + // Draw correct Canvas + switch(subbrute_state->current_scene) { + case NoneScene: + case SceneSelectFile: + subbrute_scene_load_file_on_draw(canvas, subbrute_state); + break; + case SceneSelectField: + subbrute_scene_select_field_on_draw(canvas, subbrute_state); + break; + case SceneAttack: + subbrute_scene_run_attack_on_draw(canvas, subbrute_state); + break; + case SceneEntryPoint: + subbrute_scene_entrypoint_on_draw(canvas, subbrute_state); + break; + case SceneSaveName: + break; + } + + release_mutex((ValueMutex*)ctx, subbrute_state); +} + +void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + SubBruteEvent event = { + .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; + furi_message_queue_put(event_queue, &event, 100); +} + +static void timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + SubBruteEvent event = { + .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; + furi_message_queue_put(event_queue, &event, 100); +} + +SubBruteState* subbrute_alloc() { + SubBruteState* subbrute = malloc(sizeof(SubBruteState)); + + string_init(subbrute->protocol); + string_init(subbrute->preset); + string_init(subbrute->file_path); + string_init(subbrute->file_path_tmp); + string_init_set(subbrute->notification_msg, ""); + string_init(subbrute->candidate); + string_init(subbrute->flipper_format_string); + + subbrute->previous_scene = NoneScene; + subbrute->current_scene = SceneSelectFile; + subbrute->is_running = true; + subbrute->is_attacking = false; + subbrute->key_index = 7; + subbrute->notify = furi_record_open(RECORD_NOTIFICATION); + + subbrute->view_dispatcher = view_dispatcher_alloc(); + + //Dialog + subbrute->dialogs = furi_record_open(RECORD_DIALOGS); + + subbrute->preset_def = malloc(sizeof(SubGhzPresetDefinition)); + + subbrute->flipper_format = flipper_format_string_alloc(); + subbrute->environment = subghz_environment_alloc(); + subbrute->receiver = subghz_receiver_alloc_init(subbrute->environment); + subghz_receiver_set_filter(subbrute->receiver, SubGhzProtocolFlag_Decodable); + + return subbrute; +} + +void subbrute_free(SubBruteState* subbrute) { + //Dialog + furi_record_close(RECORD_DIALOGS); + + notification_message(subbrute->notify, &sequence_blink_stop); + + furi_record_close(RECORD_NOTIFICATION); + + view_dispatcher_free(subbrute->view_dispatcher); + + string_clear(subbrute->preset); + string_clear(subbrute->candidate); + + // Path strings + string_clear(subbrute->file_path); + string_clear(subbrute->file_path_tmp); + string_clear(subbrute->notification_msg); + string_clear(subbrute->candidate); + string_clear(subbrute->flipper_format_string); + + flipper_format_free(subbrute->flipper_format); + subghz_environment_free(subbrute->environment); + subghz_receiver_free(subbrute->receiver); + + free(subbrute->preset_def); + + // The rest + free(subbrute); +} + +// ENTRYPOINT +int32_t subbrute_start(void* p) { + UNUSED(p); + // Input + FURI_LOG_I(TAG, "Initializing input"); + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(SubBruteEvent)); + SubBruteState* subbrute_state = subbrute_alloc(); + ValueMutex subbrute_state_mutex; + + // Mutex + FURI_LOG_I(TAG, "Initializing flipfrid mutex"); + if(!init_mutex(&subbrute_state_mutex, subbrute_state, sizeof(SubBruteState))) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + furi_message_queue_free(event_queue); + subbrute_free(subbrute_state); + return 255; + } + + furi_hal_power_suppress_charge_enter(); + + // Configure view port + FURI_LOG_I(TAG, "Initializing viewport"); + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, &subbrute_state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Configure timer + FURI_LOG_I(TAG, "Initializing timer"); + FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // 10 times per second + + // Register view port in GUI + FURI_LOG_I(TAG, "Initializing gui"); + subbrute_state->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(subbrute_state->gui, view_port, GuiLayerFullscreen); + + view_dispatcher_attach_to_gui( + subbrute_state->view_dispatcher, subbrute_state->gui, ViewDispatcherTypeFullscreen); + + subbrute_state->current_scene = SceneEntryPoint; + + // Init values + SubBruteEvent event; + while(subbrute_state->is_running) { + // Get next event + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); + if(event_status == FuriStatusOk) { + if(event.evt_type == EventTypeKey) { + //Handle event key + FURI_LOG_D(TAG, "EVENT ###"); + switch(subbrute_state->current_scene) { + case SceneSelectFile: + subbrute_scene_load_file_on_event(event, subbrute_state); + break; + case SceneSelectField: + subbrute_scene_select_field_on_event(event, subbrute_state); + break; + case SceneSaveName: + subbrute_scene_save_name_on_event(event, subbrute_state); + break; + case SceneAttack: + subbrute_scene_run_attack_on_event(event, subbrute_state); + break; + case NoneScene: + case SceneEntryPoint: + subbrute_scene_entrypoint_on_event(event, subbrute_state); + break; + } + + } else if(event.evt_type == EventTypeTick) { + //Handle event tick + if(subbrute_state->current_scene != subbrute_state->previous_scene) { + // Trigger Exit Scene + switch(subbrute_state->previous_scene) { + case SceneSelectFile: + subbrute_scene_load_file_on_exit(subbrute_state); + break; + case SceneSelectField: + subbrute_scene_select_field_on_exit(subbrute_state); + break; + case SceneAttack: + subbrute_scene_run_attack_on_exit(subbrute_state); + break; + case SceneEntryPoint: + subbrute_scene_entrypoint_on_exit(subbrute_state); + break; + case SceneSaveName: + subbrute_scene_save_name_on_exit(subbrute_state); + break; + case NoneScene: + break; + } + + // Trigger Entry Scene + switch(subbrute_state->current_scene) { + case NoneScene: + case SceneSelectFile: + subbrute_scene_load_file_on_enter(subbrute_state); + break; + case SceneSelectField: + subbrute_scene_select_field_on_enter(subbrute_state); + break; + case SceneAttack: + subbrute_scene_run_attack_on_enter(subbrute_state); + break; + case SceneSaveName: + subbrute_scene_save_name_on_enter(subbrute_state); + break; + case SceneEntryPoint: + subbrute_scene_entrypoint_on_enter(subbrute_state); + break; + } + subbrute_state->previous_scene = subbrute_state->current_scene; + } + + // Trigger Tick Scene + switch(subbrute_state->current_scene) { + case NoneScene: + case SceneSelectFile: + subbrute_scene_load_file_on_tick(subbrute_state); + break; + case SceneSelectField: + subbrute_scene_select_field_on_tick(subbrute_state); + break; + case SceneAttack: + //subbrute_scene_run_attack_on_tick(subbrute_state); + break; + case SceneEntryPoint: + subbrute_scene_entrypoint_on_tick(subbrute_state); + break; + case SceneSaveName: + subbrute_scene_save_name_on_tick(subbrute_state); + break; + } + view_port_update(view_port); + } + } + } + + // Cleanup + furi_timer_stop(timer); + furi_timer_free(timer); + + furi_hal_power_suppress_charge_exit(); + + FURI_LOG_I(TAG, "Cleaning up"); + gui_remove_view_port(subbrute_state->gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_record_close(RECORD_GUI); + subbrute_free(subbrute_state); + + return 0; +} \ No newline at end of file diff --git a/applications/subbrute/subbrute.h b/applications/subbrute/subbrute.h new file mode 100644 index 0000000000..b6389a9100 --- /dev/null +++ b/applications/subbrute/subbrute.h @@ -0,0 +1,109 @@ +#pragma once +#include +#include +#include +#include +#include "m-string.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "SUBBRUTE" + +typedef enum { + NoneScene, + SceneSelectFile, + SceneSelectField, + SceneAttack, + SceneEntryPoint, + SceneSaveName +} SubBruteScene; + +typedef enum { + SubBruteAttackLoadFile, + SubBruteAttackCAME12bit433, + SubBruteAttackCAME12bit868, + SubBruteAttackChamberlain9bit315, + SubBruteAttackChamberlain9bit390, + SubBruteAttackLinear10bit300, + SubBruteAttackLinear10bit310, + SubBruteAttackNICE12bit433, + SubBruteAttackNICE12bit868, +} SubBruteAttacks; + +typedef enum { + EventTypeTick, + EventTypeKey, + EventTypeCustom, +} EventType; + +typedef struct { + EventType evt_type; + InputKey key; + InputType input_type; +} SubBruteEvent; + +// STRUCTS +typedef struct { + // Application stuff + bool is_running; + bool is_attacking; + bool is_thread_running; + bool close_thread_please; + SubBruteScene current_scene; + SubBruteScene previous_scene; + NotificationApp* notify; + Gui* gui; + ViewDispatcher* view_dispatcher; + TextInput* text_input; + Popup* popup; + + // SubGhz Stuff + FuriThread* bruthread; + FlipperFormat* flipper_format; + SubGhzEnvironment* environment; + SubGhzTransmitter* transmitter; + SubGhzReceiver* receiver; + SubGhzProtocolDecoderBase* decoder_result; + SubGhzPresetDefinition* preset_def; + string_t preset; + Stream* stream; + string_t protocol; + uint32_t frequency; + uint32_t frequency_cal; + uint32_t repeat; + uint32_t bit; + string_t key; + uint32_t te; + + // Context Stuff + DialogsApp* dialogs; + char file_name_tmp[64]; + string_t file_path; + string_t file_path_tmp; + string_t notification_msg; + uint8_t key_index; + uint64_t payload; + string_t candidate; + uint8_t str_index; + string_t flipper_format_string; + + SubBruteAttacks attack; + + //Menu stuff + uint8_t menu_index; + + // RAW stuff + string_t subbrute_raw_one; + string_t subbrute_raw_zero; + string_t subbrute_raw_stop; + +} SubBruteState; \ No newline at end of file diff --git a/applications/subbrute/subbrute_utils.c b/applications/subbrute/subbrute_utils.c new file mode 100644 index 0000000000..2aafc7175e --- /dev/null +++ b/applications/subbrute/subbrute_utils.c @@ -0,0 +1,13 @@ +#include "subbrute_utils.h" + +bool subbrute_is_frequency_allowed(SubBruteState* context) { + // I know you don't like it but laws are laws + // It's opensource so do whatever you want, but remember the risks :) + // (Yes, this comment is the only purpose of this function) + bool r = furi_hal_subghz_is_tx_allowed(context->frequency); + if(!r) { + FURI_LOG_E(TAG, "Frequency %d is not allowed in your region", context->frequency); + notification_message(context->notify, &sequence_single_vibro); + } + return r; +} \ No newline at end of file diff --git a/applications/subbrute/subbrute_utils.h b/applications/subbrute/subbrute_utils.h new file mode 100644 index 0000000000..90f7c60adb --- /dev/null +++ b/applications/subbrute/subbrute_utils.h @@ -0,0 +1,4 @@ +#pragma once +#include "subbrute.h" + +bool subbrute_is_frequency_allowed(SubBruteState* context); \ No newline at end of file diff --git a/applications/unit_tests/subghz/subghz_test.c b/applications/unit_tests/subghz/subghz_test.c index 6345b758fd..df269ed046 100644 --- a/applications/unit_tests/subghz/subghz_test.c +++ b/applications/unit_tests/subghz/subghz_test.c @@ -13,7 +13,7 @@ #define CAME_ATOMO_DIR_NAME EXT_PATH("subghz/assets/came_atomo") #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 208 +#define TEST_RANDOM_COUNT_PARSE 232 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -427,6 +427,13 @@ MU_TEST(subghz_decoder_intertechno_v3_test) { "Test decoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n"); } +MU_TEST(subghz_decoder_clemsa_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/clemsa_raw.sub"), SUBGHZ_PROTOCOL_CLEMSA_NAME), + "Test decoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -542,6 +549,12 @@ MU_TEST(subghz_encoder_intertechno_v3_test) { "Test encoder " SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME " error\r\n"); } +MU_TEST(subghz_encoder_clemsa_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/clemsa.sub")), + "Test encoder " SUBGHZ_PROTOCOL_CLEMSA_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -581,6 +594,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_honeywell_wdb_test); MU_RUN_TEST(subghz_decoder_magellen_test); MU_RUN_TEST(subghz_decoder_intertechno_v3_test); + MU_RUN_TEST(subghz_decoder_clemsa_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -601,6 +615,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_honeywell_wdb_test); MU_RUN_TEST(subghz_encoder_magellen_test); MU_RUN_TEST(subghz_encoder_intertechno_v3_test); + MU_RUN_TEST(subghz_encoder_clemsa_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/applications/wav_player/application.fam b/applications/wav_player/application.fam index 9a1f48566c..609c702f8b 100644 --- a/applications/wav_player/application.fam +++ b/applications/wav_player/application.fam @@ -1,9 +1,9 @@ App( appid="wav_player", name="WAV Player", - apptype=FlipperAppType.PLUGIN, + apptype=FlipperAppType.GAME, entry_point="wav_player_app", cdefines=["APP_WAV_PLAYER"], stack_size=4 * 1024, - order=21, + order=46, ) diff --git a/assets/icons/Common/Round_loader_8x8/frame_01.png b/assets/icons/Common/Round_loader_8x8/frame_01.png new file mode 100644 index 0000000000..a5dc239d85 Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_01.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_02.png b/assets/icons/Common/Round_loader_8x8/frame_02.png new file mode 100644 index 0000000000..162d8a8f42 Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_02.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_03.png b/assets/icons/Common/Round_loader_8x8/frame_03.png new file mode 100644 index 0000000000..5483e47345 Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_03.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_04.png b/assets/icons/Common/Round_loader_8x8/frame_04.png new file mode 100644 index 0000000000..ce2fbbd476 Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_04.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_05.png b/assets/icons/Common/Round_loader_8x8/frame_05.png new file mode 100644 index 0000000000..8b786c0293 Binary files /dev/null and b/assets/icons/Common/Round_loader_8x8/frame_05.png differ diff --git a/assets/icons/Common/Round_loader_8x8/frame_rate b/assets/icons/Common/Round_loader_8x8/frame_rate new file mode 100644 index 0000000000..d8263ee986 --- /dev/null +++ b/assets/icons/Common/Round_loader_8x8/frame_rate @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/assets/icons/NFC/Reader_detect.png b/assets/icons/NFC/ArrowC_1_36x36.png similarity index 84% rename from assets/icons/NFC/Reader_detect.png rename to assets/icons/NFC/ArrowC_1_36x36.png index 56d3663eaa..3a0c6dd0cb 100644 Binary files a/assets/icons/NFC/Reader_detect.png and b/assets/icons/NFC/ArrowC_1_36x36.png differ diff --git a/assets/icons/NFC/Tap_reader_36x38.png b/assets/icons/NFC/Tap_reader_36x38.png new file mode 100644 index 0000000000..4e0ba8f059 Binary files /dev/null and b/assets/icons/NFC/Tap_reader_36x38.png differ diff --git a/assets/icons/RFID/RFIDBigChip_37x36.png b/assets/icons/RFID/RFIDBigChip_37x36.png deleted file mode 100644 index a60d51d585..0000000000 Binary files a/assets/icons/RFID/RFIDBigChip_37x36.png and /dev/null differ diff --git a/assets/icons/RFID/RFIDSmallChip_14x14.png b/assets/icons/RFID/RFIDSmallChip_14x14.png new file mode 100644 index 0000000000..24219a548e Binary files /dev/null and b/assets/icons/RFID/RFIDSmallChip_14x14.png differ diff --git a/assets/resources/Manifest b/assets/resources/Manifest index 4fbac9f549..0fa51d9c28 100644 --- a/assets/resources/Manifest +++ b/assets/resources/Manifest @@ -1,10 +1,11 @@ V:0 -T:1661895856 +T:1662677298 D:badusb D:dolphin D:infrared D:music_player D:nfc +D:rfidfuzzer D:subghz D:u2f D:unirf @@ -241,17 +242,18 @@ F:33b8fde22f34ef556b64b77164bc19b0:578:dolphin/L3_Lab_research_128x54/frame_8.bm F:f267f0654781049ca323b11bb4375519:581:dolphin/L3_Lab_research_128x54/frame_9.bm F:41106c0cbc5144f151b2b2d3daaa0527:727:dolphin/L3_Lab_research_128x54/meta.txt D:infrared/assets -F:13fe3def425723bccd05fe09c745a335:122232:infrared/assets/ac.ir -F:a3dad0916846fef907c93a65dd8e331d:52540:infrared/assets/audio.ir -F:1703fea41cb6ef71553b91a1004dc936:82397:infrared/assets/fans.ir -F:08d864cf44e2557fb25aec8837740de5:6567:infrared/assets/projectors.ir -F:77bc8314d113b8618942589f21a491fc:127350:infrared/assets/tv.ir +F:5e55f221320c8f0bbeafd5df59673e75:139785:infrared/assets/ac.ir +F:1720722745039a27440e62ea08db7f3a:60611:infrared/assets/audio.ir +F:faa49194e6ba4e5bc1ae5b2f90909d29:87512:infrared/assets/fans.ir +F:9fea10aa40ce414753b45449acd8a2d0:6910:infrared/assets/projectors.ir +F:a377e1f74292ce3ccde7aebe5b41e81b:140936:infrared/assets/tv.ir F:a157a80f5a668700403d870c23b9567d:470:music_player/Marble_Machine.fmf D:nfc/assets F:81dc04c7b181f94b644079a71476dff4:4742:nfc/assets/aid.nfc F:86efbebdf41bb6bf15cc51ef88f069d5:2565:nfc/assets/country_code.nfc F:41b4f08774249014cb8d3dffa5f5c07d:1757:nfc/assets/currency_code.nfc F:5f57e2ecfc850efb74c7d2677eb75a2e:51832:nfc/assets/mf_classic_dict.nfc +F:319958d37b2316a2e752ebb856c32524:126:rfidfuzzer/example_uids.txt D:subghz/assets F:dda1ef895b8a25fde57c874feaaef997:650:subghz/assets/came_atomo F:50cf77ba8b935ee6cb3b6f111cf2d93d:286:subghz/assets/dangerous_settings diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index fa5d5a372b..1f7b42d235 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,6 +1,43 @@ Filetype: IR library file Version: 1 -# Last Updated 30th Aug, 2022 +# Last Updated 7th Sep, 2022 +# +# TIMER ON +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4421 4371 559 1622 564 530 563 1622 564 1617 559 562 541 553 540 1615 561 561 542 528 565 1646 540 528 565 555 538 1643 543 1622 530 563 1650 536 560 533 562 541 527 566 1641 535 1620 566 1614 562 559 534 1627 559 1627 559 1621 565 1642 534 534 559 562 541 527 566 1619 567 529 564 532 561 1650 536 532 561 1623 563 1618 558 536 567 554 539 531 562 560 533 561 532 1623 563 557 536 1623 563 1617 559 1649 537 1618 558 5224 4420 4368 562 1618 568 552 541 1618 558 1649 537 531 562 532 561 1620 566 530 563 533 560 1625 561 533 560 560 533 1622 564 1617 559 535 558 1629 568 528 565 556 537 557 536 1619 567 1613 563 1618 558 536 567 1619 567 1620 566 1615 561 1620 566 528 565 555 538 530 563 1622 564 558 535 536 567 1617 559 535 568 1643 533 1622 564 531 562 533 560 536 567 555 538 557 536 1619 567 527 566 1645 541 1614 562 1619 567 1617 559 +# TIMER ON +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4407 4355 571 1574 563 509 564 1581 566 506 567 505 568 530 543 528 545 1574 563 1583 564 508 565 506 567 505 568 503 570 502 571 1573 564 509 564 507 566 1579 568 1603 544 1575 572 500 563 536 537 1581 566 506 567 1578 569 1576 571 1574 563 1583 564 1581 566 1580 567 1578 569 1576 571 1575 572 526 537 509 564 507 566 532 541 504 569 503 570 1574 563 510 563 508 565 1580 567 1578 569 503 570 1575 572 1573 564 509 564 5168 4411 4351 565 507 566 1579 568 504 569 1576 571 1574 573 1573 564 1581 566 507 566 505 568 1577 570 1575 572 1574 563 1582 565 1581 566 506 567 1578 569 1576 571 501 572 500 563 509 564 1580 567 1578 569 504 569 1576 571 501 562 510 563 508 565 507 566 531 542 530 543 502 571 527 536 510 563 1581 566 1579 568 1578 569 1576 571 1575 572 1573 564 509 564 1581 566 1579 568 504 569 503 570 1574 563 510 563 508 565 1580 567 +# SWING ON +name: SWING +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9811 9792 9816 9789 4617 2485 383 345 387 925 385 931 379 357 386 926 384 351 381 351 381 363 380 348 384 928 382 353 379 353 379 933 387 348 384 348 384 940 380 351 381 931 379 357 386 346 386 349 383 353 379 353 379 364 378 349 383 352 380 356 387 922 388 924 386 349 383 349 383 361 381 346 386 349 383 352 380 352 380 932 388 347 385 347 385 358 385 344 388 347 385 350 382 350 382 930 380 355 388 345 387 356 386 342 380 355 388 348 384 925 385 926 384 352 380 352 380 363 379 925 385 350 382 933 387 349 383 928 382 354 388 921 379 941 379 20334 4618 +# ON +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4415 4353 570 1577 565 507 569 1577 565 508 568 504 572 501 565 508 568 1578 564 1583 570 503 563 510 566 1580 573 1574 568 504 572 501 565 508 568 505 571 1576 566 506 570 503 563 510 566 506 570 503 573 499 567 1581 572 1575 567 1580 573 1574 568 1579 563 1583 570 1577 565 1581 572 1576 566 1580 573 1575 567 1579 563 1584 569 1578 564 1583 570 1577 565 508 568 1578 564 509 567 506 570 502 564 1583 570 1577 565 508 568 5166 4419 4352 571 501 565 1583 570 502 574 1573 569 1578 564 1582 571 1576 566 506 570 502 574 1573 569 1577 565 507 569 504 572 1574 568 1579 574 1572 570 1576 566 532 544 1576 566 1580 573 1574 568 1577 565 1581 572 1574 568 531 545 527 539 534 542 530 546 526 540 533 543 529 547 525 541 505 571 501 565 508 568 504 572 500 566 506 570 503 573 499 567 1578 564 508 568 1577 565 1580 573 1572 570 502 564 508 568 1577 565 +# ON +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3202 1591 383 411 383 1206 382 413 381 413 381 1207 381 412 382 1207 381 414 380 413 381 1209 379 1209 389 1199 389 406 387 1200 388 406 388 1201 387 1201 387 1202 386 409 385 409 385 411 383 409 385 1204 384 1205 383 411 383 1206 382 413 381 1206 382 1208 380 414 380 414 380 415 389 1198 379 416 388 1201 387 405 389 406 388 1201 387 1202 386 1202 386 408 386 1204 384 1206 382 410 384 1205 383 1207 381 1206 382 1207 381 1207 381 415 389 403 380 1208 380 415 389 405 389 405 389 406 388 1200 388 1201 387 1203 385 407 386 1202 386 1205 383 1203 385 1204 384 412 382 412 382 412 382 1206 382 413 381 413 381 414 380 413 381 1208 380 1208 380 415 389 1199 389 1200 388 1200 388 1201 387 1202 386 410 384 408 386 1202 386 409 385 410 384 411 383 410 384 411 383 1206 382 1206 382 1206 382 1208 380 1210 388 403 380 1210 388 1200 388 405 389 406 388 406 388 406 388 407 387 1203 385 408 386 409 385 1203 385 1203 385 410 384 1205 383 1207 381 411 383 1206 382 1206 382 413 381 414 380 1210 388 404 379 415 379 1209 389 405 389 406 388 1201 387 1201 387 1201 387 1202 386 1204 384 1203 385 1204 384 1205 383 412 382 412 382 413 381 412 382 412 382 414 380 414 380 413 381 414 380 1209 389 1199 389 1199 389 1200 388 1201 387 1201 387 408 386 1201 387 408 385 409 385 410 384 410 384 410 384 411 383 1205 383 +# ON +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4463 4417 539 1598 546 495 546 1591 543 1592 542 501 540 502 539 1597 547 496 545 496 545 1591 543 499 542 500 541 1594 540 1597 547 494 547 1590 544 497 544 499 542 1593 541 1596 538 1597 547 1589 545 1590 544 1593 541 1594 540 1597 547 494 547 496 545 496 545 497 544 497 544 499 542 500 541 500 541 1596 538 503 538 504 547 494 547 495 546 495 546 1590 544 1591 543 499 542 1593 541 1595 539 1596 538 1599 545 1589 545 5190 4441 4412 544 1591 543 500 541 1594 540 1596 538 503 538 505 546 1588 546 497 544 499 542 1592 542 500 541 500 541 1595 539 1595 539 504 547 1586 548 494 547 495 546 1589 545 1590 544 1592 542 1594 540 1597 547 1587 547 1589 545 1589 545 498 543 498 543 499 542 499 542 501 540 502 539 501 540 503 538 1596 538 504 547 493 537 505 546 495 546 494 547 1589 545 1590 544 497 544 1591 543 1593 541 1594 540 1597 547 1589 545 # name: TEMP+ type: raw @@ -751,7 +788,7 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3439 1755 439 1262 435 1286 432 438 441 423 456 418 441 1282 436 433 436 431 438 1285 433 1314 435 408 461 1293 435 408 440 426 433 1289 460 1296 412 455 414 1312 406 1319 409 429 440 429 461 1267 441 426 464 406 442 1281 437 430 439 427 432 436 464 405 433 432 437 458 411 456 434 422 437 428 462 412 436 428 441 422 457 411 437 456 413 454 405 436 433 434 435 427 432 435 465 404 434 435 434 431 459 413 435 429 440 422 437 433 457 1270 438 1288 461 410 438 426 464 404 434 1293 435 1315 413 423 436 1287 462 407 431 434 435 435 434 433 467 415 433 1318 410 1286 463 409 439 1284 455 1301 438 410 438 426 464 1262 456 1269 439 429 461 410 469 407 462 1268 440 1285 433 461 408 456 413 1284 465 1261 457 414 455 1271 468 1277 431 1321 438 405 433 460 409 430 439 426 464 407 462 407 441 450 409 434 435 430 439 452 438 431 438 405 464 405 464 405 464 409 460 409 439 430 439 427 442 423 436 460 430 415 433 431 438 425 434 444 435 456 413 432 437 427 442 425 434 436 464 429 430 439 430 417 442 428 441 428 462 411 437 452 438 405 433 434 435 456 413 427 442 425 434 455 414 427 432 435 434 433 436 427 463 406 432 437 442 449 430 1268 460 1265 463 1262 466 405 464 406 442 422 437 1289 439 428 441 17052 3577 1746 407 1321 407 1294 434 460 409 431 459 439 409 1288 440 429 440 431 438 1309 430 1272 456 415 464 1289 408 433 457 412 436 1289 439 1284 465 404 434 1292 436 1289 439 428 441 450 409 1315 413 427 442 451 408 1294 434 432 437 430 439 428 462 406 432 432 458 414 455 414 434 430 439 428 441 426 433 436 433 432 437 454 405 438 441 428 431 434 456 418 441 448 431 411 437 429 440 425 434 433 436 458 411 425 434 433 436 457 412 1287 462 1263 455 419 461 408 440 430 439 1310 429 1272 435 460 409 1288 440 429 461 408 441 425 465 405 464 407 441 1286 432 1291 437 432 437 1315 413 1314 414 450 440 406 442 1283 456 1270 458 413 456 413 456 413 435 1297 431 1294 465 404 434 433 436 1314 414 1285 433 433 436 1288 440 1285 464 1264 433 433 436 453 416 425 434 460 409 434 435 432 437 454 415 423 436 431 459 424 435 430 460 413 435 430 439 423 456 413 435 431 438 455 414 427 432 435 434 455 414 426 464 406 432 437 442 422 457 414 434 431 438 424 435 435 455 414 455 416 432 433 436 457 412 426 464 410 438 453 437 412 436 437 463 409 439 425 434 455 414 426 433 437 432 433 436 433 436 457 433 412 436 453 437 406 432 435 465 1266 462 1263 434 1318 410 426 464 410 438 426 433 1293 435 434 435 -# +# POWER OFF name: POWER type: raw frequency: 38000 @@ -955,3 +992,88 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 3063 3027 3062 4289 597 1680 571 565 576 1686 575 692 573 563 567 1694 567 569 572 1821 575 1686 596 554 566 569 572 696 569 567 574 562 568 568 573 694 571 565 576 560 601 1673 567 700 576 560 570 566 575 561 569 698 567 569 572 564 566 569 603 678 566 570 571 564 577 559 571 1821 575 562 568 567 574 562 568 700 596 1678 573 1688 573 563 567 700 576 561 569 1691 570 566 575 693 572 564 597 552 568 568 573 694 571 565 576 560 570 1690 571 697 568 567 574 563 598 551 569 698 567 569 572 564 566 569 572 696 569 567 574 562 568 567 594 710 576 1685 576 560 570 1691 570 1846 571 566 575 561 569 1692 569 567 594 +# OFF +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4468 4412 543 1593 541 502 539 1596 548 1589 545 496 545 498 543 1593 541 501 540 502 539 1598 546 495 546 497 544 1591 543 1594 540 501 539 1597 547 494 547 1590 544 1591 543 1594 540 1595 539 504 547 1588 546 1590 544 1593 540 501 539 503 538 503 538 505 546 1588 546 497 544 496 545 1592 542 1592 542 1594 540 501 539 503 538 503 538 505 546 494 547 496 545 496 545 497 544 1590 544 1593 541 1594 540 1597 547 1589 545 5188 4430 4421 544 1592 541 499 541 1595 539 1596 538 505 546 494 547 1589 545 497 544 496 545 1592 542 500 540 499 542 1594 540 1596 538 503 538 1599 545 497 544 1591 543 1593 541 1595 539 1596 538 504 547 1588 546 1588 546 1590 544 498 542 498 543 499 541 500 541 1593 541 501 540 502 539 1597 547 1586 548 1587 547 496 545 495 546 497 544 498 543 497 544 498 543 499 542 498 542 1592 542 1594 540 1595 539 1597 547 1589 545 +# AUTO MODE +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4456 4417 538 1597 547 494 547 1589 545 1590 544 498 543 499 541 1593 541 501 539 502 539 1595 539 501 539 502 538 1596 538 1595 538 503 538 1597 547 493 537 504 547 495 546 1587 547 1587 547 1588 545 1588 546 1588 545 1589 545 1590 544 1590 544 497 544 498 543 497 544 498 543 498 543 497 544 1591 543 1592 542 1591 543 1592 542 499 542 498 543 499 542 1592 542 500 541 499 541 499 542 500 541 1591 543 1591 543 1591 543 5185 4434 4411 544 1591 543 498 543 1590 544 1590 544 497 543 497 544 1590 544 497 544 498 543 1589 545 496 545 497 544 1588 545 1588 546 495 545 1587 547 494 547 495 546 494 536 1597 547 1586 547 1585 538 1596 538 1596 537 1596 538 1594 540 1594 539 502 539 501 540 501 540 501 540 500 540 500 540 1593 541 1591 543 1591 543 1591 542 497 544 498 543 498 542 1591 543 496 545 497 544 497 544 496 545 1588 546 1588 546 1586 547 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8968 4464 593 1659 559 593 536 563 566 1660 558 567 562 563 536 590 539 560 559 567 542 584 535 564 565 1660 568 557 562 564 535 1665 594 1658 560 1665 563 588 541 1659 559 1666 593 559 539 586 533 566 563 563 536 1663 585 566 533 593 536 563 566 560 539 587 532 567 562 563 536 590 539 560 559 1667 561 1664 564 1661 587 1664 564 1662 566 1659 589 562 537 589 540 559 560 566 533 593 536 563 566 1658 560 1667 561 1664 584 1667 561 1664 564 1661 587 564 535 591 538 561 568 558 540 585 534 565 564 561 537 588 541 559 560 1665 563 562 567 1659 559 1667 561 590 539 1660 558 593 536 +# OFF +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4412 4354 570 1575 568 504 572 1574 569 503 563 510 567 505 572 527 539 1580 563 509 568 505 572 1574 569 503 563 509 568 505 572 527 539 507 569 503 563 1582 571 500 566 507 569 503 563 535 542 505 561 511 566 1579 564 1582 571 1574 569 1576 567 1578 565 1581 562 1583 570 1575 568 1577 566 1580 563 1582 571 1574 569 1576 567 1579 564 1581 562 1583 570 1575 568 1577 566 1579 564 509 568 1577 566 1580 563 1582 571 501 565 5165 4412 4352 572 500 566 1579 564 509 568 1577 566 1579 564 1581 562 1583 570 502 564 1581 562 1582 571 501 565 1580 563 1582 571 1574 569 1576 567 1578 565 1579 564 509 567 1577 566 1579 564 1581 562 1583 570 1575 568 1577 566 506 571 501 565 534 542 529 537 535 542 531 535 537 539 532 545 527 539 507 569 529 537 509 568 504 562 510 566 532 544 527 539 507 569 528 538 534 542 1576 567 504 562 510 566 506 571 1574 569 +# +name: TEMP+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4411 4378 546 1573 570 502 564 1582 571 500 566 507 570 528 538 508 569 1576 567 1578 565 507 569 1575 568 504 562 510 567 532 544 527 539 507 569 528 538 1580 563 509 568 505 571 501 565 533 543 528 538 1580 563 1582 571 1573 570 1575 568 1577 566 1578 565 1580 563 1582 571 1573 570 1575 568 1577 566 1579 564 1580 563 1582 571 1574 569 1575 568 1577 566 506 570 1574 569 1576 567 505 571 1573 570 1575 568 1577 566 1578 565 5165 4412 4350 563 508 568 1576 567 506 571 1574 569 1575 568 1577 566 1579 564 508 568 504 562 1582 571 501 565 1579 564 1581 562 1583 570 1574 569 1575 568 1577 566 506 570 1574 569 1576 567 1578 565 1580 563 1582 571 500 566 506 570 502 564 534 542 504 562 535 541 531 535 511 565 532 544 528 538 507 569 503 563 509 568 530 536 509 568 531 535 537 540 1578 565 507 569 503 563 1581 562 510 566 506 571 502 564 507 569 +# +name: TEMP- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4465 4299 562 1582 571 501 565 1580 563 509 568 505 561 511 566 506 571 1574 569 1576 567 505 572 1574 569 503 563 509 568 504 562 510 566 505 572 501 565 1579 564 509 567 504 562 510 566 505 572 501 565 507 570 1575 568 1577 566 1579 564 1581 562 1583 571 1574 569 1576 567 1577 566 1579 564 1581 562 1583 570 1574 569 1576 567 1578 565 1580 563 1582 571 501 565 1579 564 1581 562 510 567 1578 565 1580 563 1582 571 501 565 5164 4413 4349 565 508 569 1576 567 505 572 1573 570 1575 568 1577 566 1579 564 508 568 503 563 1582 571 500 566 1579 564 1581 562 1582 571 1574 569 1575 568 1577 566 506 571 1574 569 1576 567 1577 566 1579 564 1581 562 1582 571 501 565 507 569 502 564 508 569 504 562 509 568 505 561 511 566 506 571 501 565 507 570 502 564 508 569 503 563 509 568 505 561 1583 571 502 564 508 568 1576 567 504 562 510 567 506 571 1574 569 +# MODE-AUTO +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4416 4349 564 1581 572 500 566 1580 563 508 568 505 571 449 669 453 571 1575 568 1578 565 507 569 502 564 508 568 504 572 500 566 1579 564 508 568 504 572 1573 570 502 564 508 568 504 572 499 567 505 571 501 565 1580 573 1572 571 1575 568 1577 566 1579 563 1581 572 1573 570 1575 568 1578 565 1580 563 1582 571 1574 569 1576 567 1578 564 1581 572 1573 570 502 564 1581 572 500 566 1579 564 1582 571 1573 570 502 564 508 568 5163 4414 4348 565 507 569 1576 567 505 571 1573 570 1576 567 1578 564 1580 573 499 567 505 571 1574 569 1576 567 1578 565 1580 573 1572 571 502 564 1581 572 1573 570 502 564 1581 572 1573 570 1575 568 1577 566 1580 563 1582 571 501 565 507 569 503 563 509 567 505 571 501 565 507 569 503 563 509 567 504 572 500 566 506 570 502 564 508 568 504 572 499 567 1578 565 508 568 1576 567 505 571 501 565 507 569 1575 568 1577 566 +# SWING OFF +name: SWING +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9830 9789 9825 9795 4618 2487 381 348 384 929 381 936 384 353 379 934 386 350 382 350 382 362 380 349 383 929 381 355 387 346 386 926 384 353 379 354 388 355 387 918 382 354 388 348 384 349 383 353 379 357 385 924 386 358 384 921 389 347 385 351 381 929 381 932 388 348 384 349 383 361 381 347 385 351 381 355 387 345 387 925 385 352 380 353 379 365 388 340 382 354 388 348 384 349 383 929 381 355 387 346 386 358 384 921 379 357 385 351 381 352 380 356 386 930 380 357 385 358 385 344 388 348 384 933 387 349 383 353 379 938 382 354 388 352 380 20353 4625 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 5623 5581 567 552 569 551 570 549 562 557 564 582 539 580 541 1672 571 1671 572 545 566 553 568 1698 545 1668 564 1676 567 1700 543 549 562 589 543 1667 566 552 569 1671 561 1677 566 554 567 552 569 1670 562 561 571 1665 568 1671 572 1667 566 1673 570 550 571 549 562 1676 567 583 538 1671 561 583 538 1674 569 552 569 1670 562 1676 567 579 542 556 565 1669 563 557 564 1700 543 549 562 585 536 1676 567 553 568 1676 567 574 537 1675 568 551 570 551 570 1669 564 1674 569 1669 564 558 563 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3296 1651 412 415 414 414 405 1240 408 420 409 1237 411 416 413 415 414 413 406 1240 408 1238 410 419 410 417 412 415 414 1232 406 1214 434 420 409 418 411 417 412 415 414 413 406 423 406 421 408 419 410 418 411 416 413 414 405 423 406 421 408 1237 411 416 413 415 414 414 405 423 406 421 408 419 410 417 412 1234 414 413 406 422 407 420 409 418 411 1235 413 1233 415 1205 433 1214 434 1212 436 1210 438 1208 440 1207 431 423 406 421 408 1238 410 417 412 415 414 414 405 422 407 420 409 419 410 418 411 416 413 1232 406 1241 407 420 409 418 411 417 412 415 414 413 406 422 407 421 408 1237 411 1235 413 414 415 413 406 422 407 420 409 418 411 416 413 414 405 424 405 422 407 420 409 419 410 417 412 415 414 413 406 422 407 420 409 418 411 416 413 414 405 424 405 422 407 420 409 418 411 416 413 415 404 423 406 421 408 419 410 418 411 416 413 414 405 424 405 422 407 420 409 418 411 416 413 414 405 423 406 421 408 419 410 417 412 415 414 414 405 423 406 421 408 420 409 1236 412 415 414 414 405 422 407 420 409 419 410 417 412 1233 405 424 405 1240 408 420 409 +# +name: MODE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8518 4245 544 1597 545 1568 543 1597 545 564 517 1570 541 568 513 1601 510 1603 539 4244 514 568 513 1601 541 541 519 1595 537 572 509 547 513 570 511 571 593 19280 8571 4219 539 1600 542 1572 539 1600 542 567 493 1594 538 571 520 1593 518 1595 547 4209 539 570 521 1593 539 544 516 1598 544 565 485 572 519 563 518 565 598 19272 8528 4262 538 1603 518 1595 537 1603 539 544 517 1597 545 537 544 1570 541 1599 543 4213 545 563 518 1596 515 567 514 1600 542 540 521 562 519 564 517 566 597 +# TIMER OFF +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4419 4378 562 1618 568 552 541 1619 567 1640 536 533 560 535 558 1649 537 559 534 537 566 1619 567 554 539 530 563 1618 568 1613 563 532 561 1626 560 537 566 554 539 530 563 557 536 536 1622 564 1616 560 1624 562 1625 561 1620 566 1615 561 1621 565 1616 560 534 559 536 567 529 564 559 534 1625 561 533 560 1625 561 1620 566 528 565 530 563 1620 566 530 563 532 561 1624 562 532 561 1650 536 1645 541 1614 562 1620 566 5226 4418 4372 558 1622 564 557 536 1623 563 1618 568 527 566 529 564 1617 559 564 539 531 562 1623 563 558 535 533 560 1621 565 1616 560 535 568 1619 567 529 564 557 536 532 561 560 533 561 542 1614 562 1619 567 1642 534 1628 569 1613 563 1619 567 1614 562 1619 567 528 565 530 563 533 560 563 540 1619 567 527 566 1619 567 1614 562 533 560 535 558 1625 561 536 567 527 566 1619 567 528 565 1620 566 1615 561 1620 566 1618 558 +# TIMER OFF +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4459 4372 589 1565 597 480 595 1559 592 485 590 487 588 488 598 479 596 1558 593 1562 589 1565 597 1558 593 483 593 485 590 486 589 488 598 479 596 480 595 1559 592 485 590 486 589 1565 597 481 594 1559 592 485 590 1564 598 479 596 481 594 483 592 484 591 1563 588 489 597 1558 593 1561 590 486 589 488 598 479 596 481 594 483 592 484 591 1563 599 478 597 480 595 482 593 483 592 485 590 486 589 488 588 489 597 5158 4462 4367 594 483 592 1563 588 488 598 1557 594 1560 591 1563 599 1556 595 482 593 484 591 485 590 487 588 1566 596 1559 592 1562 589 1565 597 1558 593 1562 589 488 598 1556 595 1559 592 485 590 1564 598 479 596 1558 593 484 591 1563 588 1566 596 1559 592 1562 589 488 598 1557 594 482 593 484 591 1563 588 1566 596 1559 592 1563 588 1565 597 1558 593 484 591 1563 588 1566 596 1559 592 1562 589 1565 597 1558 593 1561 590 1564 598 +# TIMER OFF +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4415 4348 568 1578 569 503 570 1575 572 500 563 510 563 508 565 533 540 1578 569 1576 571 527 546 500 563 509 564 533 540 532 541 1603 544 502 571 500 563 1582 565 1580 567 1578 569 504 569 502 571 1574 563 509 564 1581 566 506 567 504 569 529 544 501 572 500 563 509 564 1580 567 1578 569 529 544 501 572 500 563 509 564 507 566 1578 569 1576 571 501 572 1573 564 508 565 1606 541 504 569 1576 571 501 572 1573 564 5169 4410 4351 565 507 566 1579 568 504 569 1575 572 1573 564 1582 565 1580 567 505 568 503 570 1575 572 1573 564 1581 566 1580 567 1578 569 503 570 1575 572 1573 564 534 539 507 566 505 568 1577 570 1575 572 500 563 1582 565 507 566 1579 568 1577 570 1575 572 1573 564 1582 565 1580 567 505 568 504 569 1576 571 1574 563 1582 565 1580 567 1579 568 504 569 503 570 1574 563 509 564 1581 566 506 567 1578 569 503 570 1574 563 510 563 +# diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 0fe001247a..123d63f185 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,6 +1,12 @@ Filetype: IR library file Version: 1 -# Last Updated 30th Aug, 2022 +# Last Updated 7th Sep, 2022 +# +name: POWER +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 2E 00 00 00 # name: POWER type: parsed @@ -1256,3 +1262,112 @@ frequency: 38000 duty_cycle: 0.330000 data: 8435 4189 547 1557 547 1557 547 505 541 510 547 505 541 1562 542 510 547 1557 547 505 541 510 547 1557 546 1557 547 1557 547 505 541 510 547 505 541 21550 547 1558 545 1558 546 506 540 511 546 506 540 1563 541 511 546 1558 545 506 540 511 546 1558 546 1558 546 1558 546 506 540 511 546 506 540 21551 546 1558 546 1558 546 506 540 512 545 506 540 1563 540 512 545 1558 546 506 540 512 545 1558 546 1558 546 1558 546 506 540 512 545 506 540 21551 546 1559 545 1559 545 507 539 512 545 507 539 1564 539 512 545 1559 545 507 539 512 545 1559 545 1559 545 1559 545 507 539 512 545 507 539 21552 545 1559 545 1559 545 507 539 513 544 507 539 1564 540 512 545 1559 545 507 539 512 545 1559 545 1559 545 1559 545 507 539 512 545 507 539 21552 545 1559 545 1559 545 507 539 513 544 507 539 1565 538 513 544 1559 545 507 539 513 544 1559 545 1559 544 1559 545 534 512 513 544 507 539 21553 544 1560 544 1560 544 534 512 513 544 508 538 1565 539 539 518 1560 544 534 512 513 544 1560 544 1560 544 1560 544 534 512 513 544 508 538 # +# +name: POWER +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 41 00 00 00 +# +name: VOL+ +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 42 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 43 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 1B 00 00 00 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8498 4205 651 1471 576 530 550 1572 547 535 545 536 544 1578 541 540 550 1572 547 535 545 1576 543 539 541 1580 549 1572 547 534 546 1576 543 539 541 540 550 1571 548 533 547 1574 545 537 543 539 541 541 549 532 548 1574 545 536 544 1578 541 540 550 1571 548 1572 547 1574 545 1576 543 26533 8497 4203 653 1468 569 538 542 1579 550 531 549 533 547 1573 546 535 545 1576 543 538 542 1579 550 531 549 1571 548 1573 546 536 544 1576 543 539 541 540 550 1571 548 533 547 1574 545 536 544 538 542 540 550 530 550 1572 547 534 546 1575 544 537 543 1578 541 1579 550 1570 549 1572 547 26524 8496 4207 576 1570 549 533 547 1574 545 537 543 539 541 1580 549 532 548 1573 546 536 544 1577 542 539 551 1570 549 1572 547 534 546 1575 544 538 542 540 550 1571 548 534 546 1575 544 538 542 540 550 531 549 533 547 1575 544 537 543 1579 550 531 549 1571 548 1572 547 1574 545 1576 543 26529 8491 4211 573 1573 546 535 545 1576 543 539 551 530 550 1571 548 533 547 1574 545 536 544 1578 541 540 550 1570 549 1572 547 534 546 1575 544 538 542 539 541 1580 549 532 548 1572 547 535 545 537 543 539 541 540 550 1571 548 533 547 1574 545 537 543 1578 541 1579 550 1571 548 1574 545 26522 8498 4202 571 1574 545 537 543 1578 541 541 549 532 548 1573 546 534 546 1575 544 537 543 1577 542 540 550 1570 549 1571 548 533 547 1574 545 537 543 538 542 1579 550 531 549 1572 547 534 546 536 544 537 543 538 542 1579 550 531 549 1572 547 535 545 1575 544 1577 542 1579 550 1571 548 26522 8498 4203 570 1575 544 537 543 1578 541 541 549 532 548 1573 546 535 545 1575 544 538 542 1579 550 531 549 1571 548 1572 547 535 545 1575 544 538 542 539 541 1580 549 532 548 1572 547 534 546 536 544 537 543 539 541 1579 550 531 549 1571 548 533 547 1573 546 1574 545 1575 544 1577 542 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8490 4211 573 1573 546 535 545 1576 543 539 541 540 550 1571 548 532 548 1573 546 535 545 1576 543 539 541 1579 550 1571 548 533 547 1574 545 536 544 1576 543 1578 541 540 550 1570 549 533 547 534 546 536 544 537 543 539 541 540 550 1570 549 532 548 1572 547 1573 546 1574 545 1576 543 26528 8492 4208 648 1475 572 534 546 1575 544 537 543 539 541 1580 549 532 548 1573 546 535 545 1576 543 538 542 1579 550 1571 548 533 547 1575 544 537 543 1577 542 1579 550 532 548 1573 546 535 545 537 543 539 541 541 549 532 548 533 547 1575 544 537 543 1578 541 1579 550 1571 548 1573 546 26523 8496 4203 622 1499 569 537 543 1578 541 541 549 532 548 1572 547 534 546 1574 545 537 543 1578 541 540 550 1570 549 1571 548 534 546 1575 544 538 542 1578 541 1580 549 532 548 1573 546 536 544 538 542 540 550 532 548 533 547 535 545 1576 543 539 541 1579 550 1571 548 1573 546 1574 545 26521 8498 4201 572 1573 546 536 544 1577 542 540 550 531 549 1571 548 533 547 1573 546 536 544 1576 543 539 541 1579 550 1570 549 532 548 1573 546 535 545 1576 543 1578 541 540 550 1571 548 533 547 535 545 537 543 538 542 540 550 531 549 1572 547 534 546 1575 544 1576 543 1577 542 1579 550 26522 8498 4203 570 1575 544 538 542 1579 550 532 548 533 547 1575 544 537 543 1578 541 540 550 1570 549 533 547 1573 546 1575 544 537 543 1578 541 540 550 1571 548 1573 546 536 544 1578 551 531 549 533 547 535 545 537 543 539 541 540 550 1571 548 533 547 1575 544 1576 543 1578 541 1580 549 26521 8498 4203 570 1576 543 538 542 1580 549 532 548 533 547 1574 545 536 544 1577 542 540 550 1570 549 533 547 1573 546 1575 544 538 542 1579 550 531 549 1571 548 1573 546 536 544 1576 543 539 551 531 549 532 548 534 546 536 544 538 542 1579 550 531 549 1572 547 1573 546 1574 545 1576 543 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8500 4203 653 1468 569 538 542 1579 550 532 548 534 546 1575 544 536 544 1578 541 540 550 1572 547 534 546 1575 544 1577 542 539 541 1580 550 532 548 534 546 535 545 1576 543 1577 542 1579 551 532 548 534 546 536 544 1577 542 1578 552 531 549 533 547 534 546 1574 545 1575 544 1577 542 26533 8492 4211 573 1573 546 535 545 1576 543 539 551 530 550 1571 548 533 547 1574 545 536 544 1577 542 540 550 1570 549 1572 547 534 546 1576 543 539 541 541 549 532 548 1573 546 1574 545 1576 543 539 551 531 549 533 547 1573 546 1576 543 539 541 541 549 532 548 1573 546 1574 545 1576 543 26532 8493 4209 575 1571 548 533 547 1575 544 537 543 539 541 1580 550 531 549 1572 547 534 546 1576 543 538 542 1579 551 1570 549 532 548 1574 545 537 543 538 542 540 550 1570 549 1572 547 1575 544 537 543 539 541 540 550 1571 548 1573 546 536 544 538 542 539 551 1570 549 1572 547 1574 545 26530 8496 4207 567 1579 551 531 549 1572 547 535 545 536 544 1576 543 538 542 1579 551 530 550 1571 548 534 546 1574 545 1576 543 538 542 1579 551 532 548 533 547 534 546 1576 543 1577 542 1579 551 532 548 534 546 535 545 1576 543 1578 541 540 550 532 548 533 547 1574 545 1575 544 1577 542 26531 8495 4210 574 1571 548 534 546 1575 544 538 542 539 551 1569 550 531 549 1572 547 535 545 1576 543 539 541 1579 550 1571 548 534 546 1575 544 538 542 540 550 531 549 1572 547 1549 570 1551 568 539 551 531 549 532 548 1573 546 1550 569 538 542 540 550 531 549 1571 548 1548 571 1550 569 +# +name: POWER +type: parsed +protocol: NECext +address: 00 EF 00 00 +command: 1C E3 00 00 +# +name: VOL+ +type: parsed +protocol: NECext +address: 00 EF 00 00 +command: 00 FF 00 00 +# +name: VOL- +type: parsed +protocol: NECext +address: 00 EF 00 00 +command: 04 FB 00 00 +# +name: POWER +type: parsed +protocol: NEC42 +address: 6E 00 00 00 +command: 00 00 00 00 +# +name: VOL+ +type: parsed +protocol: NEC42 +address: 6E 00 00 00 +command: 4D 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC42 +address: 6E 00 00 00 +command: 4E 00 00 00 +# +name: POWER +type: parsed +protocol: NEC42 +address: 6E 00 00 00 +command: 0E 00 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 8441 4184 542 1562 541 1562 542 511 546 506 540 511 546 1557 546 506 540 1563 541 1563 540 1563 541 512 545 507 539 512 545 507 539 512 545 507 539 22605 538 1565 539 1565 538 514 543 509 537 514 543 1560 543 509 548 1555 548 1556 548 1556 547 504 542 510 547 505 541 510 547 505 541 510 547 22597 546 1559 544 1559 545 507 539 513 544 507 539 1564 539 513 544 1559 544 1559 545 545 508 549 513 508 538 513 544 508 538 513 544 22601 542 1562 542 1561 543 510 547 505 541 510 547 1557 547 505 541 1562 542 1562 542 1562 542 510 547 505 541 510 547 505 541 511 546 505 541 22603 540 1564 539 1565 538 513 544 508 538 513 544 1560 544 508 538 1565 538 1565 538 1566 537 514 543 509 548 504 542 509 548 504 542 509 548 22597 546 1558 546 1558 546 506 540 512 545 507 539 1564 540 512 545 1559 545 1559 544 1559 544 507 539 513 544 508 538 513 544 508 538 513 544 22600 543 1561 542 1562 542 510 547 505 541 510 547 1557 547 505 541 1562 541 1563 540 1563 541 511 546 506 540 511 546 506 540 511 546 506 540 22604 539 1565 538 1566 537 514 543 509 548 504 542 1561 543 509 548 1556 548 1556 548 1556 547 504 542 510 547 505 541 510 547 505 541 510 547 22598 545 1559 544 1559 545 507 539 512 545 507 539 1564 540 512 545 1559 544 1559 545 1559 545 508 538 513 544 508 538 513 544 508 538 513 544 22601 542 1562 542 1562 542 511 546 505 541 511 546 1557 546 506 540 1563 541 1563 540 1563 541 511 546 506 540 511 546 506 540 512 545 506 540 22604 539 1565 538 1566 537 514 543 509 548 504 542 1561 543 509 548 1556 547 1556 547 1556 548 504 542 510 547 505 541 510 547 505 541 510 547 +# +name: POWER +type: parsed +protocol: SIRC +address: 10 00 00 00 +command: 60 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 0E 00 00 00 +# +name: VOL+ +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 1A 00 00 00 +# diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index 76c25a3873..0fae271542 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,6 +1,6 @@ Filetype: IR library file Version: 1 -# Last Updated 29th Aug, 2022 +# Last Updated 7th Sep, 2022 # name: POWER type: parsed @@ -852,3 +852,46 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1294 388 1291 390 455 1215 1289 392 1298 384 451 1220 460 1210 459 1210 1294 388 457 1212 457 1212 457 7954 1301 382 1298 384 461 1207 1297 387 1293 389 456 1212 457 1212 457 1213 1291 392 463 1205 465 1205 464 9263 1295 414 1266 417 428 1214 1301 409 1271 412 433 1209 460 1209 460 1210 1294 415 430 1213 456 1213 456 7959 1296 413 1267 415 430 1213 1302 408 1271 411 434 1208 461 1209 460 1209 1295 414 431 1212 457 1213 456 9272 1297 412 1268 415 430 1212 1292 417 1273 410 435 1207 462 1208 461 1208 1296 413 432 1211 458 1211 458 7956 1299 410 1270 413 432 1210 1294 390 1300 409 436 1206 463 1207 463 1207 1298 387 458 1210 459 1211 458 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1251 410 1245 415 407 1252 1246 415 1250 410 412 1247 408 1252 413 1247 408 1252 413 1247 408 1252 1246 7350 1248 438 1227 433 389 1245 1253 434 1221 439 383 1250 415 1245 410 1250 405 1255 410 1249 406 1255 1253 7340 1247 439 1226 407 415 1245 1253 433 1222 412 410 1250 405 1255 410 1249 406 1254 411 1249 406 1254 1254 7340 1247 412 1253 407 415 1245 1253 407 1248 412 410 1250 405 1255 410 1249 406 1254 411 1248 407 1253 1245 7349 1249 411 1254 406 405 1254 1254 406 1249 411 411 1248 407 1252 413 1246 409 1251 414 1245 410 1250 1248 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1250 409 1246 441 381 1252 1246 414 1252 435 387 1246 409 1250 405 1255 1243 417 405 1281 384 1275 380 8188 1244 416 1249 437 385 1249 1249 437 1218 443 379 1253 412 1248 407 1253 1245 415 407 1252 413 1246 409 8186 1246 414 1251 435 387 1272 1226 408 1247 439 383 1276 379 1281 384 1249 1249 437 385 1248 407 1253 412 8181 1251 409 1246 440 382 1277 1221 439 1226 434 378 1255 410 1249 406 1254 1244 416 406 1253 412 1247 408 8187 1245 441 1225 435 387 1273 1225 435 1220 440 382 1251 414 1245 410 1250 1248 412 410 1249 406 1254 411 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1243 444 1221 438 384 1276 1222 438 1217 443 379 1254 411 1274 381 1252 413 1246 409 1251 1247 413 409 8185 1247 440 1225 435 387 1246 1252 408 1247 440 382 1277 388 1245 410 1276 379 1280 385 1249 1249 411 411 8184 1248 412 1243 444 378 1282 1216 444 1221 439 383 1250 405 1254 411 1248 407 1253 412 1248 1250 410 412 8182 1250 410 1245 442 380 1253 1245 415 1250 437 385 1247 408 1252 413 1272 383 1251 414 1272 1226 408 414 8180 1252 409 1246 440 382 1252 1246 440 1225 435 387 1272 383 1250 405 1254 411 1249 406 1254 1244 416 406 8188 1244 416 1249 437 385 1249 1249 411 1244 443 379 1253 412 1273 382 1278 387 1246 409 1251 1247 413 409 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1305 379 1283 402 434 1251 431 1253 1276 408 1275 411 435 1249 1280 404 432 1253 1276 408 438 1247 1282 7144 1281 404 1279 407 439 1247 435 1250 1310 375 1307 379 467 1218 1311 374 431 1254 1306 379 436 1249 1311 7119 1305 380 1302 382 464 1222 460 1224 1305 380 1302 382 464 1221 1308 377 469 1217 1302 383 463 1222 1307 7121 1303 380 1303 382 464 1221 462 1223 1306 378 1305 381 465 1220 1309 375 430 1254 1306 380 435 1250 1279 7148 1276 408 1274 411 435 1250 432 1252 1277 408 1274 410 436 1249 1280 404 432 1253 1276 408 438 1247 1282 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1282 402 1281 404 432 1252 430 1254 1275 408 1275 410 436 1248 435 1249 434 1251 1278 405 431 1254 439 7984 1277 407 1276 408 438 1246 436 1248 1281 402 1281 403 433 1251 432 1253 430 1254 1275 408 438 1246 437 7985 1276 407 1276 408 438 1246 436 1248 1281 402 1281 403 433 1252 431 1253 440 1245 1274 409 437 1248 435 7990 1333 351 1332 352 433 1252 431 1254 1326 358 1335 349 436 1249 434 1250 433 1252 1328 355 440 1244 438 7984 1328 355 1328 356 439 1245 438 1247 1333 350 1333 351 434 1250 433 1252 431 1253 1327 357 438 1246 437 7988 1273 410 1273 411 435 1249 434 1250 1279 405 1278 406 440 1244 439 1246 436 1247 1282 402 434 1251 432 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1282 403 1279 405 431 1254 439 1246 1273 412 434 1251 432 1254 1275 410 436 1249 433 1251 431 1254 1275 7152 1283 402 1281 405 431 1254 439 1246 1273 412 434 1251 432 1254 1305 379 436 1249 433 1252 430 1254 1306 7122 1302 382 1311 374 462 1223 459 1226 1303 381 465 1221 461 1223 1306 379 467 1218 464 1221 461 1224 1305 7123 1312 373 1310 375 461 1224 469 1216 1303 381 465 1221 461 1223 1306 379 467 1218 464 1221 461 1224 1305 7123 1312 374 1308 376 460 1225 468 1218 1311 374 462 1223 459 1226 1303 382 464 1222 460 1224 469 1217 1302 7128 1306 379 1303 382 464 1221 461 1224 1305 380 466 1219 463 1221 1277 408 469 1217 465 1219 463 1222 1307 7122 1282 403 1279 405 462 1223 459 1226 1283 402 465 1221 461 1224 1274 410 467 1218 464 1221 461 1224 1274 7155 1280 405 1278 408 438 1247 435 1250 1279 406 440 1245 437 1249 1280 405 431 1255 438 1249 433 1252 1277 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1279 406 1307 377 438 1246 436 1249 1311 373 463 1221 462 1224 469 1216 466 1218 465 1220 463 1222 460 7967 1304 380 1303 382 464 1221 462 1223 1276 409 468 1217 466 1220 463 1223 459 1225 468 1217 465 1220 462 7966 1275 410 1303 382 464 1220 463 1222 1276 409 468 1218 465 1220 463 1223 459 1225 468 1217 435 1250 463 7965 1306 378 1304 381 465 1220 463 1222 1307 378 468 1217 465 1219 433 1253 440 1245 437 1248 434 1251 462 7966 1275 411 1282 404 432 1254 439 1248 1281 404 432 1254 439 1247 436 1250 433 1253 440 1246 437 1248 435 7995 1276 409 1284 402 434 1252 431 1255 1274 411 435 1251 431 1254 439 1247 436 1250 433 1253 440 1246 436 7992 1279 407 1276 410 436 1249 433 1252 1277 409 447 1238 434 1252 431 1254 439 1247 435 1250 433 1253 440 7990 1281 404 1278 406 440 1245 438 1249 1280 405 441 1245 437 1248 435 1251 432 1254 439 1247 435 1250 433 +# diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index b761a21b52..533700cdd2 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,6 +1,13 @@ Filetype: IR library file Version: 1 -# Last Updated 29th Aug, 2022 +# Last Updated 7th Sep, 2022 +# +# ON +name: POWER +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4F B0 00 00 # name: POWER type: parsed @@ -314,3 +321,21 @@ frequency: 38000 duty_cycle: 0.330000 data: 9031 4479 596 560 572 558 564 566 566 1693 571 1688 566 563 569 561 571 559 563 1696 568 561 571 559 563 1697 567 562 570 1689 565 1694 570 1688 566 1693 571 1661 593 1693 571 558 564 566 566 564 568 1691 563 541 591 564 568 562 570 560 562 1697 567 1692 562 1696 568 562 570 1689 565 564 568 561 571 559 563 567 565 564 568 562 570 560 562 567 565 1694 570 1689 565 1694 570 1688 566 1693 571 1688 566 1693 571 1662 592 13987 9031 2220 590 96231 9034 2217 593 96234 9030 2222 588 96247 9037 2215 595 # +name: VOL+ +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 11 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 14 00 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4E B1 00 00 +# diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index a946944acd..b1685a0319 100755 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,6 +1,31 @@ Filetype: IR library file Version: 1 -# Last Updated 27 Aug, 2022 +# Last Updated 7th Sep, 2022 +# +# ON +name: POWER +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 40 00 00 00 +# ON +name: POWER +type: parsed +protocol: NECext +address: A0 B7 00 00 +command: E9 16 00 00 +# ON +name: POWER +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 3A 00 00 00 +# ON +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4008 3984 520 1976 517 1980 523 1975 518 1979 514 1016 492 1012 496 1975 518 987 521 1976 517 987 521 1977 516 1014 494 1010 488 990 518 985 523 1008 489 1981 522 1976 517 1013 495 1976 517 987 521 1977 516 987 521 1977 516 8473 4002 3988 516 1979 514 1984 519 1977 516 1982 521 1008 489 988 520 1977 516 988 520 1977 516 988 520 1977 516 987 521 1010 488 989 519 985 523 980 518 2006 487 1984 519 983 514 1983 520 983 515 1983 520 983 515 1982 521 8466 4010 3978 516 1980 523 1974 519 1978 578 1918 522 982 516 1014 494 1977 516 987 521 1976 517 987 521 1976 517 986 522 982 515 1015 493 1011 487 990 518 1979 514 1984 519 984 524 1973 520 984 513 1984 519 983 514 1983 520 # name: POWER type: parsed @@ -3062,3 +3087,183 @@ protocol: NECext address: 40 40 00 00 command: 0F F0 00 00 # +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3476 1735 443 429 446 1300 451 422 453 420 445 428 447 426 449 423 452 448 417 429 446 426 449 424 451 422 443 430 445 1300 451 422 453 420 445 428 447 426 449 423 452 421 444 429 446 427 448 424 451 1295 445 427 448 425 450 423 452 420 445 429 446 426 449 424 451 421 444 1303 448 425 450 1296 444 1301 450 1296 444 1302 448 424 451 422 453 1293 447 426 449 1296 444 1302 448 1297 454 1293 447 425 450 1296 444 74939 3477 1734 444 429 446 1300 451 422 453 419 446 455 420 425 450 423 452 420 445 428 447 426 449 424 451 421 444 429 446 1300 451 422 453 420 445 428 447 425 450 423 452 420 445 429 446 426 449 424 451 1295 445 427 448 425 450 422 453 420 445 428 447 426 449 424 451 421 444 1302 449 424 451 1295 445 1301 450 1296 444 1302 449 424 451 422 453 1292 448 425 450 1296 444 1301 450 1297 443 1302 448 425 450 1295 445 74938 3478 1734 444 428 447 1299 452 421 444 429 446 427 448 425 450 422 453 420 444 428 447 426 449 423 452 421 444 429 446 1300 451 422 453 419 446 428 447 425 450 423 452 421 444 429 446 426 449 424 451 1295 445 427 448 425 450 423 452 420 445 428 447 426 449 424 451 421 444 1302 448 424 451 1295 445 1301 450 1296 444 1302 449 424 451 422 453 1292 448 425 450 1296 444 1301 450 1296 444 1302 449 424 451 1295 445 74938 3479 1733 445 428 447 1299 452 421 444 429 446 426 449 424 451 421 454 419 446 427 448 425 450 422 453 420 445 428 447 1299 452 421 444 429 446 426 449 424 451 422 453 419 446 427 448 425 450 422 453 1293 447 426 449 423 452 421 444 429 446 426 449 424 451 422 453 419 446 1301 450 422 453 1293 447 1299 452 1294 446 1300 451 422 453 419 446 1301 449 423 452 1294 446 1300 451 1295 445 1300 451 422 453 1293 447 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3476 1736 473 399 445 1301 481 392 452 421 444 429 446 427 448 424 451 422 453 419 446 427 448 425 450 423 452 420 445 1301 450 423 452 421 444 429 446 427 448 424 451 422 453 419 446 427 448 425 450 1296 444 428 447 426 449 423 452 421 444 429 446 427 448 424 451 422 453 419 446 1301 450 423 452 420 445 1301 450 1297 443 429 446 427 448 424 451 1295 445 428 478 394 450 1296 444 1302 449 424 451 1294 446 74876 3479 1732 446 427 448 1298 453 420 445 428 447 425 450 423 452 421 444 429 446 426 449 424 451 421 444 429 446 427 448 1298 453 420 445 428 447 426 449 423 452 421 444 429 446 426 449 424 451 421 444 1303 448 425 450 423 452 420 445 428 447 426 449 423 452 421 444 429 446 426 449 1297 454 419 446 428 447 1298 453 1293 447 426 449 423 452 421 444 1302 449 424 451 422 453 1293 447 1298 453 420 445 1302 449 74849 3474 1738 450 422 453 1293 447 425 450 423 452 420 445 428 447 426 449 424 451 421 444 429 446 427 448 425 450 422 453 1293 447 426 449 423 452 421 444 429 446 426 449 424 451 422 453 419 446 427 448 1298 453 420 445 428 447 426 449 423 452 421 444 429 446 426 449 424 451 422 443 1303 448 425 450 423 452 1293 447 1299 452 421 444 429 446 427 448 1298 453 420 445 428 447 1299 452 1294 446 426 449 1297 443 74855 3479 1733 445 427 448 1298 453 420 445 428 447 426 449 423 452 421 444 429 446 427 448 424 451 422 453 419 446 427 448 1298 453 420 445 428 447 426 449 424 451 421 444 429 446 427 448 425 450 422 453 1293 447 426 449 423 452 421 444 429 446 427 448 424 451 422 453 419 446 427 448 1298 453 420 445 428 447 1299 452 1294 446 427 448 424 451 422 453 1293 447 425 450 423 452 1293 447 1299 452 421 444 1302 449 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3482 1730 448 425 450 1296 444 429 446 427 448 424 451 422 453 420 445 428 447 425 450 423 452 421 444 429 446 426 449 1297 454 419 446 427 448 425 450 423 452 420 445 428 447 426 449 424 451 421 444 1303 448 425 450 422 453 420 445 428 447 425 450 423 452 421 444 429 446 427 448 424 451 422 453 419 446 428 447 1298 453 420 445 428 447 426 449 424 451 421 444 429 446 427 448 1298 453 420 445 1301 450 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3512 1700 478 395 480 1266 474 399 476 396 479 394 481 392 473 400 475 397 478 395 480 393 482 390 475 398 477 396 479 1267 473 399 476 397 478 395 480 392 473 400 475 398 477 396 479 394 481 391 474 1272 479 394 481 392 473 400 475 398 477 395 480 393 482 391 474 399 476 1269 482 391 474 399 476 397 478 395 480 1266 474 398 477 396 479 1267 473 399 476 397 478 394 481 392 473 1273 478 395 480 1266 474 +# +name: CH+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3505 1707 481 391 474 1272 479 394 481 392 473 400 475 398 477 395 480 393 482 391 474 399 476 396 479 394 481 392 473 1273 478 395 480 393 482 390 475 398 477 396 479 394 481 391 474 399 476 397 478 1268 472 400 475 398 477 395 480 393 482 391 474 399 476 396 479 394 481 392 473 400 475 1271 480 393 482 1264 476 1269 482 391 474 399 476 397 478 395 480 1266 474 398 477 1269 482 1264 476 397 478 1267 473 106476 3510 1702 476 396 479 1267 473 400 475 398 477 395 480 393 482 391 474 398 477 396 479 394 481 391 474 399 476 397 478 1268 472 401 474 398 477 396 479 394 481 391 474 399 476 397 478 395 480 392 473 1274 477 396 479 393 482 391 474 399 476 397 478 394 481 392 473 400 475 398 477 395 480 1266 474 399 476 1270 481 1265 475 397 478 395 480 393 472 401 474 1271 480 393 482 1264 476 1270 481 392 473 1273 478 +# +name: CH- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3503 1708 480 392 473 1274 477 396 479 393 482 391 474 399 476 397 478 395 480 392 473 400 475 398 446 427 448 424 482 1264 476 397 478 394 481 392 473 400 475 398 477 395 480 393 482 391 474 399 476 1270 480 392 473 400 475 398 477 395 480 393 482 391 474 399 476 397 478 1268 472 400 475 1271 480 393 482 1264 476 1270 481 392 473 400 475 1270 481 393 472 1274 477 396 479 1267 473 1273 478 395 480 1266 474 74632 3510 1702 476 397 478 1268 472 400 475 398 477 395 480 393 482 391 474 399 476 396 479 394 481 392 473 400 475 398 477 1268 483 391 474 399 476 396 479 394 481 391 474 399 476 397 478 395 480 392 473 1273 478 396 479 393 482 391 484 388 477 397 478 395 480 392 473 400 475 1271 479 394 481 1264 476 397 478 1268 472 1273 478 395 480 393 482 1264 476 397 478 1267 473 400 475 1271 480 1266 474 399 476 1270 481 74602 3509 1703 475 397 478 1268 482 390 475 398 477 396 479 394 481 391 474 399 476 397 478 395 480 392 473 400 475 398 477 1269 482 391 474 399 476 397 478 394 481 392 473 400 475 397 478 395 480 393 482 1264 476 396 479 394 481 392 473 400 475 398 477 395 480 393 482 391 474 1272 479 394 481 1265 475 397 478 1268 472 1274 476 396 479 394 481 1264 476 397 478 1268 483 390 475 1272 479 1267 473 400 475 1270 481 +# +name: MUTE +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 41 00 00 00 +# +name: MUTE +type: parsed +protocol: NECext +address: A0 B7 00 00 +command: AF 50 00 00 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3999 3980 517 1984 520 1980 513 1987 517 1983 521 979 513 987 515 1985 519 1983 511 1989 515 1986 518 1982 511 1989 515 984 518 982 520 980 512 989 513 1987 517 1983 521 979 513 988 514 986 516 984 518 982 520 980 512 9042 3991 3987 521 1980 514 1987 517 1983 521 1980 513 986 516 984 518 1982 511 1989 515 1986 518 1982 512 1989 515 1985 519 981 521 979 513 987 515 985 517 1983 521 1979 515 986 516 984 518 982 520 980 512 989 513 987 515 9038 4027 3952 514 1987 517 1983 521 1979 515 1986 518 981 521 979 513 1988 516 1985 519 1981 513 1988 516 1984 520 1980 514 986 516 984 518 982 520 980 522 1978 516 1986 518 982 520 980 512 988 514 986 516 984 518 982 520 9035 4019 3959 517 1983 521 1979 515 1986 518 1982 511 988 514 986 516 1984 520 1981 512 1988 516 1984 520 1980 513 1987 517 983 519 981 521 979 513 987 515 1985 519 1982 511 988 514 986 516 984 518 982 520 980 543 957 545 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4006 3983 521 1975 518 1980 513 1984 519 1977 516 987 521 983 515 1982 521 1977 516 1981 522 1974 519 1979 514 1983 520 983 514 989 519 985 523 981 517 2007 496 1975 518 985 523 981 517 987 521 1009 489 989 519 985 523 8466 4010 3979 515 1981 522 1975 518 1980 513 1984 519 984 513 990 518 2005 488 1983 520 1977 516 1981 522 1974 519 1979 514 1015 493 985 523 980 518 986 522 1975 518 1980 513 990 518 986 522 981 517 988 520 1010 488 989 519 8469 4007 3981 523 1972 521 1976 517 1980 523 1974 519 984 524 980 518 2005 488 1984 519 1978 515 1982 521 1975 518 1980 513 990 518 985 523 1008 489 987 521 1976 517 1981 522 981 517 1013 495 1009 489 988 520 984 513 1016 492 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4008 3980 524 1972 521 1976 517 1980 513 1984 519 983 514 989 519 1979 514 989 519 1978 515 1983 520 1976 517 1980 523 980 518 986 522 1008 490 1014 494 1977 516 1980 523 980 518 1980 513 990 518 1012 496 1007 491 986 522 8465 4011 3978 516 1980 513 1983 520 1977 516 1980 523 980 517 986 522 1975 518 985 523 1974 519 1978 515 1982 521 1975 518 985 523 981 517 986 522 1008 489 1981 522 1974 519 985 523 1974 519 1010 498 979 518 985 523 981 517 8470 4006 3983 521 1974 519 1978 515 1982 521 1976 517 986 522 982 515 1981 522 982 516 1982 521 1975 518 1979 514 1983 520 983 515 989 519 1011 497 981 517 1980 523 1974 519 985 523 1974 519 984 524 1006 491 1012 496 982 516 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4010 3978 515 1980 523 1974 519 1978 515 1982 521 982 515 988 520 1977 516 988 520 1977 516 1981 522 1974 519 1011 497 1006 491 1012 496 1007 490 1013 495 1976 517 1980 523 980 518 1980 523 980 517 985 523 981 516 1981 522 8465 4000 3987 517 1979 513 1984 519 1977 516 1981 522 981 517 987 521 1976 517 987 521 1976 517 1980 523 1974 519 985 523 980 517 986 522 982 516 988 520 1977 516 1981 522 981 517 1981 522 980 517 986 522 982 515 1981 522 8465 3999 3986 518 1977 515 1981 522 1974 519 1978 514 1015 493 984 524 1973 520 1010 487 1983 520 1976 516 1980 523 1006 491 1012 496 1007 490 1013 495 1008 489 1982 521 1975 518 1012 496 1975 518 1011 497 1006 492 1012 496 1975 518 +# +name: CH+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4005 3982 522 1974 519 1978 515 1982 521 1975 518 986 522 982 515 1981 522 981 517 1980 523 1974 519 984 524 1973 520 984 524 979 518 985 523 981 517 1980 523 1973 520 984 524 1973 520 984 513 990 518 1978 515 989 519 8468 4008 3979 514 1981 522 1974 519 1978 515 1982 521 983 515 1014 494 1977 516 1014 494 1977 516 1982 521 982 516 1981 522 982 515 987 521 983 514 989 519 1978 515 1982 521 982 516 1981 522 981 516 988 520 1977 516 988 520 8468 4007 3980 524 1972 521 1976 517 1980 523 1973 520 984 524 979 519 1979 514 989 519 1978 515 1982 521 982 516 1981 522 981 517 986 522 982 516 1014 494 1977 516 1980 523 980 517 1980 523 979 519 985 523 1975 518 985 523 +# +name: CH- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 4006 3981 523 1973 520 1977 516 1981 522 1974 519 1011 497 1006 491 1979 524 1005 493 1978 515 1982 521 1009 488 1015 493 1010 498 1006 491 1012 496 1007 490 1980 523 1973 520 1010 498 1973 520 983 515 1016 492 1978 514 1983 520 8468 4007 3982 522 1973 520 1977 516 1981 522 1975 518 986 522 1008 490 1982 521 982 516 1982 521 1976 517 986 522 982 516 988 520 984 524 979 519 985 523 1975 518 1979 514 989 519 1978 515 988 520 984 524 1973 520 1977 516 +# +name: POWER +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 59 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 08 00 00 00 +command: D5 00 00 00 +# +name: POWER +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 2A 00 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 00 7F 00 00 +command: 0C F3 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 86 05 00 00 +command: 13 EC 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 02 7D 00 00 +command: 11 EE 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 84 E0 00 00 +command: 22 DD 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 4E 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 0E 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 04 00 00 00 +command: 1B 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 5E 00 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: EA C7 00 00 +command: 62 9D 00 00 +# +name: POWER +type: parsed +protocol: RCA +address: 0F 00 00 00 +command: 3B 00 00 00 +# +name: POWER +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 03 00 00 00 +# +name: POWER +type: parsed +protocol: SIRC +address: 01 00 00 00 +command: 36 00 00 00 +# +name: POWER +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 24 00 00 00 +# diff --git a/assets/resources/rfidfuzzer/example_uids.txt b/assets/resources/rfidfuzzer/example_uids.txt new file mode 100644 index 0000000000..46ce16ba8c --- /dev/null +++ b/assets/resources/rfidfuzzer/example_uids.txt @@ -0,0 +1,8 @@ +# Example file, P.S. keep empty line at the end! +0000000000 +FE00000000 +CAFE000000 +00CAFE0000 +0000CAFE00 +000000CAFE +00000000CA diff --git a/assets/unit_tests/subghz/clemsa.sub b/assets/unit_tests/subghz/clemsa.sub new file mode 100644 index 0000000000..b07d031f09 --- /dev/null +++ b/assets/unit_tests/subghz/clemsa.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Clemsa +Bit: 18 +Key: 00 00 00 00 00 02 FC AA diff --git a/assets/unit_tests/subghz/clemsa_raw.sub b/assets/unit_tests/subghz/clemsa_raw.sub new file mode 100644 index 0000000000..5f86de98c6 --- /dev/null +++ b/assets/unit_tests/subghz/clemsa_raw.sub @@ -0,0 +1,14 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -334 10811 -4320 65 -100 65 -698 10157 -7550 65 -200 165 -166 133 -66 531 -66 331 -102 197 -132 133 -298 99 -132 263 -200 261 -988 99 -262 131 -296 97 -132 229 -100 459 -100 131 -132 393 -100 1119 -100 8325 -6376 133 -366 131 -562 65 -1034 131 -198 563 -168 365 -66 229 -332 297 -100 231 -166 429 -132 295 -166 97 -100 195 -724 97 -132 97 -1088 163 -200 1651 -100 2885 -8520 365 -166 97 -1558 163 -198 163 -132 465 -134 131 -66 267 -198 65 -232 299 -66 165 -166 65 -498 165 -100 233 -200 133 -166 131 -68 821 -100 263 -66 7633 -7610 1259 -200 99 -98 165 -1196 99 -132 263 -266 99 -200 463 -66 627 -66 1981 -98 7801 -4004 97 -628 65 -264 133 -1088 163 -134 131 -928 297 -166 133 -134 131 -266 297 -596 229 -164 427 -564 197 -166 265 -198 65 -100 559 -6708 131 -132 131 -430 527 -200 367 -66 263 -198 233 -98 299 -68 365 -296 465 -132 855 -66 857 -98 4741 -8312 99 -364 163 -200 133 -1428 529 -132 65 -166 595 -1392 97 -100 97 -132 99 -264 199 -828 99 -398 297 -66 233 -98 861 -100 663 -100 2357 -100 5075 -4640 131 -3312 231 -100 363 -132 99 -296 99 -132 165 -132 363 -98 165 -130 65 -98 165 -132 163 -130 63 -164 297 -198 9769 -3852 133 -98 67 -1226 329 -526 99 -164 295 -496 1713 -196 1681 -130 131 -132 5497 -7230 65 -1150 133 -330 259 -66 329 -100 97 -988 165 -134 197 -166 67 -100 361 -68 461 -100 231 -132 165 -66 365 -264 231 -100 99 -98 265 -696 99 -166 199 -100 101 -962 7101 -6484 363 -760 363 -132 265 -134 431 -264 329 -66 427 -330 263 -164 593 -130 231 -130 627 -66 399 -432 329 -526 131 -100 591 -166 9305 -4044 65 -3532 361 -98 163 -66 461 -264 197 -98 391 -66 329 -132 165 -136 463 -66 529 -166 131 -100 199 -264 133 -66 361 -98 689 -66 229 -198 627 -66 297 -100 261 -66 1685 -134 7883 -3932 229 -728 133 -98 133 -862 65 -132 99 -498 297 -166 133 -332 197 -132 693 -198 97 -656 1087 -64 6209 -6164 131 -266 99 -496 165 -432 67 -100 97 -330 821 -98 361 -100 493 -164 133 -66 197 -200 431 -66 65 -66 399 -66 331 -200 199 -402 131 -664 10955 -5314 65 -262 97 -198 97 -724 99 -196 65 -592 327 -66 625 -262 131 -66 197 -64 427 -132 65 -628 265 -332 329 -368 789 -66 8809 -6238 129 -328 295 -232 363 -98 431 -100 199 -98 261 -530 561 -592 263 -132 1645 -5358 65 -764 65 -330 165 -1158 197 -432 265 -98 397 -166 463 -498 561 -398 199 -66 199 -66 3479 +RAW_Data: -12124 165 -626 65 -890 229 -362 1329 -66 2187 -98 2081 -66 725 -134 3309 -9856 165 -166 263 -198 65 -960 653 -66 261 -66 821 -66 12463 -4032 97 -166 97 -924 65 -464 97 -68 163 -198 165 -100 263 -232 97 -366 633 -12244 65 -332 231 -200 197 -134 197 -234 2457 -134 399 -132 923 -198 197 -64 331 -132 295 -66 11377 -3896 163 -98 65 -194 131 -132 231 -366 131 -132 65 -1050 197 -200 299 -66 3007 -100 11685 -6172 133 -1154 491 -100 293 -200 65 -98 429 -266 463 -64 797 -98 265 -266 397 -132 1227 -66 8485 -4224 97 -166 65 -100 199 -2706 65 -66 263 -98 299 -298 231 -100 499 -66 97 -134 295 -66 431 -198 565 -66 2093 -100 533 -4056 65 -1482 229 -1160 165 -168 299 -166 459 -66 165 -134 99 -100 497 -166 397 -200 431 -200 65 -66 661 -164 529 -66 4671 -8442 131 -100 65 -66 165 -530 131 -132 597 -66 963 -488 275 -2806 2641 -438 2639 -420 2691 -412 2677 -404 2669 -416 2693 -416 361 -2730 367 -2724 2665 -422 355 -2726 2687 -394 399 -2698 2697 -390 375 -2732 2701 -388 355 -21244 2697 -416 377 -2722 2675 -416 2689 -388 2707 -396 2679 -414 2689 -392 2717 -388 375 -2706 383 -2732 2673 -396 365 -2734 2695 -386 377 -2728 2689 -384 389 -2730 2671 -386 379 -21252 2693 -416 347 -2748 2687 -396 2707 -384 2701 -388 2679 -412 2701 -418 2669 -404 363 -2726 385 -2698 2699 -416 355 -2706 2689 -416 365 -2736 2673 -386 415 -2692 2709 -378 361 -21270 2685 -452 311 -2774 2637 -442 2665 -418 2661 -448 2653 -408 2703 -384 2687 -410 365 -2738 355 -2742 2689 -382 371 -2738 2677 -384 415 -2700 2673 -410 363 -2730 2701 -386 357 -21270 2705 -398 361 -2740 2689 -386 2679 -414 2687 -410 2673 -418 2697 -386 2681 -412 383 -2702 395 -2706 2703 -380 385 -2696 2709 -418 355 -2702 2699 -418 361 -2700 2717 -386 375 -21242 2697 -418 355 -2748 2695 -382 2683 -410 2703 -380 2707 -384 2705 -404 2675 -416 383 -2700 359 -2728 2695 -382 385 -2728 2687 -416 357 -2708 2705 -386 389 -2700 2717 -388 373 -21234 2721 -418 353 -2724 2667 -416 2705 -380 2693 -386 2711 -384 2691 -422 2699 -398 365 -2726 385 -2700 2703 -378 361 -2728 2697 -420 357 -2704 2711 -388 377 -2734 2667 -406 363 -21260 2699 -418 361 -2702 2711 -380 2695 -416 2689 -402 2687 -384 2695 -416 2677 -408 361 -2732 385 -2704 2707 -414 325 -2730 2695 -418 361 -2728 2669 -414 385 -2702 2701 -414 355 -21246 2705 -378 379 -2728 2693 -384 2715 -380 2717 -386 2695 -390 2709 -388 2683 -420 355 -2730 385 -2700 2685 -416 347 -2748 2683 -396 365 -2740 2667 -416 385 -2696 2693 -414 349 -21260 2689 -452 319 -2762 2631 -476 2627 -430 2689 +RAW_Data: -416 2667 -412 2671 -416 2689 -424 347 -2732 353 -2738 2669 -410 363 -2728 2689 -410 349 -2742 2677 -386 415 -2696 2705 -380 361 -21254 2717 -386 413 -2700 2693 -380 2695 -416 2691 -398 2679 -416 2699 -384 2709 -382 367 -2726 361 -2730 2707 -382 377 -2728 2675 -386 411 -2684 2713 -414 357 -2704 2707 -388 357 -21276 2699 -382 385 -2724 2689 -416 2661 -416 2685 -384 2723 -388 2703 -400 2677 -416 385 -2696 357 -2724 2713 -384 375 -2726 2673 -420 355 -2728 2685 -396 397 -2688 2697 -408 363 -21252 2667 -484 311 -2766 2635 -476 2637 -420 2665 -448 2651 -408 2701 -384 2697 -406 355 -2758 327 -2736 2685 -420 361 -2728 2683 -384 385 -2726 2685 -414 357 -2708 2711 -388 375 -21246 2689 -448 323 -2750 2695 -378 2715 -386 2687 -392 2711 -384 2683 -416 2705 -412 327 -2732 387 -2728 2679 -416 357 -2738 2659 -418 363 -2732 2677 -386 413 -2700 2695 -380 391 -21258 2679 -406 383 -2706 2695 -384 2703 -418 2679 -404 2705 -380 2687 -418 2669 -410 359 -2726 387 -2696 2707 -416 357 -2710 2693 -418 361 -2730 2691 -380 385 -2730 2677 -414 357 -21254 2711 -382 385 -2700 2713 -414 2667 -384 2705 -418 2675 -406 2699 -382 2689 -418 365 -2726 377 -2708 2701 -384 389 -2698 2709 -398 361 -2738 2669 -416 385 -2692 2687 -418 357 -77456 131 -398 197 -132 295 -330 97 -132 229 -164 459 -164 295 -264 393 -264 719 -64 427 -98 855 -134 395 -98 297 -164 263 -262 65 -100 63 -132 197 -328 1185 -66 9359 -6420 261 -664 131 -100 299 -134 301 -232 363 -232 299 -200 165 -166 427 -230 299 -164 361 -394 1025 -100 225 -820 165 -1248 491 -100 293 -66 261 -264 131 -98 589 -164 655 -132 427 -132 295 -164 129 -132 163 -328 263 -196 627 -566 129 -100 131 -98 2377 -130 1255 -3878 297 -232 195 -132 65 -98 165 -596 397 -266 99 -198 363 -98 923 -100 431 -66 1383 -3724 297 -166 165 -66 99 -398 265 -266 463 -232 133 -232 65 -230 65 -266 959 -200 99 -298 231 -68 65 -100 97 -398 363 -132 199 -134 133 -134 133 -266 593 -66 363 -66 827 -2374 65 -1724 399 -166 265 -100 331 -198 165 -398 233 -98 233 -66 165 -266 97 -66 231 -132 165 -298 395 -234 99 -132 65 -100 99 -132 131 -66 297 -264 197 -194 229 -530 2189 -166 9577 -3702 199 -98 465 -398 97 -134 395 -132 429 -100 529 -68 263 -132 265 -368 263 -860 97 -100 163 -196 427 -98 163 -166 327 -98 493 -166 327 -98 233 -5094 99 -198 97 -100 65 -1250 131 -560 855 -66 855 -262 859 -164 10219 -7528 761 -66 1121 -100 429 -298 331 -232 263 -166 261 -166 265 -100 1427 -98 9787 -6682 131 -564 429 +RAW_Data: -66 529 -66 2519 -66 265 -68 10101 -1794 65 -1890 393 -562 97 -132 197 -98 493 -330 97 -164 97 -230 327 -326 99 -100 97 -164 65 -132 293 -98 297 -166 161 -130 297 -230 1391 -68 11185 -3800 229 -230 297 -66 65 -198 65 -466 99 -464 99 -430 67 -698 295 -132 165 -164 1095 -66 299 -66 1321 -264 12675 -66 99 -166 229 -134 65 -330 165 -164 65 -890 131 -830 67 -66 1157 -100 167 -168 265 -66 827 -66 2047 -100 261 -594 2279 -134 10701 -3890 163 -1384 67 -98 99 -1322 99 -98 65 -398 823 -66 65 -68 927 -100 495 -132 593 -100 165 -198 1387 -1022 131 -728 99 -662 97 -462 495 -200 829 -330 563 -100 297 -330 65 -598 165 -592 295 -166 131 -764 165 -164 565 -66 131 -166 165 -66 9675 -5052 165 -2878 199 -66 265 -432 265 -66 267 -898 163 -132 231 -198 229 -164 97 -100 4445 -66 7853 -636 199 -662 265 -298 233 -1428 331 -134 1791 -66 1649 -66 297 -100 361 -198 559 -98 363 -200 1315 -66 265 -98 1049 -132 1647 -66 265 -822 295 -526 131 -1712 199 -166 231 -200 165 -66 265 -166 97 -132 163 -164 395 -630 495 -168 297 -298 229 -266 629 -200 133 -132 133 -166 65 -132 99 -100 131 -66 67 -98 133 -496 1391 -98 1751 -164 359 -132 97 -164 263 -64 691 -66 199 -66 293 -98 589 -198 11299 -3968 65 -68 65 -2702 65 -1186 927 -166 65 -66 429 -134 197 -134 529 -200 67 -66 231 -100 2151 -4014 97 -1486 99 -464 65 -330 129 -330 331 -134 599 -66 497 -200 165 -66 661 -166 6881 -8830 295 -100 197 -232 725 -134 299 -166 229 -166 525 -198 295 -66 459 -66 329 -230 595 -98 299 -132 329 -66 99 -98 163 -134 229 -100 8345 -6726 131 -132 295 -66 1579 -66 329 -98 501 -132 231 -66 491 -298 331 -266 363 -132 1193 -168 8847 -4194 199 -828 65 -100 195 -262 197 -298 65 -898 65 -132 629 -66 229 -100 291 -100 623 -66 295 -66 461 -132 529 -632 597 -132 65 -100 97 -134 297 -100 297 -166 397 -168 527 -134 9603 -3850 99 -200 67 -896 959 -198 165 -100 229 -266 531 -64 165 -132 163 -296 3715 -11994 165 -1492 429 -68 263 -100 265 -330 199 -64 495 -132 363 -66 63 -166 297 -398 65 -100 231 -332 199 -100 7683 -4916 65 -1294 297 -1022 1325 -166 393 -132 165 -498 1255 -134 197 -198 427 -164 329 -132 631 -594 199 -196 99 -100 265 -134 1457 -100 3649 -8592 67 -268 131 -332 99 -100 65 -760 101 -198 297 -168 199 -132 369 -100 97 -132 99 -232 397 -198 99 -134 97 -100 231 -332 131 -796 329 -266 263 +RAW_Data: -100 10841 -4030 163 -164 197 -398 195 -592 65 -132 63 -430 295 -298 263 -200 3517 -132 3763 -12296 99 -330 361 -98 99 -200 65 -430 165 -166 2327 -100 4051 -66 9653 -3478 197 -66 163 -198 167 -66 65 -598 165 -298 131 -666 199 -198 299 -298 165 -200 565 -66 797 -98 1125 -98 825 -100 4113 -6956 65 -5536 165 -266 99 -232 461 -198 65 -200 1989 -66 295 -66 723 -66 65 -98 329 -98 955 -66 559 -232 331 -66 10851 -1048 65 -3748 65 -498 99 -1392 99 -794 529 -98 331 -98 397 -164 363 -394 331 -266 299 -230 165 -66 3001 -568 197 -2872 2579 -468 2637 -472 2599 -488 2647 -426 2653 -448 2665 -392 379 -2734 381 -2700 2691 -388 377 -2732 2669 -420 355 -2726 2687 -394 403 -2706 2687 -388 377 -21248 2717 -388 377 -2738 2659 -408 2703 -382 2689 -416 2679 -408 2701 -382 2687 -418 365 -2736 365 -2722 2689 -384 391 -2696 2707 -386 379 -2734 2665 -410 361 -2726 2703 -418 357 -21246 2679 -466 297 -2768 2657 -448 2627 -434 2669 -450 2653 -416 2673 -408 2697 -386 383 -2728 369 -2706 2701 -382 387 -2726 2687 -418 357 -2708 2693 -418 361 -2702 2709 -396 401 -21232 2689 -406 361 -2736 2695 -386 2695 -406 2695 -382 2687 -418 2675 -410 2693 -414 375 -2692 389 -2706 2701 -404 363 -2724 2695 -388 389 -2702 2719 -358 405 -2704 2701 -402 363 -21262 2677 -414 367 -2738 2677 -386 2693 -420 2701 -400 2677 -386 2695 -420 2669 -430 369 -2718 353 -2730 2673 -412 361 -2734 2691 -420 357 -2698 2701 -394 401 -2702 2687 -424 347 -21244 2701 -418 365 -2726 2703 -382 2697 -420 2675 -400 2685 -384 2721 -398 2667 -418 355 -2744 343 -2722 2703 -420 353 -2724 2689 -396 363 -2736 2687 -390 377 -2730 2697 -386 357 -21274 2683 -414 375 -2726 2667 -420 2703 -398 2677 -388 2695 -420 2699 -398 2671 -384 415 -2698 357 -2738 2695 -382 383 -2724 2685 -416 357 -2706 2707 -384 391 -2726 2671 -384 415 -21238 2651 -476 293 -2776 2653 -462 2641 -446 2661 -422 2663 -418 2689 -412 2683 -414 357 -2706 385 -2698 2715 -378 379 -2710 2719 -388 377 -2708 2695 -406 361 -2724 2689 -416 361 -21244 2703 -386 413 -2698 2697 -414 2689 -384 2685 -386 2719 -378 2701 -416 2689 -386 377 -2728 387 -2700 2673 -410 361 -2730 2695 -420 357 -2732 2679 -386 377 -2734 2699 -378 361 -21262 2697 -392 405 -2702 2687 -406 2703 -382 2703 -418 2671 -406 2677 -416 2695 -386 387 -2700 381 -2704 2695 -418 357 -2712 2721 -388 375 -2702 2693 -408 363 -2730 2693 -420 355 -21248 2653 -494 289 -91206 131 -132 97 -232 559 -132 591 -98 691 -66 131 -130 297 -66 231 -66 331 -66 433 -100 499 -132 231 -166 197 -134 593 -100 11707 -4456 133 -200 131 +RAW_Data: -66 133 -66 97 -166 561 -100 895 -132 1323 -66 10873 -3752 99 -722 229 -394 97 -66 99 -98 99 -328 297 -328 265 -298 3089 -132 10573 -1460 133 -432 99 -232 99 -132 333 -232 731 -164 65 -166 165 -132 131 -330 65 -98 131 -596 65 -198 133 -98 397 -568 65 -132 1157 -166 195 -130 131 -64 99 -66 63 -198 265 -98 297 -66 63 -166 295 -100 1747 -232 6099 -11348 199 -528 297 -266 97 -598 99 -198 231 -64 4433 -334 65 -298 65 -3284 67 -530 97 -432 133 -2356 493 -68 231 -168 297 -266 427 -100 559 -98 229 -460 197 -66 261 -132 65 -98 565 -132 231 -66 497 -100 3491 -12356 65 -660 197 -198 165 -132 331 -134 65 -98 2651 -134 4531 -10850 65 -1322 263 -68 431 -232 165 -134 165 -202 231 -300 5625 -66 6951 -8162 65 -398 99 -596 65 -132 461 -598 429 -132 97 -132 463 -232 229 -98 329 -100 397 -100 363 -100 231 -200 163 -200 961 -66 693 -100 397 -134 10601 -3872 263 -100 165 -100 131 -198 99 -696 233 -1524 331 -132 131 -164 229 -132 493 -98 631 -134 231 -100 595 -66 295 -66 5965 -8248 99 -296 99 -98 397 -66 65 -924 229 -398 299 -98 1425 -130 565 -198 827 -262 429 -598 725 -704 729 -12290 131 -98 99 -98 65 -100 163 -164 65 -494 231 -100 97 -100 863 -66 1751 -3948 165 -100 195 -66 165 -296 65 -2042 99 -200 495 -132 557 -100 827 -98 167 -66 433 -100 661 -164 689 -98 10803 -3906 231 -296 295 -232 99 -234 131 -332 395 -266 1283 -164 755 -466 397 -164 335 -66 1355 -14376 557 -66 331 -68 431 -134 599 -364 229 -100 763 -98 265 -132 525 -166 99 -396 495 -98 3867 -134 595 -168 865 -166 503 -200 467 -134 8145 -458 235 -794 599 -458 265 -436 231 -426 333 -368 299 -730 689 -360 327 -370 363 -326 367 -668 733 -328 363 -302 397 -328 371 -296 393 -666 725 -622 795 -634 433 -264 1023 -228 5041 -762 585 -466 233 -826 235 -470 631 -368 299 -402 299 -726 361 -328 331 -370 363 -332 701 -298 401 -692 369 -302 759 -268 461 -236 435 -622 423 -260 465 -266 719 -13608 65 -624 197 -558 921 -164 1315 -134 465 -134 263 -100 295 -132 293 -66 329 -98 197 -132 9977 -5036 197 -798 333 -828 295 -100 197 -100 165 -66 665 -100 763 -300 297 -166 165 -98 823 -8348 229 -100 427 -196 263 -624 197 -134 797 -100 263 -68 529 -132 233 -134 165 -264 131 -132 559 -66 263 -228 927 -132 731 -102 1061 -66 863 -8206 131 -332 299 -166 461 -100 99 -66 429 -66 3271 -98 465 -100 401 -232 331 -66 397 -430 10341 +RAW_Data: -5434 65 -298 133 -132 131 -68 231 -200 661 -132 9517 -424 97 -1456 99 -1694 393 -100 131 -560 131 -196 197 -298 65 -428 229 -196 297 -266 131 -166 2435 -66 10161 -11230 65 -1320 131 -298 265 -532 231 -200 1291 -68 631 -66 12645 -4048 133 -66 67 -132 167 -266 163 -66 397 -132 197 -132 299 -98 197 -198 2903 -66 2361 -66 9627 -3588 197 -332 165 -68 331 -68 197 -132 99 -100 663 -66 363 -230 231 -166 131 -100 201 -298 163 -132 133 -202 363 -300 397 -102 263 -100 165 -66 1221 -66 1479 -132 165 -98 229 -12976 263 -66 363 -134 231 -66 629 -132 327 -100 97 -130 99 -164 227 -64 297 -132 397 -164 425 -198 97 -198 99 -66 365 -164 199 -102 97 -66 1817 -13524 231 -134 16907 -4086 233 -630 65 -396 201 -66 165 -198 67 -198 99 -664 2117 -166 12473 -446 2649 -440 2661 -420 2651 -422 2681 -418 2703 -400 365 -2724 387 -2696 2695 -414 357 -2704 2707 -386 389 -2700 2687 -392 405 -2706 2695 -402 363 -21268 2707 -388 377 -2706 2691 -404 2699 -382 2717 -382 2707 -378 2693 -416 2687 -396 363 -2736 355 -2748 2659 -416 365 -2708 2715 -388 377 -2708 2697 -404 363 -2730 2673 -420 355 -21268 2655 -460 319 -2766 2663 -448 2631 -436 2665 -418 2683 -410 2681 -416 2701 -386 383 -2700 375 -2744 2669 -416 353 -2730 2685 -416 357 -2708 2721 -380 369 -2724 2697 -382 385 -21260 2701 -418 353 -2720 2673 -418 2675 -408 2693 -384 2715 -386 2717 -386 2691 -404 363 -2732 387 -2702 2669 -412 359 -2736 2699 -380 381 -2728 2675 -416 381 -2720 2675 -414 347 -21280 2685 -390 377 -2724 2689 -416 2673 -408 2705 -382 2695 -410 2689 -414 2661 -418 385 -2704 369 -2704 2693 -416 375 -2726 2661 -420 355 -2728 2711 -388 375 -2702 2691 -410 363 -21252 2659 -488 287 -2794 2651 -448 2629 -436 2671 -416 2695 -416 2663 -406 2699 -384 383 -2730 367 -2702 2695 -418 385 -2702 2685 -412 349 -2744 2693 -366 389 -2714 2693 -394 381 -21266 2685 -418 363 -2730 2683 -382 2693 -418 2675 -410 2699 -384 2719 -382 2707 -380 359 -2734 387 -2704 2709 -380 361 -2732 2699 -418 357 -2728 2667 -416 383 -2696 2709 -380 391 -21228 2685 -458 307 -2800 2647 -412 2659 -432 2667 -416 2695 -416 2675 -406 2675 -416 383 -2700 361 -2730 2687 -414 375 -2696 2701 -420 353 -2720 2711 -382 367 -2728 2675 -416 385 -21222 2735 -386 355 -2744 2687 -396 2679 -418 2701 -386 2705 -382 2681 -410 2697 -384 385 -2736 365 -2704 2715 -384 377 -2696 2697 -416 349 -2722 2707 -386 379 -2732 2671 -410 361 -21258 2681 -464 297 -2796 2629 -456 2655 -420 2661 -448 2663 -404 2695 -382 2715 -380 371 -2740 355 -2744 2679 -384 391 -2728 2675 -388 379 +RAW_Data: -2728 2695 -414 357 -2704 2705 -418 357 -21262 2673 -416 383 -2696 2709 -380 2703 -384 2699 -418 2671 -408 2695 -382 2713 -386 379 -2730 357 -2732 2695 -384 383 -2730 2679 -416 357 -2708 2701 -410 349 -2736 2697 -382 385 -21252 2669 -478 289 -2790 2647 -426 2651 -444 2653 -430 2659 -418 2695 -414 2681 -402 349 -2738 383 -2722 2677 -414 347 -2744 2691 -382 369 -2730 2691 -384 383 -2734 2679 -414 347 -21264 2705 -386 379 -2736 2667 -410 2695 -382 2715 -380 2709 -420 2665 -392 2713 -382 383 -2730 365 -2728 2665 -418 383 -2696 2693 -418 357 -2710 2711 -380 375 -2718 2701 -416 357 -21238 2677 -484 311 -2766 2635 -444 2657 -420 2663 -422 2695 -416 2667 -428 2675 -396 363 -73890 133 -98 131 -132 129 -658 99 -66 853 -100 63 -100 361 -98 1589 -66 1231 -132 65 -100 297 -198 65 -132 265 -66 9857 -4672 165 -1030 97 -1394 65 -200 2687 -68 6873 -8336 99 -1156 97 -66 163 -232 163 -262 197 -132 295 -132 263 -166 953 -100 263 -130 393 -164 295 -64 329 -66 393 -164 823 -130 165 -66 6133 -8436 165 -164 265 -266 65 -362 197 -696 3181 -132 363 -98 65 -166 131 -66 399 -132 663 -396 329 -66 7335 -7578 497 -230 627 -264 99 -366 99 -132 131 -134 265 -498 163 -100 1323 -66 265 -66 1129 -100 399 -132 365 -100 795 -68 397 -98 597 -364 297 -132 361 -132 265 -132 8591 -4740 65 -100 131 -166 199 -1088 97 -296 99 -528 131 -98 661 -66 401 -198 1157 -166 361 -164 495 -100 165 -66 297 -100 1423 -66 3067 -5658 67 -6406 197 -1092 65 -530 659 -68 265 -100 991 -68 231 -230 297 -66 327 -66 131 -132 659 -134 131 -100 1183 -132 263 -98 621 -66 2075 -6976 65 -5138 67 -132 129 -664 67 -132 165 -100 331 -466 231 -68 467 -98 563 -66 231 -100 531 -66 465 -66 1023 -166 297 -134 3409 -12290 67 -164 99 -532 133 -166 263 -66 231 -66 721 -64 131 -68 959 -134 495 -100 299 -98 497 -98 365 -100 397 -232 297 -98 531 -66 3029 -12216 265 -132 99 -364 199 -234 131 -66 431 -166 333 -166 397 -132 327 -100 395 -66 197 -132 395 -66 527 -98 295 -100 97 -98 789 -132 363 -132 297 -200 2815 -4914 65 -6620 65 -462 65 -134 297 -66 497 -264 231 -198 2773 -134 365 -100 831 -166 131 -100 297 -132 861 -132 299 -100 561 -66 1381 -6946 65 -5516 231 -266 97 -1362 1093 -68 1621 -134 165 -332 297 -98 361 -228 97 -132 797 -98 3487 -13224 229 -164 65 -132 913 -66 1123 -98 527 -134 929 -98 723 -100 12259 -270 165 -132 67 -132 165 -1326 99 -98 65 -1194 431 -66 695 -66 733 -134 197 +RAW_Data: -134 10801 -166 67 -6130 133 -198 231 -334 365 -98 229 -132 165 -68 231 -166 14501 -524 65 -328 131 -498 129 -1288 65 -494 163 -64 165 -66 527 -132 131 -132 1019 -198 129 -166 393 -198 65 -164 6411 -66 3255 -10642 65 -1320 165 -164 493 -492 559 -264 2555 -66 695 -66 1657 -164 855 -66 4001 -10526 97 -596 133 -298 67 -264 65 -300 65 -100 263 -166 231 -134 99 -100 2703 -68 13643 -4922 297 -100 65 -232 133 -198 331 -300 231 -66 331 -100 12047 -3872 97 -196 65 -494 329 -66 65 -890 97 -98 229 -164 195 -596 797 -66 861 -132 65 -66 231 -100 565 -66 65 -66 1297 -132 265 -66 363 -134 265 -364 297 -164 299 -134 297 -134 495 -98 11309 -3790 131 -1380 65 -758 65 -164 129 -460 65 -360 199 -100 563 -68 497 -198 363 -266 263 -100 165 -66 697 -66 1933 -13594 65 -762 1223 -132 1119 -196 361 -134 131 -100 793 -166 695 -68 231 -68 463 -66 11727 -4204 363 -264 131 -132 133 -1124 97 -100 163 -100 327 -100 331 -198 397 -66 397 -100 395 -100 163 -66 197 -564 1059 -7962 65 -100 65 -198 129 -362 99 -394 197 -296 495 -100 1357 -68 459 -66 593 -66 265 -68 301 -132 465 -66 231 -200 397 -66 397 -232 199 -298 12077 -4350 231 -796 363 -198 133 -264 65 -1132 597 -332 3295 -100 755 -98 231 -164 97 -264 459 -166 759 -164 3265 -12138 99 -232 99 -1228 1025 -100 393 -66 531 -132 693 -132 1063 -66 427 -64 297 -294 229 -98 9723 -5404 67 -466 99 -796 267 -98 201 -100 167 -264 461 -98 1415 -66 861 -66 267 -66 331 -134 1663 -66 2089 -7012 65 -100 101 -4804 431 -728 99 -100 65 -100 995 -134 165 -66 929 -100 65 -66 927 -100 1093 -168 99 -100 497 -66 665 -200 6517 -8312 165 -66 129 -66 559 -166 99 -430 65 -398 67 -66 593 -198 459 -132 261 -132 263 -130 723 -66 459 -100 325 -166 67 -198 559 -66 493 -66 11475 -3896 99 -266 99 -66 197 -1092 129 -198 361 -166 163 -98 263 -196 759 -100 265 -100 365 -630 4635 -12748 65 -1712 461 -100 497 -66 395 -98 265 -98 229 -164 529 -132 297 -66 565 -132 987 -132 8665 -2820 2265 -450 313 -2774 2643 -442 325 -2772 2665 -416 359 -2734 2667 -386 379 -21274 2657 -474 293 -2810 2619 -466 2613 -476 2629 -452 2663 -388 2683 -418 2705 -400 365 -2722 387 -2700 2697 -380 361 -2732 2691 -418 361 -2732 2667 -416 383 -2698 2697 -416 357 -21238 2715 -384 383 -2732 2685 -416 2667 -416 2695 -398 2671 -418 2687 -390 2713 -382 383 -2730 365 -2728 2661 -416 379 -2716 2685 -384 379 -2720 2703 -378 401 -2718 2671 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 06dfb9b4b6..928838d3c8 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -139,3 +139,9 @@ RAW_Data: 277 -244 275 -1360 275 -1340 277 -246 277 -236 261 -1380 275 -1346 277 RAW_Data: 29262 361 -68 2635 -66 24113 -66 1131 -100 4157 -66 26253 -130 621 -18438 99 -298 231 -66 197 -496 753 -230 7503 -16526 65 -396 65 -296 99 -196 293 -64 429 -132 397 -66 329 -66 37701 -66 13475 -100 54967 -64 18209 -18340 97 -462 197 -98 587 -232 97 -100 259 -98 197 -262 297 -64 557 -100 599 -100 333 -234 42493 -13212 6449 -206 173 -214 217 -176 195 -218 181 -218 181 -182 217 -182 217 -176 187 -214 215 -180 217 -182 217 -182 217 -178 185 -424 1177 -388 387 -240 381 -214 181 -398 211 -380 419 -176 217 -394 203 -394 205 -380 189 -402 421 -168 219 -398 393 -190 191 -398 205 -406 185 -402 381 -212 215 -362 241 -378 421 -176 377 -218 197 -378 427 -210 393 -172 429 -172 397 -212 217 -362 389 -228 197 -372 417 -204 395 -210 181 -398 391 -192 201 -216888 761 -200 299 -166 695 -132 15435 -66 5611 -66 21049 -66 4947 -66 2355 -66 1921 -100 2223 -100 2107 -100 397 -98 3643 -66 5301 -98 14205 -66 37371 -246 175 -216 179 -216 177 -224 149 -246 159 -228 181 -212 201 -204 159 -244 151 -254 169 -214 181 -210 197 -182 181 -454 1141 -444 357 -228 361 -246 177 -396 209 -412 367 -188 187 -434 201 -394 185 -406 193 -402 377 -238 181 -386 381 -234 153 -424 205 -412 157 -412 383 -240 181 -398 203 -392 385 -236 371 -212 179 -400 383 -240 359 -210 375 -220 381 -246 175 -394 383 -240 181 -398 363 -222 379 -246 175 -394 383 -204 217 -182856 99 -66 99 -300 133 -402 65 -198 99 -328 65 -100 491 -164 593 -100 3547 -64 361 -66 789 -68 2521 -66 22883 -66 2659 -98 3309 -130 3789 -100 9689 -17178 99 -1388 65 -266 197 -100 131 -134 99 -232 627 -130 233 -66 1949 -100 14567 -198 165 -256 181 -208 159 -214 183 -220 163 -244 149 -246 159 -236 181 -254 141 -226 151 -246 157 -228 181 -212 201 -400 1163 -428 379 -230 355 -244 177 -396 207 -412 367 -222 157 -418 189 -410 207 -412 171 -430 357 -226 165 -404 413 -204 181 -428 173 -428 169 -426 353 -236 173 -414 173 -408 381 -244 337 -222 201 -408 397 -208 393 -204 395 -208 359 -246 177 -394 387 -200 205 -380 415 -202 395 -208 181 -432 357 -226 169 -195084 65 -300 763 -66 297 -364 593 -68 2883 -66 1357 -68 363 -98 3841 -66 3119 -66 5153 -66 4023 -268 143 -246 133 -290 141 -250 139 -254 141 -226 181 -248 137 -254 143 -252 139 -252 143 -230 181 -250 139 -254 145 -436 1135 -448 349 -240 347 -254 157 -434 167 -426 377 -226 157 -434 167 -426 155 -440 163 -434 375 -206 215 -380 381 -234 153 RAW_Data: -424 205 -412 159 -412 381 -240 181 -398 203 -392 387 -236 369 -212 179 -400 383 -240 359 -244 339 -222 381 -246 175 -394 383 -240 181 -398 363 -222 381 -244 175 -392 383 -240 181 -184002 99 -360 63 -330 65 -132 129 -232 97 -198 295 -328 6031 -66 831 -132 3417 -66 2187 -64 2183 -100 6535 -66 1127 -66 2569 -66 2031 -66 2271 -66 2183 -66 3815 -66 3803 -66 493 -66 1909 -66 1627 -98 4805 -17512 67 -2164 131 -498 265 -430 163 -98 97 -64 99 -230 99 -100 229 -230 165 -196 63 -132 99 -66 927 -66 14955 -66 19621 -68 2627 -66 14305 -68 23247 -66 2891 -66 3941 -66 3021 -212 173 -242 181 -218 181 -214 181 -208 157 -250 141 -248 181 -218 179 -214 179 -210 159 -250 179 -214 181 -218 181 -404 1153 -404 389 -244 375 -192 181 -436 161 -414 383 -240 181 -398 205 -392 201 -394 205 -394 365 -246 177 -396 383 -204 217 -398 171 -426 167 -428 353 -242 173 -420 173 -408 373 -220 403 -208 175 -422 381 -194 399 -228 357 -246 355 -210 215 -400 387 -208 181 -398 391 -226 353 -246 177 -398 383 -204 217 -185098 163 -166 525 -98 293 -100 63 -66 229 -66 1183 -66 1507 -66 3089 -98 30187 -66 2847 -19112 133 -364 131 -394 97 -166 295 -66 229 -164 227 -66 263 -130 623 -98 2071 -66 493 -66 787 -98 691 -64 10249 -132 3879 -66 1949 -66 3453 -198 23157 -66 2845 -100 1193 -66 1587 -100 3797 -98 3187 -100 3319 -66 22119 -98 5513 -226 155 -244 153 -256 131 -248 151 -246 159 -262 121 -274 133 -272 127 -244 153 -254 167 -248 145 -244 133 -252 177 -398 1169 -418 381 -238 359 -242 141 -430 169 -426 357 -274 139 -422 171 -442 173 -428 167 -426 353 -236 171 -416 379 -226 149 -436 161 -438 173 -406 381 -234 153 -424 205 -380 389 -244 359 -206 215 -384 381 -246 335 -224 383 -246 355 -244 179 -404 385 -206 181 -432 359 -226 355 -246 175 -398 383 -240 181 -179760 97 -168 727 -66 97 -332 1389 -66 2793 -66 4955 -100 12453 -100 2425 -66 21965 -66 3809 -68 1683 -66 3095 -66 2153 -64 999 -208 173 -220 181 -214 191 -196 181 -212 183 -220 191 -212 181 -214 191 -198 181 -212 181 -222 191 -212 181 -214 191 -416 1167 -424 369 -220 373 -210 209 -390 207 -376 403 -190 187 -418 189 -408 209 -412 173 -428 357 -226 169 -404 399 -208 179 -412 209 -396 169 -428 355 -230 201 -378 205 -406 381 -244 339 -222 193 -400 413 -204 393 -208 347 -220 401 -210 175 -422 383 -202 217 -398 365 -222 377 -246 175 -390 385 -204 217 -179890 165 -1552 131 -164 65 RAW_Data: -1448 361 -17056 131 -134 233 -1462 131 -166 953 -100 261 -164 5077 -272 137 -268 143 -252 141 -248 143 -246 159 -252 141 -244 143 -290 107 -276 145 -244 131 -250 179 -248 143 -252 141 -414 1165 -424 373 -236 359 -242 145 -434 169 -428 355 -230 169 -442 173 -434 157 -406 193 -402 379 -238 181 -422 335 -252 157 -434 167 -428 185 -406 381 -208 211 -390 207 -410 381 -200 373 -236 171 -414 383 -202 393 -210 379 -220 373 -208 211 -390 383 -204 217 -398 365 -220 379 -244 175 -394 381 -240 181 -161030 97 -166 167 -930 593 -2670 1091 -132 229 -98 461 -164 1649 -66 6311 -100 44723 -16832 67 -2656 131 -132 99 -132 263 -100 399 -68 893 -18950 99 -164 165 -198 525 -998 335 -66 565 -66 1057 -17880 97 -360 195 -262 131 -332 625 -98 197 -230 455 -98 9343 -16498 67 -368 131 -598 65 -1066 333 -300 789 -130 757 -66 87207 -16554 97 -3520 97 -786 591 -64 461 -98 21495 -66 24811 -18448 131 -296 491 -134 163 -760 1091 -230 893 -66 927 -68 4581 -68 32965 -64 45217 -17292 131 -1684 231 -132 327 -64 163 -330 263 -230 25751 +RAW_Data: -66 529 -66 2519 -66 265 -68 10101 -1794 65 -1890 393 -562 97 -132 197 -98 493 -330 97 -164 97 -230 327 -326 99 -100 97 -164 65 -132 293 -98 297 -166 161 -130 297 -230 1391 -68 11185 -3800 229 -230 297 -66 65 -198 65 -466 99 -464 99 -430 67 -698 295 -132 165 -164 1095 -66 299 -66 1321 -264 12675 -66 99 -166 229 -134 65 -330 165 -164 65 -890 131 -830 67 -66 1157 -100 167 -168 265 -66 827 -66 2047 -100 261 -594 2279 -134 10701 -3890 163 -1384 67 -98 99 -1322 99 -98 65 -398 823 -66 65 -68 927 -100 495 -132 593 -100 165 -198 1387 -1022 131 -728 99 -662 97 -462 495 -200 829 -330 563 -100 297 -330 65 -598 165 -592 295 -166 131 -764 165 -164 565 -66 131 -166 165 -66 9675 -5052 165 -2878 199 -66 265 -432 265 -66 267 -898 163 -132 231 -198 229 -164 97 -100 4445 -66 7853 -636 199 -662 265 -298 233 -1428 331 -134 1791 -66 1649 -66 297 -100 361 -198 559 -98 363 -200 1315 -66 265 -98 1049 -132 1647 -66 265 -822 295 -526 131 -1712 199 -166 231 -200 165 -66 265 -166 97 -132 163 -164 395 -630 495 -168 297 -298 229 -266 629 -200 133 -132 133 -166 65 -132 99 -100 131 -66 67 -98 133 -496 1391 -98 1751 -164 359 -132 97 -164 263 -64 691 -66 199 -66 293 -98 589 -198 11299 -3968 65 -68 65 -2702 65 -1186 927 -166 65 -66 429 -134 197 -134 529 -200 67 -66 231 -100 2151 -4014 97 -1486 99 -464 65 -330 129 -330 331 -134 599 -66 497 -200 165 -66 661 -166 6881 -8830 295 -100 197 -232 725 -134 299 -166 229 -166 525 -198 295 -66 459 -66 329 -230 595 -98 299 -132 329 -66 99 -98 163 -134 229 -100 8345 -6726 131 -132 295 -66 1579 -66 329 -98 501 -132 231 -66 491 -298 331 -266 363 -132 1193 -168 8847 -4194 199 -828 65 -100 195 -262 197 -298 65 -898 65 -132 629 -66 229 -100 291 -100 623 -66 295 -66 461 -132 529 -632 597 -132 65 -100 97 -134 297 -100 297 -166 397 -168 527 -134 9603 -3850 99 -200 67 -896 959 -198 165 -100 229 -266 531 -64 165 -132 163 -296 3715 -11994 165 -1492 429 -68 263 -100 265 -330 199 -64 495 -132 363 -66 63 -166 297 -398 65 -100 231 -332 199 -100 7683 -4916 65 -1294 297 -1022 1325 -166 393 -132 165 -498 1255 -134 197 -198 427 -164 329 -132 631 -594 199 -196 99 -100 265 -134 1457 -100 3649 -8592 67 -268 131 -332 99 -100 65 -760 101 -198 297 -168 199 -132 369 -100 97 -132 99 -232 397 -198 99 -134 97 -100 231 -332 131 -796 329 -266 263 +RAW_Data: -100 10841 -4030 163 -164 197 -398 195 -592 65 -132 63 -430 295 -298 263 -200 3517 -132 3763 -12296 99 -330 361 -98 99 -200 65 -430 165 -166 2327 -100 4051 -66 9653 -3478 197 -66 163 -198 167 -66 65 -598 165 -298 131 -666 199 -198 299 -298 165 -200 565 -66 797 -98 1125 -98 825 -100 4113 -6956 65 -5536 165 -266 99 -232 461 -198 65 -200 1989 -66 295 -66 723 -66 65 -98 329 -98 955 -66 559 -232 331 -66 10851 -1048 65 -3748 65 -498 99 -1392 99 -794 529 -98 331 -98 397 -164 363 -394 331 -266 299 -230 165 -66 3001 -568 197 -2872 2579 -468 2637 -472 2599 -488 2647 -426 2653 -448 2665 -392 379 -2734 381 -2700 2691 -388 377 -2732 2669 -420 355 -2726 2687 -394 403 -2706 2687 -388 377 -21248 2717 -388 377 -2738 2659 -408 2703 -382 2689 -416 2679 -408 2701 -382 2687 -418 365 -2736 365 -2722 2689 -384 391 -2696 2707 -386 379 -2734 2665 -410 361 -2726 2703 -418 357 -21246 2679 -466 297 -2768 2657 -448 2627 -434 2669 -450 2653 -416 2673 -408 2697 -386 383 -2728 369 -2706 2701 -382 387 -2726 2687 -418 357 -2708 2693 -418 361 -2702 2709 -396 401 -21232 2689 -406 361 -2736 2695 -386 2695 -406 2695 -382 2687 -418 2675 -410 2693 -414 375 -2692 389 -2706 2701 -404 363 -2724 2695 -388 389 -2702 2719 -358 405 -2704 2701 -402 363 -21262 2677 -414 367 -2738 2677 -386 2693 -420 2701 -400 2677 -386 2695 -420 2669 -430 369 -2718 353 -2730 2673 -412 361 -2734 2691 -420 357 -2698 2701 -394 401 -2702 2687 -424 347 -21244 2701 -418 365 -2726 2703 -382 2697 -420 2675 -400 2685 -384 2721 -398 2667 -418 355 -2744 343 -2722 2703 -420 353 -2724 2689 -396 363 -2736 2687 -390 377 -2730 2697 -386 357 -21274 2683 -414 375 -2726 2667 -420 2703 -398 2677 -388 2695 -420 2699 -398 2671 -384 415 -2698 357 -2738 2695 -382 383 -2724 2685 -416 357 -2706 2707 -384 391 -2726 2671 -384 415 -21238 2651 -476 293 -2776 2653 -462 2641 -446 2661 -422 2663 -418 2689 -412 2683 -414 357 -2706 385 -2698 2715 -378 379 -2710 2719 -388 377 -2708 2695 -406 361 -2724 2689 -416 361 -21244 2703 -386 413 -2698 2697 -414 2689 -384 2685 -386 2719 -378 2701 -416 2689 -386 377 -2728 387 -2700 2673 -410 361 -2730 2695 -420 357 -2732 2679 -386 377 -2734 2699 -378 361 -21262 2697 -392 405 -2702 2687 -406 2703 -382 2703 -418 2671 -406 2677 -416 2695 -386 387 -2700 381 -2704 2695 -418 357 -2712 2721 -388 375 -2702 2693 -408 363 -2730 2693 -420 355 -21248 2653 -494 289 -91206 131 -132 97 -232 559 -132 591 -98 691 -66 131 -130 297 -66 231 -66 331 -66 433 -100 499 -132 231 -166 197 -134 593 -100 11707 -4456 133 -200 131 +RAW_Data: -66 133 -66 97 -166 561 -100 895 -132 1323 -66 10873 -3752 99 -722 229 -394 97 -66 99 -98 99 -328 297 -328 265 -298 3089 -132 10573 -1460 133 -432 99 -232 99 -132 333 -232 731 -164 65 -166 165 -132 131 -330 65 -98 131 -596 65 -198 133 -98 397 -568 65 -132 1157 -166 195 -130 131 -64 99 -66 63 -198 265 -98 297 -66 63 -166 295 -100 1747 -232 6099 -11348 199 -528 297 -266 97 -598 99 -198 231 -64 4433 -334 65 -298 65 -3284 67 -530 97 -432 133 -2356 493 -68 231 -168 297 -266 427 -100 559 -98 229 -460 197 -66 261 -132 65 -98 565 -132 231 -66 497 -100 3491 -12356 65 -660 197 -198 165 -132 331 -134 65 -98 2651 -134 4531 -10850 65 -1322 263 -68 431 -232 165 -134 165 -202 231 -300 5625 -66 6951 -8162 65 -398 99 -596 65 -132 461 -598 429 -132 97 -132 463 -232 229 -98 329 -100 397 -100 363 -100 231 -200 163 -200 961 -66 693 -100 397 -134 10601 -3872 263 -100 165 -100 131 -198 99 -696 233 -1524 331 -132 131 -164 229 -132 493 -98 631 -134 231 -100 595 -66 295 -66 5965 -8248 99 -296 99 -98 397 -66 65 -924 229 -398 299 -98 1425 -130 565 -198 827 -262 429 -598 725 -704 729 -12290 131 -98 99 -98 65 -100 163 -164 65 -494 231 -100 97 -100 863 -66 1751 -3948 165 -100 195 -66 165 -296 65 -2042 99 -200 495 -132 557 -100 827 -98 167 -66 433 -100 661 -164 689 -98 10803 -3906 231 -296 295 -232 99 -234 131 -332 395 -266 1283 -164 755 -466 397 -164 335 -66 1355 -14376 557 -66 331 -68 431 -134 599 -364 229 -100 763 -98 265 -132 525 -166 99 -396 495 -98 3867 -134 595 -168 865 -166 503 -200 467 -134 8145 -458 235 -794 599 -458 265 -436 231 -426 333 -368 299 -730 689 -360 327 -370 363 -326 367 -668 733 -328 363 -302 397 -328 371 -296 393 -666 725 -622 795 -634 433 -264 1023 -228 5041 -762 585 -466 233 -826 235 -470 631 -368 299 -402 299 -726 361 -328 331 -370 363 -332 701 -298 401 -692 369 -302 759 -268 461 -236 435 -622 423 -260 465 -266 719 -13608 65 -624 197 -558 921 -164 1315 -134 465 -134 263 -100 295 -132 293 -66 329 -98 197 -132 9977 -5036 197 -798 333 -828 295 -100 197 -100 165 -66 665 -100 763 -300 297 -166 165 -98 823 -8348 229 -100 427 -196 263 -624 197 -134 797 -100 263 -68 529 -132 233 -134 165 -264 131 -132 559 -66 263 -228 927 -132 731 -102 1061 -66 863 -8206 131 -332 299 -166 461 -100 99 -66 429 -66 3271 -98 465 -100 401 -232 331 -66 397 -430 10341 +RAW_Data: -5434 65 -298 133 -132 131 -68 231 -200 661 -132 9517 -424 97 -1456 99 -1694 393 -100 131 -560 131 -196 197 -298 65 -428 229 -196 297 -266 131 -166 2435 -66 10161 -11230 65 -1320 131 -298 265 -532 231 -200 1291 -68 631 -66 12645 -4048 133 -66 67 -132 167 -266 163 -66 397 -132 197 -132 299 -98 197 -198 2903 -66 2361 -66 9627 -3588 197 -332 165 -68 331 -68 197 -132 99 -100 663 -66 363 -230 231 -166 131 -100 201 -298 163 -132 133 -202 363 -300 397 -102 263 -100 165 -66 1221 -66 1479 -132 165 -98 229 -12976 263 -66 363 -134 231 -66 629 -132 327 -100 97 -130 99 -164 227 -64 297 -132 397 -164 425 -198 97 -198 99 -66 365 -164 199 -102 97 -66 1817 -13524 231 -134 16907 -4086 233 -630 65 -396 201 -66 165 -198 67 -198 99 -664 2117 -166 12473 -446 2649 -440 2661 -420 2651 -422 2681 -418 2703 -400 365 -2724 387 -2696 2695 -414 357 -2704 2707 -386 389 -2700 2687 -392 405 -2706 2695 -402 363 -21268 2707 -388 377 -2706 2691 -404 2699 -382 2717 -382 2707 -378 2693 -416 2687 -396 363 -2736 355 -2748 2659 -416 365 -2708 2715 -388 377 -2708 2697 -404 363 -2730 2673 -420 355 -21268 2655 -460 319 -2766 2663 -448 2631 -436 2665 -418 2683 -410 2681 -416 2701 -386 383 -2700 375 -2744 2669 -416 353 -2730 2685 -416 357 -2708 2721 -380 369 -2724 2697 -382 385 -21260 2701 -418 353 -2720 2673 -418 2675 -408 2693 -384 2715 -386 2717 -386 2691 -404 363 -2732 387 -2702 2669 -412 359 -2736 2699 -380 381 -2728 2675 -416 381 -2720 2675 -414 347 -21280 2685 -390 377 -2724 2689 -416 2673 -408 2705 -382 2695 -410 2689 -414 2661 -418 385 -2704 369 -2704 2693 -416 375 -2726 2661 -420 355 -2728 2711 -388 375 -2702 2691 -410 363 -21252 2659 -488 287 -2794 2651 -448 2629 -436 2671 -416 2695 -416 2663 -406 2699 -384 383 -2730 367 -2702 2695 -418 385 -2702 2685 -412 349 -2744 2693 -366 389 -2714 2693 -394 381 -21266 2685 -418 363 -2730 2683 -382 2693 -418 2675 -410 2699 -384 2719 -382 2707 -380 359 -2734 387 -2704 2709 -380 361 -2732 2699 -418 357 -2728 2667 -416 383 -2696 2709 -380 391 -21228 2685 -458 307 -2800 2647 -412 2659 -432 2667 -416 2695 -416 2675 -406 2675 -416 383 -2700 361 -2730 2687 -414 375 -2696 2701 -420 353 -2720 2711 -382 367 -2728 2675 -416 385 -21222 2735 -386 355 -2744 2687 -396 2679 -418 2701 -386 2705 -382 2681 -410 2697 -384 385 -2736 365 -2704 2715 -384 377 -2696 2697 -416 349 -2722 2707 -386 379 -2732 2671 -410 361 -21258 2681 -464 297 -2796 2629 -456 2655 -420 2661 -448 2663 -404 2695 -382 2715 -380 371 -2740 355 -2744 2679 -384 391 -2728 2675 -388 379 +RAW_Data: -2728 2695 -414 357 -2704 2705 -418 357 -21262 2673 -416 383 -2696 2709 -380 2703 -384 2699 -418 2671 -408 2695 -382 2713 -386 379 -2730 357 -2732 2695 -384 383 -2730 2679 -416 357 -2708 2701 -410 349 -2736 2697 -382 385 -21252 2669 -478 289 -2790 2647 -426 2651 -444 2653 -430 2659 -418 2695 -414 2681 -402 349 -2738 383 -2722 2677 -414 347 -2744 2691 -382 369 -2730 2691 -384 383 -2734 2679 -414 347 -21264 2705 -386 379 -2736 2667 -410 2695 -382 2715 -380 2709 -420 2665 -392 2713 -382 383 -2730 365 -2728 2665 -418 383 -2696 2693 -418 357 -2710 2711 -380 375 -2718 2701 -416 357 -21238 2677 -484 311 -2766 2635 -444 2657 -420 2663 -422 2695 -416 2667 -428 2675 -396 363 -73890 133 -98 131 -132 129 -658 99 -66 853 -100 63 -100 361 -98 1589 -66 1231 -132 65 -100 297 -198 65 -132 265 -66 9857 -4672 165 -1030 97 -1394 65 -200 2687 -68 6873 -8336 99 -1156 97 -66 163 -232 163 -262 197 -132 295 -132 263 -166 953 -100 263 -130 393 -164 295 -64 329 -66 393 -164 823 -130 165 -66 6133 -8436 165 -164 265 -266 65 -362 197 -696 3181 -132 363 -98 65 -166 131 -66 399 -132 663 -396 329 -66 7335 -7578 497 -230 627 -264 99 -366 99 -132 131 -134 265 -498 163 -100 1323 -66 265 -66 1129 -100 399 -132 365 -100 795 -68 397 -98 597 -364 297 -132 361 -132 265 -132 8591 -4740 65 -100 131 -166 199 -1088 97 -296 99 -528 131 -98 661 -66 401 -198 1157 -166 361 -164 495 -100 165 -66 297 -100 1423 -66 3067 -5658 67 -6406 197 -1092 65 -530 659 -68 265 -100 991 -68 231 -230 297 -66 327 -66 131 -132 659 -134 131 -100 1183 -132 263 -98 621 -66 2075 -6976 65 -5138 67 -132 129 -664 67 -132 165 -100 331 -466 231 -68 467 -98 563 -66 231 -100 531 -66 465 -66 1023 -166 297 -134 3409 -12290 67 -164 99 -532 133 -166 263 -66 231 -66 721 -64 131 -68 959 -134 495 -100 299 -98 497 -98 365 -100 397 -232 297 -98 531 -66 3029 -12216 265 -132 99 -364 199 -234 131 -66 431 -166 333 -166 397 -132 327 -100 395 -66 197 -132 395 -66 527 -98 295 -100 97 -98 789 -132 363 -132 297 -200 2815 -4914 65 -6620 65 -462 65 -134 297 -66 497 -264 231 -198 2773 -134 365 -100 831 -166 131 -100 297 -132 861 -132 299 -100 561 -66 1381 -6946 65 -5516 231 -266 97 -1362 1093 -68 1621 -134 165 -332 297 -98 361 -228 97 -132 797 -98 3487 -13224 229 -164 65 -132 913 -66 1123 -98 527 -134 929 -98 723 -100 12259 -270 165 -132 67 -132 165 -1326 99 -98 65 -1194 431 -66 695 -66 733 -134 197 +RAW_Data: -134 10801 -166 67 -6130 133 -198 231 -334 365 -98 229 -132 165 -68 231 -166 14501 -524 65 -328 131 -498 129 -1288 65 -494 163 -64 165 -66 527 -132 131 -132 1019 -198 129 -166 393 -198 65 -164 6411 -66 3255 -10642 65 -1320 165 -164 493 -492 559 -264 2555 -66 695 -66 1657 -164 855 -66 4001 -10526 97 -596 133 -298 67 -264 65 -300 65 -100 263 -166 231 -134 99 -100 2703 -68 13643 -4922 297 -100 65 -232 133 -198 331 -300 231 -66 331 -100 12047 -3872 97 -196 65 -494 329 -66 65 -890 97 -98 229 -164 195 -596 797 -66 861 -132 65 -66 231 -100 565 -66 65 -66 1297 -132 265 -66 363 -134 265 -364 297 -164 299 -134 297 -134 495 -98 11309 -3790 131 -1380 65 -758 65 -164 129 -460 65 -360 199 -100 563 -68 497 -198 363 -266 263 -100 165 -66 697 -66 1933 -13594 65 -762 1223 -132 1119 -196 361 -134 131 -100 793 -166 695 -68 231 -68 463 -66 11727 -4204 363 -264 131 -132 133 -1124 97 -100 163 -100 327 -100 331 -198 397 -66 397 -100 395 -100 163 -66 197 -564 1059 -7962 65 -100 65 -198 129 -362 99 -394 197 -296 495 -100 1357 -68 459 -66 593 -66 265 -68 301 -132 465 -66 231 -200 397 -66 397 -232 199 -298 12077 -4350 231 -796 363 -198 133 -264 65 -1132 597 -332 3295 -100 755 -98 231 -164 97 -264 459 -166 759 -164 3265 -12138 99 -232 99 -1228 1025 -100 393 -66 531 -132 693 -132 1063 -66 427 -64 297 -294 229 -98 9723 -5404 67 -466 99 -796 267 -98 201 -100 167 -264 461 -98 1415 -66 861 -66 267 -66 331 -134 1663 -66 2089 -7012 65 -100 101 -4804 431 -728 99 -100 65 -100 995 -134 165 -66 929 -100 65 -66 927 -100 1093 -168 99 -100 497 -66 665 -200 6517 -8312 165 -66 129 -66 559 -166 99 -430 65 -398 67 -66 593 -198 459 -132 261 -132 263 -130 723 -66 459 -100 325 -166 67 -198 559 -66 493 -66 11475 -3896 99 -266 99 -66 197 -1092 129 -198 361 -166 163 -98 263 -196 759 -100 265 -100 365 -630 4635 -12748 65 -1712 461 -100 497 -66 395 -98 265 -98 229 -164 529 -132 297 -66 565 -132 987 -132 8665 -2820 2265 -450 313 -2774 2643 -442 325 -2772 2665 -416 359 -2734 2667 -386 379 -21274 2657 -474 293 -2810 2619 -466 2613 -476 2629 -452 2663 -388 2683 -418 2705 -400 365 -2722 387 -2700 2697 -380 361 -2732 2691 -418 361 -2732 2667 -416 383 -2698 2697 -416 357 -21238 2715 -384 383 -2732 2685 -416 2667 -416 2695 -398 2671 -418 2687 -390 2713 -382 383 -2730 365 -2728 2661 -416 379 -2716 2685 -384 379 -2720 2703 -378 401 -2718 2671 diff --git a/fbt_options.py b/fbt_options.py index 73f5d191b0..ab70ae8aea 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -86,19 +86,19 @@ # Custom Games "custom_games", # Debug - "debug_apps", + "debug_apps_lite", ], "unit_tests": [ "basic_services", "updater_app", "unit_tests", ], - "no_custom_apps": [ + "debug_pack": [ "crypto_start", # Svc "basic_services", # Apps - "basic_apps", + #"basic_apps", "updater_app", "storage_move_to_sd", "archive", @@ -107,7 +107,7 @@ "system_settings", "about", # Plugins - "basic_plugins", + #"basic_plugins", # Debug "debug_apps", ], diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index bd7fbc3fcc..9467748195 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -217,7 +217,6 @@ bool furi_hal_nfc_listen( } rfalLowPowerModeStop(); rfalNfcDiscoverParam params = { - .compMode = RFAL_COMPLIANCE_MODE_NFC, .techs2Find = RFAL_NFC_LISTEN_TECH_A, .totalDuration = 1000, .devLimit = 1, @@ -230,6 +229,11 @@ bool furi_hal_nfc_listen( .notifyCb = NULL, .activate_after_sak = activate_after_sak, }; + if(FURI_BIT(sak, 5)) { + params.compMode = RFAL_COMPLIANCE_MODE_EMV; + } else { + params.compMode = RFAL_COMPLIANCE_MODE_NFC; + } params.lmConfigPA.nfcidLen = uid_len; memcpy(params.lmConfigPA.nfcid, uid, uid_len); params.lmConfigPA.SENS_RES[0] = atqa[0]; @@ -271,6 +275,10 @@ void furi_hal_nfc_listen_sleep() { st25r3916ExecuteCommand(ST25R3916_CMD_GOTO_SLEEP); } +void furi_hal_nfc_stop_cmd() { + st25r3916ExecuteCommand(ST25R3916_CMD_STOP); +} + bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) { furi_assert(tx_rx); @@ -283,6 +291,9 @@ bool furi_hal_nfc_listen_rx(FuriHalNfcTxRxContext* tx_rx, uint32_t timeout_ms) { if(st25r3916GetInterrupt(ST25R3916_IRQ_MASK_RXE)) { furi_hal_nfc_read_fifo(tx_rx->rx_data, &tx_rx->rx_bits); data_received = true; + if(tx_rx->sniff_rx) { + tx_rx->sniff_rx(tx_rx->rx_data, tx_rx->rx_bits, false, tx_rx->sniff_context); + } break; } continue; @@ -497,14 +508,14 @@ static bool furi_hal_nfc_transparent_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_ furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); st25r3916ExecuteCommand(ST25R3916_CMD_UNMASK_RECEIVE_DATA); - if(tx_rx->sniff_tx) { - tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context); - } - // Manually wait for interrupt furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); st25r3916ClearAndEnableInterrupts(ST25R3916_IRQ_MASK_RXE); + if(tx_rx->sniff_tx) { + tx_rx->sniff_tx(tx_rx->tx_data, tx_rx->tx_bits, false, tx_rx->sniff_context); + } + uint32_t irq = 0; uint8_t rxe = 0; uint32_t start = DWT->CYCCNT; diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/furi_hal_include/furi_hal_nfc.h index 71186076f6..0bcb45068d 100644 --- a/firmware/targets/furi_hal_include/furi_hal_nfc.h +++ b/firmware/targets/furi_hal_include/furi_hal_nfc.h @@ -120,6 +120,8 @@ void furi_hal_nfc_field_off(); */ void furi_hal_nfc_start_sleep(); +void furi_hal_nfc_stop_cmd(); + /** NFC stop sleep */ void furi_hal_nfc_exit_sleep(); diff --git a/furi/core/core_defines.h b/furi/core/core_defines.h index 801810f6a1..487fe2ca30 100644 --- a/furi/core/core_defines.h +++ b/furi/core/core_defines.h @@ -85,7 +85,11 @@ extern "C" { #endif #ifndef FURI_BIT_SET -#define FURI_BIT_SET(x, n) ((x) |= (1 << (n))) +#define FURI_BIT_SET(x, n) \ + ({ \ + __typeof__(x) _x = (1); \ + (x) |= (_x << (n)); \ + }) #endif #ifndef FURI_BIT_CLEAR diff --git a/furi/core/timer.c b/furi/core/timer.c index 807f477e4e..462a2e89ea 100644 --- a/furi/core/timer.c +++ b/furi/core/timer.c @@ -1,5 +1,7 @@ #include "timer.h" #include "check.h" +#include "memmgr.h" +#include "kernel.h" #include "core/common_defines.h" #include @@ -39,7 +41,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co /* Dynamic memory allocation is available: if memory for callback and */ /* its context is not provided, allocate it from dynamic memory pool */ if(callb == NULL) { - callb = (TimerCallback_t*)pvPortMalloc(sizeof(TimerCallback_t)); + callb = (TimerCallback_t*)malloc(sizeof(TimerCallback_t)); if(callb != NULL) { /* Callback memory was allocated from dynamic pool, set flag */ @@ -65,7 +67,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co if((hTimer == NULL) && (callb != NULL) && (callb_dyn == 1U)) { /* Failed to create a timer, release allocated resources */ callb = (TimerCallback_t*)((uint32_t)callb & ~1U); - vPortFree(callb); + free(callb); } } @@ -82,14 +84,16 @@ void furi_timer_free(FuriTimer* instance) { callb = (TimerCallback_t*)pvTimerGetTimerID(hTimer); - if(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS) { - if((uint32_t)callb & 1U) { - /* Callback memory was allocated from dynamic pool, clear flag */ - callb = (TimerCallback_t*)((uint32_t)callb & ~1U); + furi_check(xTimerDelete(hTimer, portMAX_DELAY) == pdPASS); - /* Return allocated memory to dynamic pool */ - vPortFree(callb); - } + while (furi_timer_is_running(instance)) furi_delay_tick(2); + + if((uint32_t)callb & 1U) { + /* Callback memory was allocated from dynamic pool, clear flag */ + callb = (TimerCallback_t*)((uint32_t)callb & ~1U); + + /* Return allocated memory to dynamic pool */ + free(callb); } } @@ -120,11 +124,8 @@ FuriStatus furi_timer_stop(FuriTimer* instance) { if(xTimerIsTimerActive(hTimer) == pdFALSE) { stat = FuriStatusErrorResource; } else { - if(xTimerStop(hTimer, portMAX_DELAY) == pdPASS) { - stat = FuriStatusOk; - } else { - stat = FuriStatusError; - } + furi_check(xTimerStop(hTimer, portMAX_DELAY) == pdPASS); + stat = FuriStatusOk; } /* Return execution status */ diff --git a/lib/lfrfid/protocols/lfrfid_protocols.c b/lib/lfrfid/protocols/lfrfid_protocols.c index fc65861062..bd29bd8e09 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.c +++ b/lib/lfrfid/protocols/lfrfid_protocols.c @@ -13,6 +13,8 @@ #include "protocol_jablotron.h" #include "protocol_paradox.h" #include "protocol_pac_stanley.h" +#include "protocol_keri.h" +#include "protocol_gallagher.h" const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolEM4100] = &protocol_em4100, @@ -29,4 +31,6 @@ const ProtocolBase* lfrfid_protocols[] = { [LFRFIDProtocolJablotron] = &protocol_jablotron, [LFRFIDProtocolParadox] = &protocol_paradox, [LFRFIDProtocolPACStanley] = &protocol_pac_stanley, + [LFRFIDProtocolKeri] = &protocol_keri, + [LFRFIDProtocolGallagher] = &protocol_gallagher, }; \ No newline at end of file diff --git a/lib/lfrfid/protocols/lfrfid_protocols.h b/lib/lfrfid/protocols/lfrfid_protocols.h index 210ddd15a6..26065c9aa6 100644 --- a/lib/lfrfid/protocols/lfrfid_protocols.h +++ b/lib/lfrfid/protocols/lfrfid_protocols.h @@ -22,6 +22,8 @@ typedef enum { LFRFIDProtocolJablotron, LFRFIDProtocolParadox, LFRFIDProtocolPACStanley, + LFRFIDProtocolKeri, + LFRFIDProtocolGallagher, LFRFIDProtocolMax, } LFRFIDProtocol; diff --git a/lib/lfrfid/protocols/protocol_gallagher.c b/lib/lfrfid/protocols/protocol_gallagher.c new file mode 100644 index 0000000000..c205ab8a50 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_gallagher.c @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include "lfrfid_protocols.h" + +#define GALLAGHER_CLOCK_PER_BIT (32) + +#define GALLAGHER_ENCODED_BIT_SIZE (96) +#define GALLAGHER_ENCODED_BYTE_SIZE ((GALLAGHER_ENCODED_BIT_SIZE) / 8) +#define GALLAGHER_PREAMBLE_BIT_SIZE (16) +#define GALLAGHER_PREAMBLE_BYTE_SIZE ((GALLAGHER_PREAMBLE_BIT_SIZE) / 8) +#define GALLAGHER_ENCODED_BYTE_FULL_SIZE \ + (GALLAGHER_ENCODED_BYTE_SIZE + GALLAGHER_PREAMBLE_BYTE_SIZE) +#define GALLAGHER_DECODED_DATA_SIZE 8 + +#define GALLAGHER_READ_SHORT_TIME (128) +#define GALLAGHER_READ_LONG_TIME (256) +#define GALLAGHER_READ_JITTER_TIME (60) + +#define GALLAGHER_READ_SHORT_TIME_LOW (GALLAGHER_READ_SHORT_TIME - GALLAGHER_READ_JITTER_TIME) +#define GALLAGHER_READ_SHORT_TIME_HIGH (GALLAGHER_READ_SHORT_TIME + GALLAGHER_READ_JITTER_TIME) +#define GALLAGHER_READ_LONG_TIME_LOW (GALLAGHER_READ_LONG_TIME - GALLAGHER_READ_JITTER_TIME) +#define GALLAGHER_READ_LONG_TIME_HIGH (GALLAGHER_READ_LONG_TIME + GALLAGHER_READ_JITTER_TIME) + +typedef struct { + uint8_t data[GALLAGHER_DECODED_DATA_SIZE]; + uint8_t encoded_data[GALLAGHER_ENCODED_BYTE_FULL_SIZE]; + + uint8_t encoded_data_index; + bool encoded_polarity; + + ManchesterState decoder_manchester_state; +} ProtocolGallagher; + +ProtocolGallagher* protocol_gallagher_alloc(void) { + ProtocolGallagher* proto = malloc(sizeof(ProtocolGallagher)); + return (void*)proto; +}; + +void protocol_gallagher_free(ProtocolGallagher* protocol) { + free(protocol); +}; + +uint8_t* protocol_gallagher_get_data(ProtocolGallagher* protocol) { + return protocol->data; +}; + +static void protocol_gallagher_scramble(uint8_t* data, size_t length) { + const uint8_t lut[] = { + 0xa3, 0xb0, 0x80, 0xc6, 0xb2, 0xf4, 0x5c, 0x6c, 0x81, 0xf1, 0xbb, 0xeb, 0x55, 0x67, 0x3c, + 0x05, 0x1a, 0x0e, 0x61, 0xf6, 0x22, 0xce, 0xaa, 0x8f, 0xbd, 0x3b, 0x1f, 0x5e, 0x44, 0x04, + 0x51, 0x2e, 0x4d, 0x9a, 0x84, 0xea, 0xf8, 0x66, 0x74, 0x29, 0x7f, 0x70, 0xd8, 0x31, 0x7a, + 0x6d, 0xa4, 0x00, 0x82, 0xb9, 0x5f, 0xb4, 0x16, 0xab, 0xff, 0xc2, 0x39, 0xdc, 0x19, 0x65, + 0x57, 0x7c, 0x20, 0xfa, 0x5a, 0x49, 0x13, 0xd0, 0xfb, 0xa8, 0x91, 0x73, 0xb1, 0x33, 0x18, + 0xbe, 0x21, 0x72, 0x48, 0xb6, 0xdb, 0xa0, 0x5d, 0xcc, 0xe6, 0x17, 0x27, 0xe5, 0xd4, 0x53, + 0x42, 0xf3, 0xdd, 0x7b, 0x24, 0xac, 0x2b, 0x58, 0x1e, 0xa7, 0xe7, 0x86, 0x40, 0xd3, 0x98, + 0x97, 0x71, 0xcb, 0x3a, 0x0f, 0x01, 0x9b, 0x6e, 0x1b, 0xfc, 0x34, 0xa6, 0xda, 0x07, 0x0c, + 0xae, 0x37, 0xca, 0x54, 0xfd, 0x26, 0xfe, 0x0a, 0x45, 0xa2, 0x2a, 0xc4, 0x12, 0x0d, 0xf5, + 0x4f, 0x69, 0xe0, 0x8a, 0x77, 0x60, 0x3f, 0x99, 0x95, 0xd2, 0x38, 0x36, 0x62, 0xb7, 0x32, + 0x7e, 0x79, 0xc0, 0x46, 0x93, 0x2f, 0xa5, 0xba, 0x5b, 0xaf, 0x52, 0x1d, 0xc3, 0x75, 0xcf, + 0xd6, 0x4c, 0x83, 0xe8, 0x3d, 0x30, 0x4e, 0xbc, 0x08, 0x2d, 0x09, 0x06, 0xd9, 0x25, 0x9e, + 0x89, 0xf2, 0x96, 0x88, 0xc1, 0x8c, 0x94, 0x0b, 0x28, 0xf0, 0x47, 0x63, 0xd5, 0xb3, 0x68, + 0x56, 0x9c, 0xf9, 0x6f, 0x41, 0x50, 0x85, 0x8b, 0x9d, 0x59, 0xbf, 0x9f, 0xe2, 0x8e, 0x6a, + 0x11, 0x23, 0xa1, 0xcd, 0xb5, 0x7d, 0xc7, 0xa9, 0xc8, 0xef, 0xdf, 0x02, 0xb8, 0x03, 0x6b, + 0x35, 0x3e, 0x2c, 0x76, 0xc9, 0xde, 0x1c, 0x4b, 0xd1, 0xed, 0x14, 0xc5, 0xad, 0xe9, 0x64, + 0x4a, 0xec, 0x8d, 0xf7, 0x10, 0x43, 0x78, 0x15, 0x87, 0xe4, 0xd7, 0x92, 0xe1, 0xee, 0xe3, + 0x90}; + for(size_t i = 0; i < length; i++) { + data[i] = lut[data[i]]; + } +} + +static void protocol_gallagher_descramble(uint8_t* data, size_t length) { + const uint8_t lut[] = { + 0x2f, 0x6e, 0xdd, 0xdf, 0x1d, 0x0f, 0xb0, 0x76, 0xad, 0xaf, 0x7f, 0xbb, 0x77, 0x85, 0x11, + 0x6d, 0xf4, 0xd2, 0x84, 0x42, 0xeb, 0xf7, 0x34, 0x55, 0x4a, 0x3a, 0x10, 0x71, 0xe7, 0xa1, + 0x62, 0x1a, 0x3e, 0x4c, 0x14, 0xd3, 0x5e, 0xb2, 0x7d, 0x56, 0xbc, 0x27, 0x82, 0x60, 0xe3, + 0xae, 0x1f, 0x9b, 0xaa, 0x2b, 0x95, 0x49, 0x73, 0xe1, 0x92, 0x79, 0x91, 0x38, 0x6c, 0x19, + 0x0e, 0xa9, 0xe2, 0x8d, 0x66, 0xc7, 0x5a, 0xf5, 0x1c, 0x80, 0x99, 0xbe, 0x4e, 0x41, 0xf0, + 0xe8, 0xa6, 0x20, 0xab, 0x87, 0xc8, 0x1e, 0xa0, 0x59, 0x7b, 0x0c, 0xc3, 0x3c, 0x61, 0xcc, + 0x40, 0x9e, 0x06, 0x52, 0x1b, 0x32, 0x8c, 0x12, 0x93, 0xbf, 0xef, 0x3b, 0x25, 0x0d, 0xc2, + 0x88, 0xd1, 0xe0, 0x07, 0x2d, 0x70, 0xc6, 0x29, 0x6a, 0x4d, 0x47, 0x26, 0xa3, 0xe4, 0x8b, + 0xf6, 0x97, 0x2c, 0x5d, 0x3d, 0xd7, 0x96, 0x28, 0x02, 0x08, 0x30, 0xa7, 0x22, 0xc9, 0x65, + 0xf8, 0xb7, 0xb4, 0x8a, 0xca, 0xb9, 0xf2, 0xd0, 0x17, 0xff, 0x46, 0xfb, 0x9a, 0xba, 0x8f, + 0xb6, 0x69, 0x68, 0x8e, 0x21, 0x6f, 0xc4, 0xcb, 0xb3, 0xce, 0x51, 0xd4, 0x81, 0x00, 0x2e, + 0x9c, 0x74, 0x63, 0x45, 0xd9, 0x16, 0x35, 0x5f, 0xed, 0x78, 0x9f, 0x01, 0x48, 0x04, 0xc1, + 0x33, 0xd6, 0x4f, 0x94, 0xde, 0x31, 0x9d, 0x0a, 0xac, 0x18, 0x4b, 0xcd, 0x98, 0xb8, 0x37, + 0xa2, 0x83, 0xec, 0x03, 0xd8, 0xda, 0xe5, 0x7a, 0x6b, 0x53, 0xd5, 0x15, 0xa4, 0x43, 0xe9, + 0x90, 0x67, 0x58, 0xc0, 0xa5, 0xfa, 0x2a, 0xb1, 0x75, 0x50, 0x39, 0x5c, 0xe6, 0xdc, 0x89, + 0xfc, 0xcf, 0xfe, 0xf9, 0x57, 0x54, 0x64, 0xa8, 0xee, 0x23, 0x0b, 0xf1, 0xea, 0xfd, 0xdb, + 0xbd, 0x09, 0xb5, 0x5b, 0x05, 0x86, 0x13, 0xf3, 0x24, 0xc5, 0x3f, 0x44, 0x72, 0x7c, 0x7e, + 0x36}; + + for(size_t i = 0; i < length; i++) { + data[i] = lut[data[i]]; + } +} + +static void protocol_gallagher_decode(ProtocolGallagher* protocol) { + bit_lib_remove_bit_every_nth(protocol->encoded_data, 16, 9 * 8, 9); + protocol_gallagher_descramble(protocol->encoded_data + 2, 8); + + // Region code + bit_lib_set_bits(protocol->data, 0, (protocol->encoded_data[5] & 0x1E) >> 1, 4); + + // Issue Level + bit_lib_set_bits(protocol->data, 4, (protocol->encoded_data[9] & 0x0F), 4); + + // Facility Code + uint32_t fc = (protocol->encoded_data[7] & 0x0F) << 12 | protocol->encoded_data[3] << 4 | + ((protocol->encoded_data[9] >> 4) & 0x0F); + protocol->data[3] = (uint8_t)fc; + protocol->data[2] = (uint8_t)(fc >>= 8); + protocol->data[1] = (uint8_t)(fc >>= 8); + + // Card Number + uint32_t card = protocol->encoded_data[2] << 16 | (protocol->encoded_data[6] & 0x1F) << 11 | + protocol->encoded_data[4] << 3 | (protocol->encoded_data[5] & 0xE0) >> 5; + protocol->data[7] = (uint8_t)card; + protocol->data[6] = (uint8_t)(card >>= 8); + protocol->data[5] = (uint8_t)(card >>= 8); + protocol->data[4] = (uint8_t)(card >>= 8); +} + +static bool protocol_gallagher_can_be_decoded(ProtocolGallagher* protocol) { + // check 16 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 0, 16) != 0b0111111111101010) return false; + + // check next 16 bits preamble + if(bit_lib_get_bits_16(protocol->encoded_data, 96, 16) != 0b0111111111101010) return false; + + uint8_t checksum_arr[8] = {0}; + for(int i = 0, pos = 0; i < 8; i++) { + // Following the preamble, every 9th bit is a checksum-bit for the preceding byte + pos = 16 + (9 * i); + checksum_arr[i] = bit_lib_get_bits(protocol->encoded_data, pos, 8); + } + uint8_t crc = bit_lib_get_bits(protocol->encoded_data, 16 + (9 * 8), 8); + uint8_t calc_crc = bit_lib_crc8(checksum_arr, 8, 0x7, 0x2c, false, false, 0x00); + + // crc + if(crc != calc_crc) return false; + + return true; +} + +void protocol_gallagher_decoder_start(ProtocolGallagher* protocol) { + memset(protocol->encoded_data, 0, GALLAGHER_ENCODED_BYTE_FULL_SIZE); + manchester_advance( + protocol->decoder_manchester_state, + ManchesterEventReset, + &protocol->decoder_manchester_state, + NULL); +}; + +bool protocol_gallagher_decoder_feed(ProtocolGallagher* protocol, bool level, uint32_t duration) { + bool result = false; + + ManchesterEvent event = ManchesterEventReset; + + if(duration > GALLAGHER_READ_SHORT_TIME_LOW && duration < GALLAGHER_READ_SHORT_TIME_HIGH) { + if(!level) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(duration > GALLAGHER_READ_LONG_TIME_LOW && duration < GALLAGHER_READ_LONG_TIME_HIGH) { + if(!level) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + protocol->decoder_manchester_state, event, &protocol->decoder_manchester_state, &data); + + if(data_ok) { + bit_lib_push_bit(protocol->encoded_data, GALLAGHER_ENCODED_BYTE_FULL_SIZE, data); + + if(protocol_gallagher_can_be_decoded(protocol)) { + protocol_gallagher_decode(protocol); + result = true; + } + } + } + + return result; +}; + +bool protocol_gallagher_encoder_start(ProtocolGallagher* protocol) { + // Preamble + bit_lib_set_bits(protocol->encoded_data, 0, 0b01111111, 8); + bit_lib_set_bits(protocol->encoded_data, 8, 0b11101010, 8); + + uint8_t rc = bit_lib_get_bits(protocol->data, 0, 4); + uint8_t il = bit_lib_get_bits(protocol->data, 4, 4); + uint32_t fc = bit_lib_get_bits_32(protocol->data, 8, 24); + uint32_t cn = bit_lib_get_bits_32(protocol->data, 32, 32); + + uint8_t payload[8] = {0}; + payload[0] = (cn & 0xffffff) >> 16; + payload[1] = (fc & 0xfff) >> 4; + payload[2] = (cn & 0x7ff) >> 3; + payload[3] = (cn & 0x7) << 5 | (rc & 0xf) << 1; + payload[4] = (cn & 0xffff) >> 11; + payload[5] = (fc & 0xffff) >> 12; + payload[6] = 0; + payload[7] = (fc & 0xf) << 4 | (il & 0xf); + + // Gallagher scramble + protocol_gallagher_scramble(payload, 8); + + for(int i = 0; i < 8; i++) { + // data byte + bit_lib_set_bits(protocol->encoded_data, 16 + (i * 9), payload[i], 8); + + // every byte is followed by a bit which is the inverse of the last bit + bit_lib_set_bit(protocol->encoded_data, 16 + (i * 9) + 8, !(payload[i] & 0x1)); + } + + // checksum + uint8_t crc = bit_lib_crc8(payload, 8, 0x7, 0x2c, false, false, 0x00); + bit_lib_set_bits(protocol->encoded_data, 16 + (9 * 8), crc, 8); + + return true; +}; + +LevelDuration protocol_gallagher_encoder_yield(ProtocolGallagher* protocol) { + bool level = bit_lib_get_bit(protocol->encoded_data, protocol->encoded_data_index); + uint32_t duration = GALLAGHER_CLOCK_PER_BIT / 2; + + if(protocol->encoded_polarity) { + protocol->encoded_polarity = false; + } else { + level = !level; + + protocol->encoded_polarity = true; + bit_lib_increment_index(protocol->encoded_data_index, GALLAGHER_ENCODED_BIT_SIZE); + } + + return level_duration_make(level, duration); +}; + +bool protocol_gallagher_write_data(ProtocolGallagher* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_gallagher_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = + (LFRFID_T5577_MODULATION_MANCHESTER | LFRFID_T5577_BITRATE_RF_32 | + (3 << LFRFID_T5577_MAXBLOCK_SHIFT)); + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.block[3] = bit_lib_get_bits_32(protocol->encoded_data, 64, 32); + request->t5577.blocks_to_write = 4; + result = true; + } + return result; +}; + +void protocol_gallagher_render_data(ProtocolGallagher* protocol, string_t result) { + UNUSED(protocol); + uint8_t rc = bit_lib_get_bits(protocol->data, 0, 4); + uint8_t il = bit_lib_get_bits(protocol->data, 4, 4); + uint32_t fc = bit_lib_get_bits_32(protocol->data, 8, 24); + uint32_t card_id = bit_lib_get_bits_32(protocol->data, 32, 32); + + string_cat_printf(result, "Region: %u, Issue Level: %u\r\n", rc, il); + string_cat_printf(result, "FC: %u, C: %lu\r\n", fc, card_id); +}; + +const ProtocolBase protocol_gallagher = { + .name = "Gallagher", + .manufacturer = "Gallagher", + .data_size = GALLAGHER_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_gallagher_alloc, + .free = (ProtocolFree)protocol_gallagher_free, + .get_data = (ProtocolGetData)protocol_gallagher_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_gallagher_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_gallagher_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_gallagher_encoder_start, + .yield = (ProtocolEncoderYield)protocol_gallagher_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_gallagher_render_data, + .render_brief_data = (ProtocolRenderData)protocol_gallagher_render_data, + .write_data = (ProtocolWriteData)protocol_gallagher_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_gallagher.h b/lib/lfrfid/protocols/protocol_gallagher.h new file mode 100644 index 0000000000..2d922f605e --- /dev/null +++ b/lib/lfrfid/protocols/protocol_gallagher.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_gallagher; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_keri.c b/lib/lfrfid/protocols/protocol_keri.c new file mode 100644 index 0000000000..7e66255463 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_keri.c @@ -0,0 +1,264 @@ +#include +#include +#include +#include "lfrfid_protocols.h" + +#define KERI_PREAMBLE_BIT_SIZE (33) +#define KERI_PREAMBLE_DATA_SIZE (5) + +#define KERI_ENCODED_BIT_SIZE (64) +#define KERI_ENCODED_DATA_SIZE (((KERI_ENCODED_BIT_SIZE) / 8) + KERI_PREAMBLE_DATA_SIZE) +#define KERI_ENCODED_DATA_LAST ((KERI_ENCODED_BIT_SIZE) / 8) + +#define KERI_DECODED_BIT_SIZE (28) +#define KERI_DECODED_DATA_SIZE (4) + +#define KERI_US_PER_BIT (255) +#define KERI_ENCODER_PULSES_PER_BIT (16) + +typedef struct { + uint8_t data_index; + uint8_t bit_clock_index; + bool last_bit; + bool current_polarity; + bool pulse_phase; +} ProtocolKeriEncoder; + +typedef struct { + uint8_t encoded_data[KERI_ENCODED_DATA_SIZE]; + uint8_t negative_encoded_data[KERI_ENCODED_DATA_SIZE]; + uint8_t corrupted_encoded_data[KERI_ENCODED_DATA_SIZE]; + uint8_t corrupted_negative_encoded_data[KERI_ENCODED_DATA_SIZE]; + + uint8_t data[KERI_DECODED_DATA_SIZE]; + ProtocolKeriEncoder encoder; +} ProtocolKeri; + +ProtocolKeri* protocol_keri_alloc(void) { + ProtocolKeri* protocol = malloc(sizeof(ProtocolKeri)); + return protocol; +}; + +void protocol_keri_free(ProtocolKeri* protocol) { + free(protocol); +}; + +uint8_t* protocol_keri_get_data(ProtocolKeri* protocol) { + return protocol->data; +}; + +void protocol_keri_decoder_start(ProtocolKeri* protocol) { + memset(protocol->encoded_data, 0, KERI_ENCODED_DATA_SIZE); + memset(protocol->negative_encoded_data, 0, KERI_ENCODED_DATA_SIZE); + memset(protocol->corrupted_encoded_data, 0, KERI_ENCODED_DATA_SIZE); + memset(protocol->corrupted_negative_encoded_data, 0, KERI_ENCODED_DATA_SIZE); +}; + +static bool protocol_keri_check_preamble(uint8_t* data, size_t bit_index) { + // Preamble 11100000 00000000 00000000 00000000 1 + if(*(uint32_t*)&data[bit_index / 8] != 0b00000000000000000000000011100000) return false; + if(bit_lib_get_bit(data, bit_index + 32) != 1) return false; + return true; +} + +static bool protocol_keri_can_be_decoded(uint8_t* data) { + if(!protocol_keri_check_preamble(data, 0)) return false; + if(!protocol_keri_check_preamble(data, 64)) return false; + ///if(bit_lib_get_bit(data, 61) != 0) return false; + //if(bit_lib_get_bit(data, 60) != 0) return false; + return true; +} + +static bool protocol_keri_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) { + time += (KERI_US_PER_BIT / 2); + + size_t bit_count = (time / KERI_US_PER_BIT); + bool result = false; + + if(bit_count < KERI_ENCODED_BIT_SIZE) { + for(size_t i = 0; i < bit_count; i++) { + bit_lib_push_bit(data, KERI_ENCODED_DATA_SIZE, polarity); + if(protocol_keri_can_be_decoded(data)) { + result = true; + break; + } + } + } + + return result; +} + +static void protocol_keri_descramble(uint32_t* fc, uint32_t* cn, uint32_t* internal_id) { + const uint8_t card_to_id[] = {255, 255, 255, 255, 13, 12, 20, 5, 16, 6, 21, + 17, 8, 255, 0, 7, 10, 15, 255, 11, 4, 1, + 255, 18, 255, 19, 2, 14, 3, 9, 255, 255}; + + const uint8_t card_to_fc[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 0, 255, 255, 255, 255, 2, 255, 255, 255, + 3, 255, 4, 255, 255, 255, 255, 255, 1, 255}; + + *fc = 0; + *cn = 0; + for(uint8_t card_idx = 0; card_idx < 32; card_idx++) { + bool bit = (*internal_id >> card_idx) & 1; + // Card ID + if(card_to_id[card_idx] < 32) { + *cn = *cn | (bit << card_to_id[card_idx]); + } + // Card FC + if(card_to_fc[card_idx] < 32) { + *fc = *fc | (bit << card_to_fc[card_idx]); + } + } +} + +static void protocol_keri_decoder_save(uint8_t* data_to, const uint8_t* data_from) { + uint32_t id = bit_lib_get_bits_32(data_from, 32, 32); + data_to[3] = (uint8_t)id; + data_to[2] = (uint8_t)(id >>= 8); + data_to[1] = (uint8_t)(id >>= 8); + data_to[0] = (uint8_t)(id >>= 8); +} + +bool protocol_keri_decoder_feed(ProtocolKeri* protocol, bool level, uint32_t duration) { + bool result = false; + + if(duration > (KERI_US_PER_BIT / 2)) { + if(protocol_keri_decoder_feed_internal(level, duration, protocol->encoded_data)) { + protocol_keri_decoder_save(protocol->data, protocol->encoded_data); + result = true; + return result; + } + + if(protocol_keri_decoder_feed_internal(!level, duration, protocol->negative_encoded_data)) { + protocol_keri_decoder_save(protocol->data, protocol->negative_encoded_data); + result = true; + return result; + } + } + + if(duration > (KERI_US_PER_BIT / 4)) { + // Try to decode wrong phase synced data + if(level) { + duration += 120; + } else { + if(duration > 120) { + duration -= 120; + } + } + + if(protocol_keri_decoder_feed_internal(level, duration, protocol->corrupted_encoded_data)) { + protocol_keri_decoder_save(protocol->data, protocol->corrupted_encoded_data); + + result = true; + return result; + } + + if(protocol_keri_decoder_feed_internal( + !level, duration, protocol->corrupted_negative_encoded_data)) { + protocol_keri_decoder_save(protocol->data, protocol->corrupted_negative_encoded_data); + + result = true; + return result; + } + } + + return result; +}; + +bool protocol_keri_encoder_start(ProtocolKeri* protocol) { + memset(protocol->encoded_data, 0, KERI_ENCODED_DATA_SIZE); + *(uint32_t*)&protocol->encoded_data[0] = 0b00000000000000000000000011100000; + bit_lib_copy_bits(protocol->encoded_data, 32, 32, protocol->data, 0); + + protocol->encoder.last_bit = + bit_lib_get_bit(protocol->encoded_data, KERI_ENCODED_BIT_SIZE - 1); + protocol->encoder.data_index = 0; + protocol->encoder.current_polarity = true; + protocol->encoder.pulse_phase = true; + protocol->encoder.bit_clock_index = 0; + + return true; +}; + +LevelDuration protocol_keri_encoder_yield(ProtocolKeri* protocol) { + LevelDuration level_duration; + ProtocolKeriEncoder* encoder = &protocol->encoder; + + if(encoder->pulse_phase) { + level_duration = level_duration_make(encoder->current_polarity, 1); + encoder->pulse_phase = false; + } else { + level_duration = level_duration_make(!encoder->current_polarity, 1); + encoder->pulse_phase = true; + + encoder->bit_clock_index++; + if(encoder->bit_clock_index >= KERI_ENCODER_PULSES_PER_BIT) { + encoder->bit_clock_index = 0; + + bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index); + + if(current_bit != encoder->last_bit) { + encoder->current_polarity = !encoder->current_polarity; + } + + encoder->last_bit = current_bit; + + bit_lib_increment_index(encoder->data_index, KERI_ENCODED_BIT_SIZE); + } + } + + return level_duration; +}; + +void protocol_keri_render_data(ProtocolKeri* protocol, string_t result) { + uint32_t data = bit_lib_get_bits_32(protocol->data, 0, 32); + uint32_t internal_id = data & 0x7FFFFFFF; + uint32_t fc = 0; + uint32_t cn = 0; + protocol_keri_descramble(&fc, &cn, &data); + string_printf(result, "Internal ID: %u\r\nFC: %u, Card: %u\r\n", internal_id, fc, cn); +} + +bool protocol_keri_write_data(ProtocolKeri* protocol, void* data) { + LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data; + bool result = false; + + protocol_keri_encoder_start(protocol); + + if(request->write_type == LFRFIDWriteTypeT5577) { + request->t5577.block[0] = LFRFID_T5577_TESTMODE_DISABLED | LFRFID_T5577_X_MODE | + LFRFID_T5577_MODULATION_PSK1 | LFRFID_T5577_PSKCF_RF_2 | + (2 << LFRFID_T5577_MAXBLOCK_SHIFT); + request->t5577.block[0] |= 0xF << 18; + request->t5577.block[1] = bit_lib_get_bits_32(protocol->encoded_data, 0, 32); + request->t5577.block[2] = bit_lib_get_bits_32(protocol->encoded_data, 32, 32); + request->t5577.blocks_to_write = 3; + result = true; + } + return result; +}; + +const ProtocolBase protocol_keri = { + .name = "Keri", + .manufacturer = "Keri", + .data_size = KERI_DECODED_DATA_SIZE, + .features = LFRFIDFeaturePSK, + .validate_count = 6, + .alloc = (ProtocolAlloc)protocol_keri_alloc, + .free = (ProtocolFree)protocol_keri_free, + .get_data = (ProtocolGetData)protocol_keri_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_keri_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_keri_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_keri_encoder_start, + .yield = (ProtocolEncoderYield)protocol_keri_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_keri_render_data, + .render_brief_data = (ProtocolRenderData)protocol_keri_render_data, + .write_data = (ProtocolWriteData)protocol_keri_write_data, +}; \ No newline at end of file diff --git a/lib/lfrfid/protocols/protocol_keri.h b/lib/lfrfid/protocols/protocol_keri.h new file mode 100644 index 0000000000..4cf5f370c9 --- /dev/null +++ b/lib/lfrfid/protocols/protocol_keri.h @@ -0,0 +1,4 @@ +#pragma once +#include + +extern const ProtocolBase protocol_keri; diff --git a/lib/lfs_config.h b/lib/lfs_config.h index 59b3c486e3..ff8bc4b235 100644 --- a/lib/lfs_config.h +++ b/lib/lfs_config.h @@ -5,21 +5,26 @@ #ifdef FURI_NDEBUG #define LFS_NO_ASSERT #define LFS_ASSERT(x) -#else +#else #define LFS_ASSERT furi_assert #endif #define LFS_TAG "Lfs" +#ifdef FURI_LFS_DEBUG #define LFS_TRACE(...) FURI_LOG_T(LFS_TAG, __VA_ARGS__); #define LFS_DEBUG(...) FURI_LOG_D(LFS_TAG, __VA_ARGS__); +#else +#define LFS_TRACE(...) + +#define LFS_DEBUG(...) +#endif // FURI_LFS_DEBUG #define LFS_WARN(...) FURI_LOG_W(LFS_TAG, __VA_ARGS__); #define LFS_ERROR(...) FURI_LOG_E(LFS_TAG, __VA_ARGS__); - // Because crc #undef LFS_CONFIG @@ -35,16 +40,13 @@ #ifndef LFS_NO_ASSERT #include #endif -#if !defined(LFS_NO_DEBUG) || \ - !defined(LFS_NO_WARN) || \ - !defined(LFS_NO_ERROR) || \ - defined(LFS_YES_TRACE) +#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) || \ + defined(LFS_YES_TRACE) #include #endif #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif // Builtin functions, these may be replaced by more efficient @@ -66,21 +68,29 @@ static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { } static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) { - return lfs_aligndown(a + alignment-1, alignment); + return lfs_aligndown(a + alignment - 1, alignment); } // Find the smallest power of 2 greater than or equal to a static inline uint32_t lfs_npw2(uint32_t a) { #if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM)) - return 32 - __builtin_clz(a-1); + return 32 - __builtin_clz(a - 1); #else uint32_t r = 0; uint32_t s; a -= 1; - s = (a > 0xffff) << 4; a >>= s; r |= s; - s = (a > 0xff ) << 3; a >>= s; r |= s; - s = (a > 0xf ) << 2; a >>= s; r |= s; - s = (a > 0x3 ) << 1; a >>= s; r |= s; + s = (a > 0xffff) << 4; + a >>= s; + r |= s; + s = (a > 0xff) << 3; + a >>= s; + r |= s; + s = (a > 0xf) << 2; + a >>= s; + r |= s; + s = (a > 0x3) << 1; + a >>= s; + r |= s; return (r | (a >> 1)) + 1; #endif } @@ -114,20 +124,23 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) { // Convert between 32-bit little-endian and native order static inline uint32_t lfs_fromle32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +#if !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \ + BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \ + __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) return a; -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) +#elif !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \ + __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) return __builtin_bswap32(a); #else - return (((uint8_t*)&a)[0] << 0) | - (((uint8_t*)&a)[1] << 8) | - (((uint8_t*)&a)[2] << 16) | + return (((uint8_t*)&a)[0] << 0) | (((uint8_t*)&a)[1] << 8) | (((uint8_t*)&a)[2] << 16) | (((uint8_t*)&a)[3] << 24); #endif } @@ -138,21 +151,24 @@ static inline uint32_t lfs_tole32(uint32_t a) { // Convert between 32-bit big-endian and native order static inline uint32_t lfs_frombe32(uint32_t a) { -#if !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) +#if !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \ + BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \ + __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) return __builtin_bswap32(a); -#elif !defined(LFS_NO_INTRINSICS) && ( \ - (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \ - (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) +#elif !defined(LFS_NO_INTRINSICS) && \ + ((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \ + __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \ + (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) return a; #else - return (((uint8_t*)&a)[0] << 24) | - (((uint8_t*)&a)[1] << 16) | - (((uint8_t*)&a)[2] << 8) | - (((uint8_t*)&a)[3] << 0); + return (((uint8_t*)&a)[0] << 24) | (((uint8_t*)&a)[1] << 16) | (((uint8_t*)&a)[2] << 8) | + (((uint8_t*)&a)[3] << 0); #endif } @@ -161,11 +177,11 @@ static inline uint32_t lfs_tobe32(uint32_t a) { } // Calculate CRC-32 with polynomial = 0x04c11db7 -uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); +uint32_t lfs_crc(uint32_t crc, const void* buffer, size_t size); // Allocate memory, only used if buffers are not provided to littlefs // Note, memory must be 64-bit aligned -static inline void *lfs_malloc(size_t size) { +static inline void* lfs_malloc(size_t size) { #ifndef LFS_NO_MALLOC return malloc(size); #else @@ -175,7 +191,7 @@ static inline void *lfs_malloc(size_t size) { } // Deallocate memory, only used if buffers are not provided to littlefs -static inline void lfs_free(void *p) { +static inline void lfs_free(void* p) { #ifndef LFS_NO_MALLOC free(p); #else @@ -183,7 +199,6 @@ static inline void lfs_free(void *p) { #endif } - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/lib/littlefs b/lib/littlefs index 1863dc7883..40dba4a556 160000 --- a/lib/littlefs +++ b/lib/littlefs @@ -1 +1 @@ -Subproject commit 1863dc7883d82bd6ca79faa164b65341064d1c16 +Subproject commit 40dba4a556e0d81dfbe64301a6aa4e18ceca896c diff --git a/lib/nfc/helpers/mfkey32.c b/lib/nfc/helpers/mfkey32.c new file mode 100644 index 0000000000..64b06e259c --- /dev/null +++ b/lib/nfc/helpers/mfkey32.c @@ -0,0 +1,230 @@ +#include "mfkey32.h" + +#include +#include +#include +#include +#include + +#include +#include + +#define TAG "Mfkey32" + +#define MFKEY32_LOGS_PATH EXT_PATH("nfc/.mfkey32.log") + +typedef enum { + Mfkey32StateIdle, + Mfkey32StateAuthReceived, + Mfkey32StateAuthNtSent, + Mfkey32StateAuthArNrReceived, +} Mfkey32State; + +typedef struct { + uint32_t cuid; + uint8_t sector; + MfClassicKey key; + uint32_t nt0; + uint32_t nr0; + uint32_t ar0; + uint32_t nt1; + uint32_t nr1; + uint32_t ar1; +} Mfkey32Params; + +ARRAY_DEF(Mfkey32Params, Mfkey32Params, M_POD_OPLIST); + +typedef struct { + uint8_t sector; + MfClassicKey key; + uint32_t nt; + uint32_t nr; + uint32_t ar; +} Mfkey32Nonce; + +struct Mfkey32 { + Mfkey32State state; + Stream* file_stream; + Mfkey32Params_t params_arr; + Mfkey32Nonce nonce; + uint32_t cuid; + Mfkey32ParseDataCallback callback; + void* context; +}; + +Mfkey32* mfkey32_alloc(uint32_t cuid) { + Mfkey32* instance = malloc(sizeof(Mfkey32)); + instance->cuid = cuid; + instance->state = Mfkey32StateIdle; + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->file_stream = buffered_file_stream_alloc(storage); + if(!buffered_file_stream_open( + instance->file_stream, MFKEY32_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) { + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + free(instance); + instance = NULL; + } else { + Mfkey32Params_init(instance->params_arr); + } + + furi_record_close(RECORD_STORAGE); + + return instance; +} + +void mfkey32_free(Mfkey32* instance) { + furi_assert(instance != NULL); + + Mfkey32Params_clear(instance->params_arr); + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + free(instance); +} + +void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +static bool mfkey32_write_params(Mfkey32* instance, Mfkey32Params* params) { + string_t str; + string_init_printf( + str, + "Sector %d key %c cuid %08x nt0 %08x nr0 %08x ar0 %08x nt1 %08x nr1 %08x ar1 %08x\n", + params->sector, + params->key == MfClassicKeyA ? 'A' : 'B', + params->cuid, + params->nt0, + params->nr0, + params->ar0, + params->nt1, + params->nr1, + params->ar1); + bool write_success = stream_write_string(instance->file_stream, str); + string_clear(str); + return write_success; +} + +static void mfkey32_add_params(Mfkey32* instance) { + Mfkey32Nonce* nonce = &instance->nonce; + bool nonce_added = false; + // Search if we partially collected params + if(Mfkey32Params_size(instance->params_arr)) { + Mfkey32Params_it_t it; + for(Mfkey32Params_it(it, instance->params_arr); !Mfkey32Params_end_p(it); + Mfkey32Params_next(it)) { + Mfkey32Params* params = Mfkey32Params_ref(it); + if((params->sector == nonce->sector) && (params->key == nonce->key)) { + params->nt1 = nonce->nt; + params->nr1 = nonce->nr; + params->ar1 = nonce->ar; + nonce_added = true; + FURI_LOG_I( + TAG, + "Params for sector %d key %c collected", + params->sector, + params->key == MfClassicKeyA ? 'A' : 'B'); + // Write on sd card + if(mfkey32_write_params(instance, params)) { + Mfkey32Params_remove(instance->params_arr, it); + if(instance->callback) { + instance->callback(Mfkey32EventParamCollected, instance->context); + } + } + } + } + } + if(!nonce_added) { + Mfkey32Params params = { + .sector = nonce->sector, + .key = nonce->key, + .cuid = instance->cuid, + .nt0 = nonce->nt, + .nr0 = nonce->nr, + .ar0 = nonce->ar, + }; + Mfkey32Params_push_back(instance->params_arr, params); + } +} + +void mfkey32_process_data( + Mfkey32* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped) { + furi_assert(instance); + furi_assert(data); + + Mfkey32Nonce* nonce = &instance->nonce; + uint16_t data_len = len; + if((data_len > 3) && !crc_dropped) { + data_len -= 2; + } + + bool data_processed = false; + if(instance->state == Mfkey32StateIdle) { + if(reader_to_tag) { + if((data[0] == 0x60) || (data[0] == 0x61)) { + nonce->key = data[0] == 0x60 ? MfClassicKeyA : MfClassicKeyB; + nonce->sector = mf_classic_get_sector_by_block(data[1]); + instance->state = Mfkey32StateAuthReceived; + data_processed = true; + } + } + } else if(instance->state == Mfkey32StateAuthReceived) { + if(!reader_to_tag) { + if(len == 4) { + nonce->nt = nfc_util_bytes2num(data, 4); + instance->state = Mfkey32StateAuthNtSent; + data_processed = true; + } + } + } else if(instance->state == Mfkey32StateAuthNtSent) { + if(reader_to_tag) { + if(len == 8) { + nonce->nr = nfc_util_bytes2num(data, 4); + nonce->ar = nfc_util_bytes2num(&data[4], 4); + mfkey32_add_params(instance); + instance->state = Mfkey32StateIdle; + } + } + } + if(!data_processed) { + instance->state = Mfkey32StateIdle; + } +} + +uint16_t mfkey32_get_auth_sectors(string_t data_str) { + furi_assert(data_str); + + uint16_t nonces_num = 0; + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* file_stream = buffered_file_stream_alloc(storage); + string_t temp_str; + string_init(temp_str); + + do { + if(!buffered_file_stream_open( + file_stream, MFKEY32_LOGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) + break; + while(true) { + if(!stream_read_line(file_stream, temp_str)) break; + size_t uid_pos = string_search_str(temp_str, "cuid"); + string_left(temp_str, uid_pos); + string_push_back(temp_str, '\n'); + string_cat(data_str, temp_str); + nonces_num++; + } + } while(false); + + buffered_file_stream_close(file_stream); + stream_free(file_stream); + string_clear(temp_str); + + return nonces_num; +} diff --git a/lib/nfc/helpers/mfkey32.h b/lib/nfc/helpers/mfkey32.h new file mode 100644 index 0000000000..c4f13cc2c4 --- /dev/null +++ b/lib/nfc/helpers/mfkey32.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +typedef struct Mfkey32 Mfkey32; + +typedef enum { + Mfkey32EventParamCollected, +} Mfkey32Event; + +typedef void (*Mfkey32ParseDataCallback)(Mfkey32Event event, void* context); + +Mfkey32* mfkey32_alloc(uint32_t cuid); + +void mfkey32_free(Mfkey32* instance); + +void mfkey32_process_data( + Mfkey32* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped); + +void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context); + +uint16_t mfkey32_get_auth_sectors(string_t string); diff --git a/lib/nfc/helpers/nfc_debug_log.c b/lib/nfc/helpers/nfc_debug_log.c new file mode 100644 index 0000000000..1cbc5224f6 --- /dev/null +++ b/lib/nfc/helpers/nfc_debug_log.c @@ -0,0 +1,72 @@ +#include "nfc_debug_log.h" + +#include +#include +#include + +#define TAG "NfcDebugLog" + +#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.txt") + +struct NfcDebugLog { + Stream* file_stream; + string_t data_str; +}; + +NfcDebugLog* nfc_debug_log_alloc() { + NfcDebugLog* instance = malloc(sizeof(NfcDebugLog)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->file_stream = buffered_file_stream_alloc(storage); + + if(!buffered_file_stream_open( + instance->file_stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + instance->file_stream = NULL; + } + + if(!instance->file_stream) { + free(instance); + instance = NULL; + } else { + string_init(instance->data_str); + } + furi_record_close(RECORD_STORAGE); + + return instance; +} + +void nfc_debug_log_free(NfcDebugLog* instance) { + furi_assert(instance); + furi_assert(instance->file_stream); + furi_assert(instance->data_str); + + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + string_clear(instance->data_str); + + free(instance); +} + +void nfc_debug_log_process_data( + NfcDebugLog* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped) { + furi_assert(instance); + furi_assert(instance->file_stream); + furi_assert(instance->data_str); + furi_assert(data); + UNUSED(crc_dropped); + + string_printf(instance->data_str, "%lu %c:", furi_get_tick(), reader_to_tag ? 'R' : 'T'); + uint16_t data_len = len; + for(size_t i = 0; i < data_len; i++) { + string_cat_printf(instance->data_str, " %02x", data[i]); + } + string_push_back(instance->data_str, '\n'); + + stream_write_string(instance->file_stream, instance->data_str); +} diff --git a/lib/nfc/helpers/nfc_debug_log.h b/lib/nfc/helpers/nfc_debug_log.h new file mode 100644 index 0000000000..261b8008b8 --- /dev/null +++ b/lib/nfc/helpers/nfc_debug_log.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +typedef struct NfcDebugLog NfcDebugLog; + +NfcDebugLog* nfc_debug_log_alloc(); + +void nfc_debug_log_free(NfcDebugLog* instance); + +void nfc_debug_log_process_data( + NfcDebugLog* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped); diff --git a/lib/nfc/helpers/nfc_debug_pcap.c b/lib/nfc/helpers/nfc_debug_pcap.c index 48d72bfbfa..6c7654ad57 100644 --- a/lib/nfc/helpers/nfc_debug_pcap.c +++ b/lib/nfc/helpers/nfc_debug_pcap.c @@ -1,7 +1,9 @@ #include "nfc_debug_pcap.h" +#include +#include +#include #include -#include #define TAG "NfcDebugPcap" @@ -16,48 +18,94 @@ #define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA #define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap") -#define NFC_DEBUG_PCAP_BUFFER_SIZE 64 - -struct NfcDebugPcapWorker { - bool alive; - Storage* storage; - File* file; - StreamBufferHandle_t stream; - FuriThread* thread; + +struct NfcDebugPcap { + Stream* file_stream; }; -static File* nfc_debug_pcap_open(Storage* storage) { - File* file = storage_file_alloc(storage); - if(!storage_file_open(file, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { - storage_file_free(file); - return NULL; - } - if(!storage_file_tell(file)) { - struct { - uint32_t magic; - uint16_t major, minor; - uint32_t reserved[2]; - uint32_t snaplen; - uint32_t link_type; - } __attribute__((__packed__)) pcap_hdr = { - .magic = PCAP_MAGIC, - .major = PCAP_MAJOR, - .minor = PCAP_MINOR, - .snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE, - .link_type = DLT_ISO_14443, - }; - if(storage_file_write(file, &pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) { - FURI_LOG_E(TAG, "Failed to write pcap header"); +static Stream* nfc_debug_pcap_open(Storage* storage) { + Stream* stream = NULL; + stream = buffered_file_stream_alloc(storage); + if(!buffered_file_stream_open(stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) { + buffered_file_stream_close(stream); + stream_free(stream); + stream = NULL; + } else { + if(!stream_tell(stream)) { + struct { + uint32_t magic; + uint16_t major, minor; + uint32_t reserved[2]; + uint32_t snaplen; + uint32_t link_type; + } __attribute__((__packed__)) pcap_hdr = { + .magic = PCAP_MAGIC, + .major = PCAP_MAJOR, + .minor = PCAP_MINOR, + .snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE, + .link_type = DLT_ISO_14443, + }; + if(stream_write(stream, (uint8_t*)&pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) { + FURI_LOG_E(TAG, "Failed to write pcap header"); + buffered_file_stream_close(stream); + stream_free(stream); + stream = NULL; + } } } - return file; + return stream; +} + +NfcDebugPcap* nfc_debug_pcap_alloc() { + NfcDebugPcap* instance = malloc(sizeof(NfcDebugPcap)); + + Storage* storage = furi_record_open(RECORD_STORAGE); + instance->file_stream = nfc_debug_pcap_open(storage); + if(!instance->file_stream) { + free(instance); + instance = NULL; + } + furi_record_close(RECORD_STORAGE); + + return instance; +} + +void nfc_debug_pcap_free(NfcDebugPcap* instance) { + furi_assert(instance); + furi_assert(instance->file_stream); + + buffered_file_stream_close(instance->file_stream); + stream_free(instance->file_stream); + + free(instance); } -static void - nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) { +void nfc_debug_pcap_process_data( + NfcDebugPcap* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped) { + furi_assert(instance); + furi_assert(data); FuriHalRtcDateTime datetime; furi_hal_rtc_get_datetime(&datetime); + uint8_t event = 0; + if(reader_to_tag) { + if(crc_dropped) { + event = DATA_PCD_TO_PICC_CRC_DROPPED; + } else { + event = DATA_PCD_TO_PICC; + } + } else { + if(crc_dropped) { + event = DATA_PICC_TO_PCD_CRC_DROPPED; + } else { + event = DATA_PICC_TO_PCD; + } + } + struct { // https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header uint32_t ts_sec; @@ -77,90 +125,6 @@ static void .event = event, .len = len << 8 | len >> 8, }; - xStreamBufferSend(instance->stream, &pkt_hdr, sizeof(pkt_hdr), FuriWaitForever); - xStreamBufferSend(instance->stream, data, len, FuriWaitForever); -} - -static void - nfc_debug_pcap_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { - NfcDebugPcapWorker* instance = context; - uint8_t event = crc_dropped ? DATA_PCD_TO_PICC_CRC_DROPPED : DATA_PCD_TO_PICC; - nfc_debug_pcap_write(instance, event, data, bits / 8); -} - -static void - nfc_debug_pcap_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { - NfcDebugPcapWorker* instance = context; - uint8_t event = crc_dropped ? DATA_PICC_TO_PCD_CRC_DROPPED : DATA_PICC_TO_PCD; - nfc_debug_pcap_write(instance, event, data, bits / 8); -} - -int32_t nfc_debug_pcap_thread(void* context) { - NfcDebugPcapWorker* instance = context; - uint8_t buffer[NFC_DEBUG_PCAP_BUFFER_SIZE]; - - while(instance->alive) { - size_t ret = - xStreamBufferReceive(instance->stream, buffer, NFC_DEBUG_PCAP_BUFFER_SIZE, 50); - if(storage_file_write(instance->file, buffer, ret) != ret) { - FURI_LOG_E(TAG, "Failed to write pcap data"); - } - } - - return 0; -} - -NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage) { - NfcDebugPcapWorker* instance = malloc(sizeof(NfcDebugPcapWorker)); - - instance->alive = true; - - instance->storage = storage; - - instance->file = nfc_debug_pcap_open(storage); - - instance->stream = xStreamBufferCreate(4096, 1); - - instance->thread = furi_thread_alloc(); - furi_thread_set_name(instance->thread, "PcapWorker"); - furi_thread_set_stack_size(instance->thread, 1024); - furi_thread_set_callback(instance->thread, nfc_debug_pcap_thread); - furi_thread_set_context(instance->thread, instance); - furi_thread_start(instance->thread); - - return instance; -} - -void nfc_debug_pcap_free(NfcDebugPcapWorker* instance) { - furi_assert(instance); - - instance->alive = false; - - furi_thread_join(instance->thread); - furi_thread_free(instance->thread); - - vStreamBufferDelete(instance->stream); - - if(instance->file) storage_file_free(instance->file); - - instance->storage = NULL; - - free(instance); -} - -void nfc_debug_pcap_prepare_tx_rx( - NfcDebugPcapWorker* instance, - FuriHalNfcTxRxContext* tx_rx, - bool is_picc) { - if(!instance || !instance->file) return; - - if(is_picc) { - tx_rx->sniff_tx = nfc_debug_pcap_write_rx; - tx_rx->sniff_rx = nfc_debug_pcap_write_tx; - } else { - tx_rx->sniff_tx = nfc_debug_pcap_write_tx; - tx_rx->sniff_rx = nfc_debug_pcap_write_rx; - } - - tx_rx->sniff_context = instance; + stream_write(instance->file_stream, (uint8_t*)&pkt_hdr, sizeof(pkt_hdr)); + stream_write(instance->file_stream, data, len); } diff --git a/lib/nfc/helpers/nfc_debug_pcap.h b/lib/nfc/helpers/nfc_debug_pcap.h index 6d2a449ae2..eeddc56112 100644 --- a/lib/nfc/helpers/nfc_debug_pcap.h +++ b/lib/nfc/helpers/nfc_debug_pcap.h @@ -1,21 +1,17 @@ #pragma once -#include -#include +#include +#include -typedef struct NfcDebugPcapWorker NfcDebugPcapWorker; +typedef struct NfcDebugPcap NfcDebugPcap; -NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage); +NfcDebugPcap* nfc_debug_pcap_alloc(); -void nfc_debug_pcap_free(NfcDebugPcapWorker* instance); +void nfc_debug_pcap_free(NfcDebugPcap* instance); -/** Prepare tx/rx context for debug pcap logging, if enabled. - * - * @param instance NfcDebugPcapWorker* instance, can be NULL - * @param tx_rx TX/RX context to log - * @param is_picc if true, record Flipper as PICC, else PCD. - */ -void nfc_debug_pcap_prepare_tx_rx( - NfcDebugPcapWorker* instance, - FuriHalNfcTxRxContext* tx_rx, - bool is_picc); +void nfc_debug_pcap_process_data( + NfcDebugPcap* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped); diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c new file mode 100644 index 0000000000..680b8cef9c --- /dev/null +++ b/lib/nfc/helpers/reader_analyzer.c @@ -0,0 +1,261 @@ +#include "reader_analyzer.h" +#include +#include +#include +#include + +#include "mfkey32.h" +#include "nfc_debug_pcap.h" +#include "nfc_debug_log.h" + +#define TAG "ReaderAnalyzer" + +#define READER_ANALYZER_MAX_BUFF_SIZE (1024) + +typedef struct { + bool reader_to_tag; + bool crc_dropped; + uint16_t len; +} ReaderAnalyzerHeader; + +typedef enum { + ReaderAnalyzerNfcDataMfClassic, +} ReaderAnalyzerNfcData; + +struct ReaderAnalyzer { + FuriHalNfcDevData nfc_data; + + bool alive; + StreamBufferHandle_t stream; + FuriThread* thread; + + ReaderAnalyzerParseDataCallback callback; + void* context; + + ReaderAnalyzerMode mode; + Mfkey32* mfkey32; + NfcDebugLog* debug_log; + NfcDebugPcap* pcap; +}; + +const FuriHalNfcDevData reader_analyzer_nfc_data[] = { + [ReaderAnalyzerNfcDataMfClassic] = + {.sak = 0x08, + .atqa = {0x44, 0x00}, + .interface = FuriHalNfcInterfaceRf, + .type = FuriHalNfcTypeA, + .uid_len = 7, + .uid = {0x04, 0x77, 0x70, 0x2A, 0x23, 0x4F, 0x80}, + .cuid = 0x2A234F80}, +}; + +void reader_analyzer_parse(ReaderAnalyzer* instance, uint8_t* buffer, size_t size) { + if(size < sizeof(ReaderAnalyzerHeader)) return; + + size_t bytes_i = 0; + while(bytes_i < size) { + ReaderAnalyzerHeader* header = (ReaderAnalyzerHeader*)&buffer[bytes_i]; + uint16_t len = header->len; + if(bytes_i + len > size) break; + bytes_i += sizeof(ReaderAnalyzerHeader); + if(instance->mfkey32) { + mfkey32_process_data( + instance->mfkey32, + &buffer[bytes_i], + len, + header->reader_to_tag, + header->crc_dropped); + } + if(instance->pcap) { + nfc_debug_pcap_process_data( + instance->pcap, &buffer[bytes_i], len, header->reader_to_tag, header->crc_dropped); + } + if(instance->debug_log) { + nfc_debug_log_process_data( + instance->debug_log, + &buffer[bytes_i], + len, + header->reader_to_tag, + header->crc_dropped); + } + bytes_i += len; + } +} + +int32_t reader_analyzer_thread(void* context) { + ReaderAnalyzer* reader_analyzer = context; + uint8_t buffer[READER_ANALYZER_MAX_BUFF_SIZE] = {}; + + while(reader_analyzer->alive || !xStreamBufferIsEmpty(reader_analyzer->stream)) { + size_t ret = xStreamBufferReceive( + reader_analyzer->stream, buffer, READER_ANALYZER_MAX_BUFF_SIZE, 50); + if(ret) { + reader_analyzer_parse(reader_analyzer, buffer, ret); + } + } + + return 0; +} + +ReaderAnalyzer* reader_analyzer_alloc() { + ReaderAnalyzer* instance = malloc(sizeof(ReaderAnalyzer)); + + instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic]; + instance->alive = false; + instance->stream = + xStreamBufferCreate(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader)); + + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "ReaderAnalyzerWorker"); + furi_thread_set_stack_size(instance->thread, 2048); + furi_thread_set_callback(instance->thread, reader_analyzer_thread); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_priority(instance->thread, FuriThreadPriorityLow); + + return instance; +} + +static void reader_analyzer_mfkey_callback(Mfkey32Event event, void* context) { + furi_assert(context); + ReaderAnalyzer* instance = context; + + if(event == Mfkey32EventParamCollected) { + if(instance->callback) { + instance->callback(ReaderAnalyzerEventMfkeyCollected, instance->context); + } + } +} + +void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode) { + furi_assert(instance); + + xStreamBufferReset(instance->stream); + if(mode & ReaderAnalyzerModeDebugLog) { + instance->debug_log = nfc_debug_log_alloc(); + } + if(mode & ReaderAnalyzerModeMfkey) { + instance->mfkey32 = mfkey32_alloc(instance->nfc_data.cuid); + if(instance->mfkey32) { + mfkey32_set_callback(instance->mfkey32, reader_analyzer_mfkey_callback, instance); + } + } + if(mode & ReaderAnalyzerModeDebugPcap) { + instance->pcap = nfc_debug_pcap_alloc(); + } + + instance->alive = true; + furi_thread_start(instance->thread); +} + +void reader_analyzer_stop(ReaderAnalyzer* instance) { + furi_assert(instance); + + instance->alive = false; + furi_thread_join(instance->thread); + + if(instance->debug_log) { + nfc_debug_log_free(instance->debug_log); + instance->debug_log = NULL; + } + if(instance->mfkey32) { + mfkey32_free(instance->mfkey32); + instance->mfkey32 = NULL; + } + if(instance->pcap) { + nfc_debug_pcap_free(instance->pcap); + } +} + +void reader_analyzer_free(ReaderAnalyzer* instance) { + furi_assert(instance); + + reader_analyzer_stop(instance); + furi_thread_free(instance->thread); + vStreamBufferDelete(instance->stream); + free(instance); +} + +void reader_analyzer_set_callback( + ReaderAnalyzer* instance, + ReaderAnalyzerParseDataCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + + instance->callback = callback; + instance->context = context; +} + +NfcProtocol + reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len) { + furi_assert(instance); + furi_assert(buff_rx); + UNUSED(len); + NfcProtocol protocol = NfcDeviceProtocolUnknown; + + if((buff_rx[0] == 0x60) || (buff_rx[0] == 0x61)) { + protocol = NfcDeviceProtocolMifareClassic; + } + + return protocol; +} + +FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) { + furi_assert(instance); + + return &instance->nfc_data; +} + +static void reader_analyzer_write( + ReaderAnalyzer* instance, + uint8_t* data, + uint16_t len, + bool reader_to_tag, + bool crc_dropped) { + ReaderAnalyzerHeader header = { + .reader_to_tag = reader_to_tag, .crc_dropped = crc_dropped, .len = len}; + size_t data_sent = 0; + data_sent = xStreamBufferSend( + instance->stream, &header, sizeof(ReaderAnalyzerHeader), FuriWaitForever); + if(data_sent != sizeof(ReaderAnalyzerHeader)) { + FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, sizeof(ReaderAnalyzerHeader)); + } + data_sent = xStreamBufferSend(instance->stream, data, len, FuriWaitForever); + if(data_sent != len) { + FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, len); + } +} + +static void + reader_analyzer_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { + UNUSED(crc_dropped); + ReaderAnalyzer* reader_analyzer = context; + uint16_t bytes = bits < 8 ? 1 : bits / 8; + reader_analyzer_write(reader_analyzer, data, bytes, false, crc_dropped); +} + +static void + reader_analyzer_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) { + UNUSED(crc_dropped); + ReaderAnalyzer* reader_analyzer = context; + uint16_t bytes = bits < 8 ? 1 : bits / 8; + reader_analyzer_write(reader_analyzer, data, bytes, true, crc_dropped); +} + +void reader_analyzer_prepare_tx_rx( + ReaderAnalyzer* instance, + FuriHalNfcTxRxContext* tx_rx, + bool is_picc) { + furi_assert(instance); + furi_assert(tx_rx); + + if(is_picc) { + tx_rx->sniff_tx = reader_analyzer_write_rx; + tx_rx->sniff_rx = reader_analyzer_write_tx; + } else { + tx_rx->sniff_rx = reader_analyzer_write_rx; + tx_rx->sniff_tx = reader_analyzer_write_tx; + } + + tx_rx->sniff_context = instance; +} diff --git a/lib/nfc/helpers/reader_analyzer.h b/lib/nfc/helpers/reader_analyzer.h new file mode 100644 index 0000000000..cc501f5a63 --- /dev/null +++ b/lib/nfc/helpers/reader_analyzer.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +typedef enum { + ReaderAnalyzerModeDebugLog = 0x01, + ReaderAnalyzerModeMfkey = 0x02, + ReaderAnalyzerModeDebugPcap = 0x04, +} ReaderAnalyzerMode; + +typedef enum { + ReaderAnalyzerEventMfkeyCollected, +} ReaderAnalyzerEvent; + +typedef struct ReaderAnalyzer ReaderAnalyzer; + +typedef void (*ReaderAnalyzerParseDataCallback)(ReaderAnalyzerEvent event, void* context); + +ReaderAnalyzer* reader_analyzer_alloc(); + +void reader_analyzer_free(ReaderAnalyzer* instance); + +void reader_analyzer_set_callback( + ReaderAnalyzer* instance, + ReaderAnalyzerParseDataCallback callback, + void* context); + +void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode); + +void reader_analyzer_stop(ReaderAnalyzer* instance); + +NfcProtocol + reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len); + +FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance); + +void reader_analyzer_prepare_tx_rx( + ReaderAnalyzer* instance, + FuriHalNfcTxRxContext* tx_rx, + bool is_picc); diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index c46919219a..ea06ae70f8 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -195,6 +195,7 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) { } data->data_size = pages_total * 4; data->data_read = pages_read * 4; + if(data->data_size > MF_UL_MAX_DUMP_SIZE || data->data_read > MF_UL_MAX_DUMP_SIZE) break; bool pages_parsed = true; for(uint16_t i = 0; i < pages_total; i++) { string_printf(temp_str, "Page %d", i); diff --git a/lib/nfc/nfc_worker.c b/lib/nfc/nfc_worker.c index f07ea616ce..6355f8d1ea 100644 --- a/lib/nfc/nfc_worker.c +++ b/lib/nfc/nfc_worker.c @@ -28,9 +28,7 @@ NfcWorker* nfc_worker_alloc() { } nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage); - } + nfc_worker->reader_analyzer = reader_analyzer_alloc(nfc_worker->storage); return nfc_worker; } @@ -42,7 +40,7 @@ void nfc_worker_free(NfcWorker* nfc_worker) { furi_record_close(RECORD_STORAGE); - if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker); + reader_analyzer_free(nfc_worker->reader_analyzer); free(nfc_worker); } @@ -105,6 +103,8 @@ int32_t nfc_worker_task(void* context) { nfc_worker_mf_ultralight_read_auth(nfc_worker); } else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) { nfc_worker_mf_classic_dict_attack(nfc_worker); + } else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) { + nfc_worker_analyze_reader(nfc_worker); } furi_hal_nfc_sleep(); nfc_worker_change_state(nfc_worker, NfcWorkerStateReady); @@ -117,7 +117,11 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC MfUltralightReader reader = {}; MfUltralightData data = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + do { // Read card if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; @@ -127,6 +131,10 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC read_success = true; } while(false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + return read_success; } @@ -134,7 +142,11 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont furi_assert(nfc_worker->callback); bool read_success = false; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + do { // Try to read supported card FURI_LOG_I(TAG, "Try read supported card ..."); @@ -162,6 +174,9 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont } } while(false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } return read_success; } @@ -169,13 +184,21 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont bool read_success = false; MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + do { if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; if(!mf_df_read_card(tx_rx, data)) break; read_success = true; } while(false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + return read_success; } @@ -184,7 +207,11 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte EmvApplication emv_app = {}; EmvData* result = &nfc_worker->dev_data->emv_data; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + do { // Read card if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break; @@ -211,6 +238,10 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte read_success = true; } while(false); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } + return read_success; } @@ -320,18 +351,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) { void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data; NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data; // TODO add support for RATS - // Now remove bit 6 in SAK to support ISO-14443A-3 emulation // Need to save ATS to support ISO-14443A-4 emulation - uint8_t sak = data->sak; - FURI_BIT_CLEAR(sak, 5); while(nfc_worker->state == NfcWorkerStateUidEmulate) { - if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, sak, true, 100)) { + if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) { if(furi_hal_nfc_tx_rx(&tx_rx, 100)) { reader_data->size = tx_rx.rx_bits / 8; if(reader_data->size > 0) { @@ -349,7 +376,6 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) { void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); FuriHalNfcDevData params = { .uid = {0xCF, 0x72, 0xd4, 0x40}, .uid_len = 4, @@ -358,6 +384,11 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { .type = FuriHalNfcTypeA, }; + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + while(nfc_worker->state == NfcWorkerStateEmulateApdu) { if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) { FURI_LOG_D(TAG, "POS terminal detected"); @@ -370,6 +401,10 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) { furi_hal_nfc_sleep(); furi_delay_ms(20); } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } } void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) { @@ -484,7 +519,6 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) { void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) { FuriHalNfcTxRxContext tx_rx = {}; - nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true); FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; MfClassicEmulator emulator = { .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), @@ -525,6 +559,11 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { MfUltralightReader reader = {}; mf_ul_reset(data); + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog); + } + uint32_t key = 0; uint16_t pack = 0; while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) { @@ -577,4 +616,61 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) { furi_delay_ms(10); } } + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + reader_analyzer_stop(nfc_worker->reader_analyzer); + } +} + +static void nfc_worker_reader_analyzer_callback(ReaderAnalyzerEvent event, void* context) { + furi_assert(context); + NfcWorker* nfc_worker = context; + + if(event == ReaderAnalyzerEventMfkeyCollected) { + if(nfc_worker->callback) { + nfc_worker->callback(NfcWorkerEventDetectReaderMfkeyCollected, nfc_worker->context); + } + } +} + +void nfc_worker_analyze_reader(NfcWorker* nfc_worker) { + FuriHalNfcTxRxContext tx_rx = {}; + + ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer; + FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer); + MfClassicEmulator emulator = { + .cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4), + .data = nfc_worker->dev_data->mf_classic_data, + .data_changed = false, + }; + NfcaSignal* nfca_signal = nfca_signal_alloc(); + tx_rx.nfca_signal = nfca_signal; + reader_analyzer_prepare_tx_rx(reader_analyzer, &tx_rx, true); + reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeMfkey); + reader_analyzer_set_callback(reader_analyzer, nfc_worker_reader_analyzer_callback, nfc_worker); + + rfal_platform_spi_acquire(); + + FURI_LOG_D(TAG, "Start reader analyzer"); + while(nfc_worker->state == NfcWorkerStateAnalyzeReader) { + furi_hal_nfc_stop_cmd(); + furi_delay_ms(5); + furi_hal_nfc_listen_start(nfc_data); + if(furi_hal_nfc_listen_rx(&tx_rx, 300)) { + NfcProtocol protocol = + reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8); + if(protocol == NfcDeviceProtocolMifareClassic) { + mf_classic_emulator(&emulator, &tx_rx); + } + } else { + FURI_LOG_D(TAG, "No data from reader"); + continue; + } + } + + rfal_platform_spi_release(); + + reader_analyzer_stop(nfc_worker->reader_analyzer); + + nfca_signal_free(nfca_signal); } diff --git a/lib/nfc/nfc_worker.h b/lib/nfc/nfc_worker.h index 22cbc3dcc9..b7bf4da9ad 100644 --- a/lib/nfc/nfc_worker.h +++ b/lib/nfc/nfc_worker.h @@ -16,6 +16,7 @@ typedef enum { NfcWorkerStateMfClassicEmulate, NfcWorkerStateReadMfUltralightReadAuth, NfcWorkerStateMfClassicDictAttack, + NfcWorkerStateAnalyzeReader, // Debug NfcWorkerStateEmulateApdu, NfcWorkerStateField, @@ -54,8 +55,12 @@ typedef enum { NfcWorkerEventFoundKeyA, NfcWorkerEventFoundKeyB, + // Detect Reader events + NfcWorkerEventDetectReaderMfkeyCollected, + // Mifare Ultralight events NfcWorkerEventMfUltralightPassKey, + } NfcWorkerEvent; typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context); diff --git a/lib/nfc/nfc_worker_i.h b/lib/nfc/nfc_worker_i.h old mode 100755 new mode 100644 index 1e98879a7d..526182f9a9 --- a/lib/nfc/nfc_worker_i.h +++ b/lib/nfc/nfc_worker_i.h @@ -12,8 +12,7 @@ #include #include #include - -#include "helpers/nfc_debug_pcap.h" +#include struct NfcWorker { FuriThread* thread; @@ -27,7 +26,7 @@ struct NfcWorker { NfcWorkerState state; - NfcDebugPcapWorker* debug_pcap_worker; + ReaderAnalyzer* reader_analyzer; }; void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state); @@ -49,3 +48,5 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker); void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker); void nfc_worker_emulate_apdu(NfcWorker* nfc_worker); + +void nfc_worker_analyze_reader(NfcWorker* nfc_worker); diff --git a/lib/subghz/protocols/bett.c b/lib/subghz/protocols/bett.c index aca8b8c4f7..08080dc6c9 100644 --- a/lib/subghz/protocols/bett.c +++ b/lib/subghz/protocols/bett.c @@ -92,7 +92,7 @@ void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) { instance->generic.protocol_name = instance->base.protocol->name; instance->encoder.repeat = 10; - instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop) + instance->encoder.size_upload = 52; instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; @@ -233,7 +233,8 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat case BETTDecoderStepReset: if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < (subghz_protocol_bett_const.te_delta * 15))) { - //Found Preambula + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; instance->decoder.parser_step = BETTDecoderStepCheckDuration; } break; @@ -288,20 +289,6 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat } } -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_bett_check_remote_controller(SubGhzBlockGeneric* instance) { - uint32_t code_found_reverse = - subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); - - instance->serial = (code_found_reverse & 0xFF) << 12 | - ((code_found_reverse >> 8) & 0xFF) << 4 | - ((code_found_reverse >> 20) & 0x0F); - instance->btn = ((code_found_reverse >> 16) & 0x0F); -} - uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { furi_assert(context); SubGhzProtocolDecoderBETT* instance = context; @@ -339,8 +326,7 @@ bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flip void subghz_protocol_decoder_bett_get_string(void* context, string_t output) { furi_assert(context); SubGhzProtocolDecoderBETT* instance = context; - subghz_protocol_bett_check_remote_controller(&instance->generic); - uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF); + uint32_t data = (uint32_t)(instance->generic.data & 0x3FFFF); string_cat_printf( output, "%s %dbit\r\n" @@ -350,7 +336,7 @@ void subghz_protocol_decoder_bett_get_string(void* context, string_t output) { " -: " DIP_PATTERN "\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, - (uint32_t)(instance->generic.data & 0xFFFFFF), + data, SHOW_DIP_P(data, DIP_P), SHOW_DIP_P(data, DIP_O), SHOW_DIP_P(data, DIP_N)); diff --git a/lib/subghz/protocols/clemsa.c b/lib/subghz/protocols/clemsa.c new file mode 100644 index 0000000000..357a0b06de --- /dev/null +++ b/lib/subghz/protocols/clemsa.c @@ -0,0 +1,365 @@ +#include "clemsa.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +// protocol BERNER / ELKA / TEDSEN / TELETASTER +#define TAG "SubGhzProtocolClemsa" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \ + ((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_') + +static const SubGhzBlockConst subghz_protocol_clemsa_const = { + .te_short = 385, + .te_long = 2695, + .te_delta = 150, + .min_count_bit_for_found = 18, +}; + +struct SubGhzProtocolDecoderClemsa { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderClemsa { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + ClemsaDecoderStepReset = 0, + ClemsaDecoderStepSaveDuration, + ClemsaDecoderStepCheckDuration, +} ClemsaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder = { + .alloc = subghz_protocol_decoder_clemsa_alloc, + .free = subghz_protocol_decoder_clemsa_free, + + .feed = subghz_protocol_decoder_clemsa_feed, + .reset = subghz_protocol_decoder_clemsa_reset, + + .get_hash_data = subghz_protocol_decoder_clemsa_get_hash_data, + .serialize = subghz_protocol_decoder_clemsa_serialize, + .deserialize = subghz_protocol_decoder_clemsa_deserialize, + .get_string = subghz_protocol_decoder_clemsa_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder = { + .alloc = subghz_protocol_encoder_clemsa_alloc, + .free = subghz_protocol_encoder_clemsa_free, + + .deserialize = subghz_protocol_encoder_clemsa_deserialize, + .stop = subghz_protocol_encoder_clemsa_stop, + .yield = subghz_protocol_encoder_clemsa_yield, +}; + +const SubGhzProtocol subghz_protocol_clemsa = { + .name = SUBGHZ_PROTOCOL_CLEMSA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_clemsa_decoder, + .encoder = &subghz_protocol_clemsa_encoder, +}; + +void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderClemsa* instance = malloc(sizeof(SubGhzProtocolEncoderClemsa)); + + instance->base.protocol = &subghz_protocol_clemsa; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 52; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_clemsa_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderClemsa* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderClemsa instance + * @return true On success + */ +static bool subghz_protocol_encoder_clemsa_get_upload(SubGhzProtocolEncoderClemsa* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_long); + } + } + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_clemsa_const.te_short + + subghz_protocol_clemsa_const.te_long * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_clemsa_const.te_long + + subghz_protocol_clemsa_const.te_long * 7); + } + return true; +} + +bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderClemsa* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_clemsa_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_clemsa_get_upload(instance); + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_clemsa_stop(void* context) { + SubGhzProtocolEncoderClemsa* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_clemsa_yield(void* context) { + SubGhzProtocolEncoderClemsa* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderClemsa* instance = malloc(sizeof(SubGhzProtocolDecoderClemsa)); + instance->base.protocol = &subghz_protocol_clemsa; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_clemsa_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + free(instance); +} + +void subghz_protocol_decoder_clemsa_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + instance->decoder.parser_step = ClemsaDecoderStepReset; +} + +void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + + switch(instance->decoder.parser_step) { + case ClemsaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) < + subghz_protocol_clemsa_const.te_delta * 25)) { + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + break; + + case ClemsaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = ClemsaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + break; + + case ClemsaDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) < + subghz_protocol_clemsa_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_long) < + subghz_protocol_clemsa_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) < + subghz_protocol_clemsa_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short) < + subghz_protocol_clemsa_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) < + subghz_protocol_clemsa_const.te_delta * 25) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) < + subghz_protocol_clemsa_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) < + subghz_protocol_clemsa_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + + if(instance->decoder.decode_count_bit == + subghz_protocol_clemsa_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.parser_step = ClemsaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + } else { + instance->decoder.parser_step = ClemsaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_clemsa_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->serial = (instance->data >> 2) & 0xFFFF; + instance->btn = (instance->data & 0x03); +} + +uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_clemsa_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_clemsa_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_clemsa_get_string(void* context, string_t output) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + subghz_protocol_clemsa_check_remote_controller(&instance->generic); + //uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF); + string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%05lX Btn %X\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN "\r\n" + " -: " DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0x3FFFF), + instance->generic.btn, + SHOW_DIP_P(instance->generic.serial, DIP_P), + SHOW_DIP_P(instance->generic.serial, DIP_O), + SHOW_DIP_P(instance->generic.serial, DIP_N)); +} diff --git a/lib/subghz/protocols/clemsa.h b/lib/subghz/protocols/clemsa.h new file mode 100644 index 0000000000..a83983122c --- /dev/null +++ b/lib/subghz/protocols/clemsa.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_CLEMSA_NAME "Clemsa" + +typedef struct SubGhzProtocolDecoderClemsa SubGhzProtocolDecoderClemsa; +typedef struct SubGhzProtocolEncoderClemsa SubGhzProtocolEncoderClemsa; + +extern const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder; +extern const SubGhzProtocol subghz_protocol_clemsa; + +/** + * Allocate SubGhzProtocolEncoderClemsa. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderClemsa* pointer to a SubGhzProtocolEncoderClemsa instance + */ +void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderClemsa. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + */ +void subghz_protocol_encoder_clemsa_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + */ +void subghz_protocol_encoder_clemsa_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderClemsa instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_clemsa_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderClemsa. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderClemsa* pointer to a SubGhzProtocolDecoderClemsa instance + */ +void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + */ +void subghz_protocol_decoder_clemsa_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + */ +void subghz_protocol_decoder_clemsa_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzPresetDefinition + * @return true On success + */ +bool subghz_protocol_decoder_clemsa_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzPresetDefinition* preset); + +/** + * Deserialize data SubGhzProtocolDecoderClemsa. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderClemsa instance + * @param output Resulting text + */ +void subghz_protocol_decoder_clemsa_get_string(void* context, string_t output); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 12c5dfa8f6..a0953b1df4 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -485,7 +485,7 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_short) < subghz_protocol_keeloq_const.te_delta) && (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_long) < - subghz_protocol_keeloq_const.te_delta)) { + subghz_protocol_keeloq_const.te_delta * 2)) { if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 1); @@ -493,7 +493,7 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else if( (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_long) < - subghz_protocol_keeloq_const.te_delta) && + subghz_protocol_keeloq_const.te_delta * 2) && (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < subghz_protocol_keeloq_const.te_delta)) { if(instance->decoder.decode_count_bit < diff --git a/lib/subghz/protocols/registry.c b/lib/subghz/protocols/registry.c index 6c113cbf86..8bb45e9ade 100644 --- a/lib/subghz/protocols/registry.c +++ b/lib/subghz/protocols/registry.c @@ -12,6 +12,7 @@ const SubGhzProtocol* subghz_protocol_registry[] = { &subghz_protocol_chamb_code, &subghz_protocol_power_smart, &subghz_protocol_marantec, &subghz_protocol_bett, &subghz_protocol_doitrand, &subghz_protocol_phoenix_v2, &subghz_protocol_honeywell_wdb, &subghz_protocol_magellen, &subghz_protocol_intertechno_v3, + &subghz_protocol_clemsa }; diff --git a/lib/subghz/protocols/registry.h b/lib/subghz/protocols/registry.h index 342bf6d2bb..ad1151024b 100644 --- a/lib/subghz/protocols/registry.h +++ b/lib/subghz/protocols/registry.h @@ -35,6 +35,7 @@ #include "honeywell_wdb.h" #include "magellen.h" #include "intertechno_v3.h" +#include "clemsa.h" /** * Registration by name SubGhzProtocol. diff --git a/lib/subghz/subghz_keystore.c b/lib/subghz/subghz_keystore.c index 0abd2d5e66..4d2eb0e50c 100644 --- a/lib/subghz/subghz_keystore.c +++ b/lib/subghz/subghz_keystore.c @@ -114,62 +114,69 @@ static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); size_t encrypted_line_cursor = 0; - if(iv) furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv); - - size_t ret = 0; do { - ret = stream_read(stream, buffer, FILE_BUFFER_SIZE); - for(uint16_t i = 0; i < ret; i++) { - if(buffer[i] == '\n' && encrypted_line_cursor > 0) { - // Process line - if(iv) { - // Data alignment check, 32 instead of 16 because of hex encoding - size_t len = strlen(encrypted_line); - if(len % 32 == 0) { - // Inplace hex to bin conversion - for(size_t i = 0; i < len; i += 2) { - uint8_t hi_nibble = 0; - uint8_t lo_nibble = 0; - hex_char_to_hex_nibble(encrypted_line[i], &hi_nibble); - hex_char_to_hex_nibble(encrypted_line[i + 1], &lo_nibble); - encrypted_line[i / 2] = (hi_nibble << 4) | lo_nibble; - } - len /= 2; + if(iv) { + if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + FURI_LOG_E(TAG, "Unable to load decryption key"); + break; + } + } - if(furi_hal_crypto_decrypt( - (uint8_t*)encrypted_line, (uint8_t*)decrypted_line, len)) { - subghz_keystore_process_line(instance, decrypted_line); + size_t ret = 0; + do { + ret = stream_read(stream, buffer, FILE_BUFFER_SIZE); + for(uint16_t i = 0; i < ret; i++) { + if(buffer[i] == '\n' && encrypted_line_cursor > 0) { + // Process line + if(iv) { + // Data alignment check, 32 instead of 16 because of hex encoding + size_t len = strlen(encrypted_line); + if(len % 32 == 0) { + // Inplace hex to bin conversion + for(size_t i = 0; i < len; i += 2) { + uint8_t hi_nibble = 0; + uint8_t lo_nibble = 0; + hex_char_to_hex_nibble(encrypted_line[i], &hi_nibble); + hex_char_to_hex_nibble(encrypted_line[i + 1], &lo_nibble); + encrypted_line[i / 2] = (hi_nibble << 4) | lo_nibble; + } + len /= 2; + + if(furi_hal_crypto_decrypt( + (uint8_t*)encrypted_line, (uint8_t*)decrypted_line, len)) { + subghz_keystore_process_line(instance, decrypted_line); + } else { + FURI_LOG_E(TAG, "Decryption failed"); + result = false; + break; + } } else { - FURI_LOG_E(TAG, "Decryption failed"); - result = false; - break; + FURI_LOG_E(TAG, "Invalid encrypted data: %s", encrypted_line); } } else { - FURI_LOG_E(TAG, "Invalid encrypted data: %s", encrypted_line); + subghz_keystore_process_line(instance, encrypted_line); } + // reset line buffer + memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + encrypted_line_cursor = 0; + } else if(buffer[i] == '\r' || buffer[i] == '\n') { + // do not add line endings to the buffer } else { - subghz_keystore_process_line(instance, encrypted_line); - } - // reset line buffer - memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); - memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); - encrypted_line_cursor = 0; - } else if(buffer[i] == '\r' || buffer[i] == '\n') { - // do not add line endings to the buffer - } else { - if(encrypted_line_cursor < SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE) { - encrypted_line[encrypted_line_cursor] = buffer[i]; - encrypted_line_cursor++; - } else { - FURI_LOG_E(TAG, "Malformed file"); - result = false; - break; + if(encrypted_line_cursor < SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE) { + encrypted_line[encrypted_line_cursor] = buffer[i]; + encrypted_line_cursor++; + } else { + FURI_LOG_E(TAG, "Malformed file"); + result = false; + break; + } } } - } - } while(ret > 0 && result); + } while(ret > 0 && result); - if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + } while(false); free(encrypted_line); free(decrypted_line); diff --git a/lib/u8g2/u8g2_glue.c b/lib/u8g2/u8g2_glue.c index 77f37f14cc..17a702b50f 100644 --- a/lib/u8g2/u8g2_glue.c +++ b/lib/u8g2/u8g2_glue.c @@ -225,7 +225,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 6 (0b110) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 32, 0b110, false); + u8x8_d_st756x_init(u8x8, 31, 0b110, false); } else { /* ERC v1(ST7565) and v2(ST7567) * EV = 33 @@ -233,7 +233,7 @@ uint8_t u8x8_d_st756x_flipper(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* * RR = 9.3 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.47 is 5.5 (0b101) * Bias = 1/9 (false) */ - u8x8_d_st756x_init(u8x8, 33, 0b101, false); + u8x8_d_st756x_init(u8x8, 32, 0b101, false); } break; case U8X8_MSG_DISPLAY_SET_FLIP_MODE: diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index 138828ff0b..56f412a95f 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -12,7 +12,7 @@ #define UPDATE_ROOT_DIR EXT_PATH("update") /* Need at least 4 free LFS pages before update */ -#define UPDATE_MIN_INT_FREE_SPACE 4 * 4 * 1024 +#define UPDATE_MIN_INT_FREE_SPACE 2 * 4 * 1024 static const char* update_prepare_result_descr[] = { [UpdatePrepareResultOK] = "OK", diff --git a/scripts/get_env.py b/scripts/get_env.py new file mode 100644 index 0000000000..d052730949 --- /dev/null +++ b/scripts/get_env.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 + +import ssl +import json +import os +import shlex +import re +import string +import random +import argparse +import datetime +import urllib.request + + +def id_gen(size=5, chars=string.ascii_uppercase + string.digits): + return "".join(random.choice(chars) for _ in range(size)) + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--event_file", help="Current GitHub event file", required=True) + parser.add_argument( + "--type", + help="Event file type", + required=True, + choices=["pull", "tag", "other"], + ) + args = parser.parse_args() + return args + + +def get_commit_json(event): + context = ssl._create_unverified_context() + with urllib.request.urlopen( + event["pull_request"]["_links"]["commits"]["href"], context=context + ) as commit_file: + commit_json = json.loads(commit_file.read().decode("utf-8")) + return commit_json + + +def get_details(event, args): + data = {} + current_time = datetime.datetime.utcnow().date() + if args.type == "pull": + commit_json = get_commit_json(event) + data["commit_comment"] = shlex.quote(commit_json[-1]["commit"]["message"]) + data["commit_hash"] = commit_json[-1]["sha"] + ref = event["pull_request"]["head"]["ref"] + data["pull_id"] = event["pull_request"]["number"] + data["pull_name"] = shlex.quote(event["pull_request"]["title"]) + elif args.type == "tag": + data["commit_comment"] = shlex.quote(event["head_commit"]["message"]) + data["commit_hash"] = event["head_commit"]["id"] + ref = event["ref"] + else: + data["commit_comment"] = shlex.quote(event["commits"][-1]["message"]) + data["commit_hash"] = event["commits"][-1]["id"] + ref = event["ref"] + data["commit_sha"] = data["commit_hash"][:8] + data["branch_name"] = re.sub("refs/\w+/", "", ref) + data["suffix"] = ( + data["branch_name"].replace("/", "_") + + "-" + + current_time.strftime("%d%m%Y") + + "-" + + data["commit_sha"] + ) + if ref.startswith("refs/tags/"): + data["suffix"] = data["branch_name"].replace("/", "_") + return data + + +def add_env(name, value, file): + delimeter = id_gen() + print(f"{name}<<{delimeter}", file=file) + print(f"{value}", file=file) + print(f"{delimeter}", file=file) + + +def add_envs(data, env_file, args): + add_env("COMMIT_MSG", data["commit_comment"], env_file) + add_env("COMMIT_HASH", data["commit_hash"], env_file) + add_env("COMMIT_SHA", data["commit_sha"], env_file) + add_env("SUFFIX", data["suffix"], env_file) + add_env("BRANCH_NAME", data["branch_name"], env_file) + add_env("DIST_SUFFIX", data["suffix"], env_file) + add_env("WORKFLOW_BRANCH_OR_TAG", data["branch_name"], env_file) + if args.type == "pull": + add_env("PULL_ID", data["pull_id"], env_file) + add_env("PULL_NAME", data["pull_name"], env_file) + + +def main(): + args = parse_args() + event_file = open(args.event_file) + event = json.load(event_file) + env_file = open(os.environ["GITHUB_ENV"], "a") + data = get_details(event, args) + add_envs(data, env_file, args) + event_file.close() + env_file.close() + + +if __name__ == "__main__": + main() diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd index f955a4db3f..1403837d94 100644 --- a/scripts/toolchain/fbtenv.cmd +++ b/scripts/toolchain/fbtenv.cmd @@ -13,7 +13,7 @@ if not [%FBT_NOENV%] == [] ( exit /b 0 ) -set "FLIPPER_TOOLCHAIN_VERSION=9" +set "FLIPPER_TOOLCHAIN_VERSION=15" set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows" diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh index 32ceb5464f..cfbf25a4d2 100755 --- a/scripts/toolchain/fbtenv.sh +++ b/scripts/toolchain/fbtenv.sh @@ -5,7 +5,7 @@ # public variables DEFAULT_SCRIPT_PATH="$(pwd -P)"; SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}"; -FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"8"}"; +FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"15"}"; FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}"; fbtenv_show_usage() @@ -64,13 +64,13 @@ fbtenv_check_sourced() fbtenv_chck_many_source() { - if ! echo "${PS1:-""}" | grep -q "[fbt]"; then - if ! echo "${PROMPT:-""}" | grep -q "[fbt]"; then + if ! echo "${PS1:-""}" | grep -qF "[fbt]"; then + if ! echo "${PROMPT:-""}" | grep -qF "[fbt]"; then return 0; fi fi - echo "Warning! It script seen to be sourced more then once!"; - echo "It may signalise what you are making some mistakes, please open a new shell!"; + echo "Warning! FBT environment script sourced more than once!"; + echo "This may signal that you are making mistakes, please open a new shell!"; return 1; }